Golang: 简单smtp邮件发送样例

作者:Ajian 发布时间:April 25, 2012 分类:Golang

用GO语言写邮件发送发现也是件很简单和方便的事,可能最关键还是对字符处理和业务逻辑上的很麻烦,不过主体的smtp邮件发送功能是很简单的。最近在思考一个邮件发送路由,主要为的是解决分布式邮件报警和保证邮件送达率为100%.

样例参考:http://code.google.com/p/go-wiki/wiki/SendingMail

具体其它用法可以参考pkg/net/smtp

sendMail.go 基于Go Version 1

package main

import (
        "log"
        "net/smtp"
        "flag"
        "fmt"
        "strings"
)

var (
    subject = flag.String( "s","","subject of the mail" )
    body= flag.String( "b","","body of themail" )
    reciMail = flag.String( "m","","recipient mail address" )
)

func main() {
        // Set up authentication information.
        flag.Parse()
        sub := fmt.Sprintf("subject: %s\r\n\r\n",*subject)
        content :=  *body
        mailList := strings.Split( *reciMail,",")

        auth := smtp.PlainAuth(
                "",
                "smtpuser@example.com",
                "password",
                "smtp.example.com",
                //"smtp.gmail.com",
        )
        // Connect to the server, authenticate, set the sender and recipient,
        // and send the email all in one step.
        err := smtp.SendMail(
                "smtp.example.com:25",
                auth,
                "senduser@example.com",
                mailList,
                []byte(sub+content),
        )
        if err != nil {
                log.Fatal(err)
        }
}

使用

./sendMail -s "发送测试邮件主题" -b "我的邮件内容  yyyyyyyy" -m user1@gmail.com,user2@qq.com

看看是不是收到邮件了。发送到多个人就是这样方便。

Golang:gosync 简单文件同步 Simple File Sync

作者:Ajian 发布时间:April 23, 2012 分类:Golang

前言:最近学习Go语言 尝试写了一个简单的文件同步,基于tcp的,功能比较简单。基于Go version 1 编译。

该程序的功能:

  1. 只能传输单一文件,文件名不能有特殊符号和空格
  2. 有serve端和client命令 使用同一个程序
  3. 不能同步目录结构
  4. 不能改名字
  5. 无法查看进度
该程序主要就是将指定的文件同步到新的服务器和指定的目录下面,并且该文件的时间和属性跟原来一样,可以使用rsync进行检测。
该程序名字 gosync
gosync.go
package main

import (
    "os"
    "fmt"
    "net"
    "flag"
    "time"
    "crypto/md5"
    "io"
    "strings"
    "strconv"
)

type sysFileInfo struct{
    fName       string
    fSize       int64
    fMtime      time.Time
    fPerm       os.FileMode
    fMd5        string
    fType       bool
}

var (
    listenPort = flag.String( "port","7722","server listen port" )
    syncFile = flag.String( "file","","transfer file" )
    syncHost = flag.String( "host","","server host" )
    syncSer = flag.Bool( "d",false,"server mode")
    syncFold = flag.String( "dir","/tmp/gosync/","recive sync fold ")
)

func main(){
    flag.Parse()
    if *syncSer {  
        servPort:=fmt.Sprintf( ":%s",*listenPort )
        l,err := net.Listen( "tcp",servPort )
        if err != nil{
           fmt.Println( "net failed",err ) 
        }
        err = os.MkdirAll( *syncFold , 0755)
        if err != nil{
           fmt.Println( err ) 
        }
        fmt.Println( "Start Service" )
        Serve( l )
     }else{  
        destination:=fmt.Sprintf( "%s:%s",*syncHost,*listenPort )
        clientSend( *syncFile,destination)
     }
}

func clientSend(files string,destination string){
    fInfo:=getFileInfo( files)
    newName :=fmt.Sprintf( "%s",fInfo.fName)
    cmdLine:=  fmt.Sprintf( "upload %s %d %d %d %s " ,newName,fInfo.fMtime.Unix(),fInfo.fPerm,fInfo.fSize,fInfo.fMd5) 
    cn,err:=net.Dial( "tcp", destination)
    if err !=nil {
        fmt.Println( "connect error",err )
        return
    }
    defer cn.Close()
    cn.Write( []byte( cmdLine ) )
    cn.Write( []byte( "\r\n" ) )
    fileHandle,err := os.Open( files )
    if err != nil {
        fmt.Println("open ERROR",err) 
        return
    }
    io.Copy( cn,fileHandle)
    for{
        buffer :=make( []byte,1024)
        num,err := cn.Read(buffer)
        if err == nil && num > 0{
            fmt.Println(  string(buffer[ :num ]) )
            break
        }
    }
}

func getFileInfo( filename string) *sysFileInfo{
    fi,err:= os.Lstat( filename ) 
    if err != nil {
        fmt.Println("info ERROR",err) 
        return nil
    }
    fileHandle,err := os.Open( filename )
    if err != nil {
        fmt.Println("open ERROR",err) 
        return nil
    }

    h := md5.New()
    _,err = io.Copy( h,fileHandle )
    fileInfo := & sysFileInfo {
        fName : fi.Name(),
        fSize : fi.Size(),
        fPerm : fi.Mode().Perm(),
        fMtime: fi.ModTime(),
        fType : fi.IsDir(),
        fMd5  : fmt.Sprintf( "%x", h.Sum( nil )),
    }
        return fileInfo
}

func Serve( l net.Listener) {
    for{
        conn,err := l.Accept()
        if err != nil{
            if ne,ok := err.( net.Error );ok && ne.Temporary(){
                continue
            }
            fmt.Println( "network error",err )
        }
        go Handler(conn)
    }
}

func Handler( conn net.Conn) {
    defer conn.Close()
    state := 0
    var cmd *sysFileInfo
    var fSize int64
    var tempFileName string
    var n int64
    for {
        buffer :=make( []byte,2048)
        num,err := conn.Read(buffer)
        numLen:=int64( num )
        if err != nil && err != io.EOF {
            fmt.Println( "cannot read",err )
        }
        n=0
        if state  == 0 {
            n,cmd = cmdParse( buffer[:num] )
            tempFileName = fmt.Sprintf( "%s.newsync",cmd.fName)
            fSize = cmd.fSize
            state = 1
        } 
        if state == 1 {
            last := numLen
            if fSize <= numLen-n {
                last = fSize + n
                state = 2
            }
            err = writeToFile( buffer[int( n ):int( last )],tempFileName,cmd.fPerm )
            if err != nil{
                fmt.Println( "read num error : ",err )
            }
            fSize -=last-n 
            if state == 2{
                os.Remove( cmd.fName)
                err = os.Rename( tempFileName,cmd.fName)
                if err != nil{
                    fmt.Println( "rename ",tempFileName," to ",cmd.fName," failed" ) 
                }
                err = os.Chtimes( cmd.fName,time.Now(),cmd.fMtime )
                if err != nil{
                    fmt.Println( "change the mtime error ",err ) 
                }
                fileHandle,err := os.Open( cmd.fName)
                if err != nil {
                    fmt.Println("open ERROR",err) 
                }
                h := md5.New()
                io.Copy( h,fileHandle )
                newfMd5 := fmt.Sprintf( "%x", h.Sum( nil ))
                if newfMd5 == cmd.fMd5{
                    sendInfo:=fmt.Sprintf("%s sync success",cmd.fName)
                    conn.Write([]byte(sendInfo))
                }else{
                    sendInfo:=fmt.Sprintf("%s sync failed",cmd.fName)
                    conn.Write([]byte(sendInfo))
                }
            }
        }
    }
}

func cmdParse( infor []byte) ( int64 , *sysFileInfo) {
    var i int64
    for i=0;i<int64(len(infor));i++ {
       if infor[i] == '\n' && infor[i-1]  == '\r' {
           cmdLine:=strings.Split( string( infor[:i-1] ) ," ") 
           fileName := fmt.Sprintf( "%s/%s",*syncFold,cmdLine[ 1 ] )
           filePerm, _ := strconv.Atoi( cmdLine[ 3 ])
           fileMtime,_:= strconv.ParseInt( cmdLine[ 2 ],10,64 )
           fileSize,_:= strconv.ParseInt( cmdLine[ 4 ],10,64)
           fileInfo := & sysFileInfo {
                fName : fileName, 
                fMtime: time.Unix( fileMtime,0 ),
                fPerm : os.FileMode(filePerm),
                fSize : fileSize,
                fMd5  : string(cmdLine[ 5 ]),
           }
           return i+1,fileInfo
       }
    }
       return 0,nil
}

func writeToFile( data []byte ,fileName string,perm os.FileMode)  error{
    writeFile,err := os.OpenFile( fileName,os.O_RDWR | os.O_APPEND | os.O_CREATE ,perm)
    if err != nil{
        fmt.Println( "write file error:",err )
        return err
    }
    defer writeFile.Close()
    _,err = writeFile.Write( data )
    if err != nil{
       fmt.Println( "write file error",err ) 
       return err 
    }
    return nil
}
使用:
usage:
Usage of ./gosync:
  -d=false: server mode
  -dir="/tmp/gosync/": recive sync fold 
  -file="": transfer file
  -host="": server host
  -port="7722": server listen port
服务端开启
#./gosync -d -dir /tmp/xxxx
client传输文件:
# ./gosync --file=/root/Videos/DSCF2394.avi --host=127.0.0.1 --port=7722
/tmp/xxxx/DSCF2394.avi sync success
手动检查文件正确性:
# md5sum /tmp/xxxx/DSCF2394.avi 
eb50332d3b3b6f36b773046aca16e908  /tmp/xxxx/DSCF2394.avi
# md5sum /root/Videos/DSCF2394.avi
eb50332d3b3b6f36b773046aca16e908  /root/Videos/DSCF2394.avi

Golang:xml解析和类型重组

作者:Ajian 发布时间:April 18, 2012 分类:Golang

Go对xml的支持很好,好像其它的配置文件格式还没有太多支持的,像ini yaml 都还没有看到。我之前很少解析xml配置文件,对于很少使用这种强类型语言的自己(之前接触的语言都是弱类型语言像php python shell),还是遇到不少问题。


xml配置文件 sample.xml

<?xml version="1.0" encoding="utf-8"?>
<Autosync Version="1.01">
    <Filter>
        <Exclude>(.*)\.svn$</Exclude>
        <Exclude>(.*)\.gz</Exclude>
        <Exclude>^info/.*</Exclude>
    </Filter>
    <ListenPort>8786</ListenPort>
    <Timeout>60</Timeout>
    <Servthread>20</Servthread>

    <Debug>/tmp/sync_debug.log</Debug>
    <Warning>/tmp/sync_warning.log</Warning>
    <Fatal>/tmp/sync_fatal.log</Fatal>

    <Service Name="local">
        <Ip>192.168.1.20</Ip>
        <Project Module="TEST" User="www" Group="www" Path="/tmp/test">
            <Remote Ip="192.168.1.22" Port="8080" Module="TEST"/>
            <Remote Ip="192.168.1.21" Port="2277" Module="TEMP"/>
        </Project>
        <Project Module="kkkk" User="nobody" Group="nobody" Path="/tmp/kkkk">
            <Remote Ip="192.168.1.23" Module="TEST"/>
        </Project>
    </Service>

    <Service Name="21serv">
        <Filter>
            <Exclude>(.*)\.svn$</Exclude>
            <Exclude>(.*)\.gz</Exclude>
            <Exclude>^info/.*</Exclude>
        </Filter>
        <ListenPort>8788</ListenPort>
        <Timeout>30</Timeout>
        <Servthread>10</Servthread>
        <Ip>192.168.1.21</Ip>
        <Project Module="TEMP" User="nobody" Group="nobody" Path="/tmp/temp">
        </Project>
    </Service>
</Autosync>

该配置的作用就不具体说 但是主要实现的是 Service 中的Filter ListenPort Timeout Servthread 优先级比Global的变量高,解析例子里面将这个xml配置文件进行了结构重组。

service[name]{Filter ,ListenPort,Timeout,Servthread,Project[module]}

Project[module]{user,grop,path,remote[module]}

remote[module]{ip,port}

PS:go解析的xml文件都要首字母大写 不然在解析的时候会比较的麻烦。那就规范全部用大写开头吧。

xml.go

package main
import (
    "encoding/xml"
    "fmt"
    "flag"
    "os"
)
var (
    gServ= flag.String("service","local","service name")
    gFile= flag.String("config","sample.xml","config name")
)

type XmlConfig struct {
    Version     string      `xml:"Version,attr"`
    Filter      []string    `xml:"Filter>Exclude"`
    ListenPort  int         
    Timeout     int
    Servthread  int
    Debug       string
    Warning     string
    Fatal       string
    Security    XmlSecurity
    Service[]   XmlService
}

type XmlService struct {
    Name        string  `xml:"Name,attr"`
    Filter      []string    `xml:"Filter>Exclude"`
    Servthread  int
    ListenPort  int
    Timeout     int
    Ip          string
    Security    XmlSecurity
    Project[]   XmlProject
}

type XmlProject struct {
    Module      string  `xml:"Module,attr"`
    User        string  `xml:"User,attr"`
    Group       string  `xml:"Group,attr"`
    Path        string  `xml:"Path,attr"`
    Security    XmlSecurity
    Remote[]     XmlRemote
}

type XmlRemote struct {
    Module      string  `xml:"Module,attr"`
    Port        int     `xml:"Port,attr"`
    Ip          string  `xml:"Ip,attr"`
}

type XmlSecurity struct {
    Ip[]    string
}

//////////////////////////
/////////////////////////
type sysInoInfo struct{
    Name        string     
    Filter      []string    
    ListenPort  int
    Timeout     int
    Servthread  int
    Project     map[string]*InoProject
}

type InoProject struct{
    Module      string 
    User        string  
    Group       string 
    Path        string
    Remote      map[string]*InoRemote
}

type InoRemote struct{
    Ip          string
    Port        int
    Module      string
}

var gConfig XmlConfig

func XmlPrase(fileName string) bool{
    file,err := os.Open( fileName )
    if err != nil {
        fmt.Println( "open xml file Fail",err )
    }
    defer file.Close()
    fi,_ := file.Stat()
    buffer := make( []byte,fi.Size() )
    _,err = file.Read( buffer )
    if err != nil{
        fmt.Println( "read xml file fail",err )
        return false
    }
    err = xml.Unmarshal( buffer,&gConfig )
    if err != nil{
        fmt.Println( "get config file fail",err )
        return false
    }
    return true
}

func main(){
    flag.Parse()
    XmlPrase( *gFile)
    allService := make( map[string]*sysInoInfo)
    for _, xServer := range gConfig.Service{
        serv, ok := allService[xServer.Name]
        if !ok { serv = new( sysInoInfo  )  }
        allService[xServer.Name] = serv
        serv.Filter = xServer.Filter
        serv.ListenPort = xServer.ListenPort
        serv.Timeout= xServer.Timeout
        serv.Servthread= xServer.Servthread
        if  len(xServer.Filter) == 0 {
            serv.Filter = gConfig.Filter
        }
        if   xServer.ListenPort == 0{
            serv.ListenPort = gConfig.ListenPort
        }
        if   xServer.Timeout== 0{
            serv.Timeout= gConfig.Timeout
        }
        if   xServer.Servthread == 0{
            serv.Servthread= gConfig.Servthread
        }

        serv.Project = make( map[string]*InoProject)
        for _,xProject := range xServer.Project{
            pro,ok := serv.Project[xProject.Module]
            if !ok { pro = new( InoProject)  }
            serv.Project[xProject.Module] = pro
            pro.User=xProject.User
            pro.Group=xProject.Group
            pro.Path=xProject.Path

            pro.Remote = make( map[ string ]*InoRemote )
            for _,xRemote := range xProject.Remote{
                remo,ok := pro.Remote[xRemote.Module]
                if !ok { remo = new( InoRemote)  }
                pro.Remote[ xRemote.Module ] = remo
                remo.Ip = xRemote.Ip
                remo.Port = 7788
                if xRemote.Port == 0{
                    remo.Port = xRemote.Port
                }
            }
        }
    }
    fmt.Println( allService[ *gServ])
//    allService[ *gServ ]
    for key , value:= range allService[ *gServ ].Project {
        fmt.Println( "Project:",key)
        fmt.Println( value)
        for key,v := range value.Remote{
            fmt.Println( "Remote:",key)
            fmt.Println( v)
        }
    }
}

直接运行 看下结果:

# ./run xml.go
# ./xml
&{ [(.*)\.svn$ (.*)\.gz ^info/.*] 8786 60 20 map[kkkk:0xf8400a6320 TEST:0xf8400a62d0]}
Project: kkkk
&{ nobody nobody /tmp/kkkk map[TEST:0xf84009fab0]}
Remote: TEST
&{192.168.1.23 0 }
Project: TEST
&{ www www /tmp/test map[TEMP:0xf84009fa80 TEST:0xf84009fa50]}
Remote: TEST
&{192.168.1.22 7788 }
Remote: TEMP
&{192.168.1.21 7788 }
# ./xml --service=21serv --config=sample.xml
&{ [(.*)\.svn$ (.*)\.gz ^info/.*] 8788 30 10 map[TEMP:0xf8400b8550]}
Project: TEMP
&{ nobody nobody /tmp/temp map[]}

Golang:So easy 命令行参数传递Flag 对比Shell python

作者:Ajian 发布时间:April 13, 2012 分类:Shell,Golang

    感叹Go language写参数传递太简单了,so easy .如果要写脚本或者写服务肯定很多时候都要进行命令行参数的传递。

如:

./flag  --config=main.xml --port=7788

对比shell和python就感觉go真是很简单方便。下面对比下几个语言吧:

Shell

Shell使用了两个getopts和getopt getopts可以接受短命令 getopt就可以复杂一些 包括长命令 而且两个命令是分开的 感觉很原始

getopts 短命令

#!/bin/bash
if [ $# -eq 0 ];then
    echo "erminating..." >&2 ; exit 1 ;
else
    while getopts ":c:p:" opt; do
        case $opt in
            c)
                config=$OPTARG 
                echo "Option c, argument $config" 
                ;;
            p)
                port=$OPTARG 
                echo "Option p, argument $port"
                ;;
            *)
                echo "nternal error!" ; exit 1 ;;
        esac
    done
fi

Output:

# bash shortflag.sh -c config.xml -p 8080
Option c, argument config.xml
Option p, argument 8080

getopt长命令

#!/bin/bash
TEMP=`getopt -o p:c: --long port:,config: \
      -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

eval set -- "$TEMP"

while true ; do
        case "$1" in
                -p|--port) echo "Option p, argument \`$2'" ; shift 2 ;;
                -c|--config)echo "Option c, argument \`$2'" ; shift 2 ;;
                --) shift ; break ;;
                *) echo "Internal error!" ; exit 1 ;;
        esac
done
echo "Remaining arguments:"
for arg do
   echo '--> '"\`$arg'" ;
done

Output:

# bash longflag.sh -c config.xml -p 8080
Option c, argument `config.xml'
Option p, argument `8080'
Remaining arguments:
# bash longflag.sh --config=config.xml --port=8080
Option c, argument `config.xml'
Option p, argument `8080'
Remaining arguments:

Python

同样有一个getopt函数不过 该函数已经包括了长命令格式 做为可选的一个参数 进化了。

#!/usr/bin/python
import getopt, sys
def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "p:c:", ["port=", "config="])
    except getopt.GetoptError:
        print "Terminating..." 
        sys.exit(2)
    config = None
    port = 8001
    for o, a in opts:
        if o in ("-p", "--port"):
            port= a
            print "Option p, argument %s"%port
        if o in ("-c", "--config"):
            config = a
            print "Option c, argument %s"%config

if __name__ == "__main__":
    main()

Output:

# python flag.py  --port=9000 --config=file.xml
Option p, argument 9000
Option c, argument file.xml
# python flag.py  -p 9000 -c file.xml
Option p, argument 9000
Option c, argument file.xml

看到上面这些如果不认真看 还真容易晕吧。到Go了 想像不到的简单。

Go

使用flag库,一切都简化了

package main

import (
    "flag"
    "fmt"
)

var (
     openPort= flag.String("port",":8080","http listen port")
     configFile= flag.String( "config","","config file name" )
)

func main(){
    flag.Parse()
    fmt.Println("Configure Filename: ",*configFile)
    fmt.Println("Open Port: ",*openPort)
}

Output:

#./run flag.go
# ./flag -port 8001 -config mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001
# ./flag --port 8001 --config mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001
# ./flag --port=8001 --config=mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001
# ./flag -port=8001 -config=mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001

看到结果了吧 只要一定义 引用可以任意的 不用纠结他是怎么工作的 要的就是快捷。

里面有个run是我的小脚本,可以方便的帮助我编译做测试的脚本

#!/bin/bash
filename=$1
outfile=`echo ${filename%.*}`
go build -o $outfile $filename

更多flag函数的使用去官网查看吧: http://golang.org/pkg/flag