go smtp示例

Posted dz45693

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go smtp示例相关的知识,希望对你有一定的参考价值。

书接上文邮件实现详解,这里我们及我们简单复习一下smtp的指令如下:

好,那我们下现在用go实现代码让如下: 这里只是一个demo,主要熟悉smtp命令

package main

import (
    "bufio"
    "encoding/base64"
    "fmt"
    "net"
    "strconv"
    "strings"
)


func main() 
    testSmtp()


var gConn net.Conn
var gRead *bufio.Reader
var gWrite *bufio.Writer

//可以放到这样的类里
type TcpClient struct 
    Conn  net.Conn
    Read  *bufio.Reader
    Write *bufio.Writer
 //

func Connect(host string, port int) (net.Conn, *bufio.Reader, *bufio.Writer) 
    addr := host + ":" + strconv.Itoa(port)
    conn, err := net.Dial("tcp", addr)
    if err != nil 
        return nil, nil, nil
    

    reader := bufio.NewReader(conn)
    writer := bufio.NewWriter(conn)

    return conn, reader, writer
 //

//收取一行,可再优化
func RecvLine() string 
    line, err := gRead.ReadString(\'\\n\') //如何设定超时?
    if err != nil 
        fmt.Print(err)
        return ""
    

    line = strings.Split(line, "\\r")[0] //还要再去掉 "\\r",其实不去掉也可以
    return line


func SendLine(line string) 
    gWrite.WriteString(line + "\\r\\n")
    gWrite.Flush()


//解码一行命令,这里比较简单就是按空格进行分隔就行了
func DecodeCmd(line string, sp string) []string 
    tmp := strings.Split(line, sp)
    var cmds = []string"", "", "", "", "" //先定义多几个,以面后面使用时产生异常
    for i := 0; i < len(tmp); i++ 
        if i >= len(cmds) 
            break
        
        cmds[i] = tmp[i]
    
    return cmds


//读取多行结果
func RecvMCmd() string 
    i := 0
    rs := ""
    mLine := ""
    for i = 0; i < 50; i++ 
        rs = RecvLine() //只收取一行
        mLine = mLine + rs + "\\r\\n"
        if len(rs) < 4 
            break
         //长度要足够
        c4 := rs[4-1] //第4个字符
        if \' \' == c4 
            break
         //第4个字符是空格就表示读取完了//也可以判断 "250[空格]"
    
    return mLine


//简单的测试一下 smtp
func testSmtp() 
    //连接
    gConn, gRead, gWrite = Connect("smtp.163.com", 25)
    defer gConn.Close()
    //收取一行
    line := RecvLine()
    fmt.Println("recv:" + line)

    //解码一下,这样后面的 EHLO 才能有正确的第二个参数
    cmds := DecodeCmd(line, " ")
    domain := cmds[1] //要从对方的应答中取出域名//空格分开的各个命令参数中的第二个

    //发送一个命令
    SendLine("EHLO" + " " + domain) //domain 要求其实来自 HELO 命令//HELO <SP> <domain> <CRLF>

    //收取多行
    line = RecvMCmd()
    fmt.Println("recv:" + line)

    //--------------------------------------------------
    //用 base64 登录
    SendLine("AUTH LOGIN")

    //收取一行
    line = RecvLine()
    fmt.Println("recv:" + line)

    s := "dz45693" //要换成你的用户名,注意 163 邮箱的话不要带后面的 @域名 部分
    s = base64.StdEncoding.EncodeToString([]byte(s))
    SendLine(s)

    //收取一行
    line = RecvLine()
    fmt.Println("recv:" + line)

    s = "xxxxx" //要换成您的密码
    s = base64.StdEncoding.EncodeToString([]byte(s))
    SendLine(s)

    //收取一行
    line = RecvLine()
    fmt.Println("recv:" + line)

    //--------------------------------------------------
    //邮件内容
    from := "dz45693@163.com"
    to := "dz45693@sina.com"

    SendLine("MAIL FROM: <" + from + ">") //注意"<" 符号和前面的空格。空格在协议中有和没有都可能,最好还是有
    //收取一行
    line = RecvLine()
    fmt.Println("recv:" + line)

    SendLine("RCPT TO: <" + to + ">")
    //收取一行
    line = RecvLine()
    fmt.Println("recv:" + line)

    SendLine("DATA")
    //收取一行
    line = RecvLine()
    fmt.Println("recv:" + line)

//发送邮件头
    SendLine("from:<dz45693@163.com>")
    SendLine("to:<dz45693@sina.com>")
    SendLine("subject:hello world")
    SendLine("") //发送空行 后面就是邮件体
    SendLine("This is the first email sent by hand using the SMTP protocol")

    SendLine(".") //邮件结束符

    //收取一行
    line = RecvLine()
    fmt.Println("recv:" + line)

    SendLine("quit") //链接推出
    line = RecvLine()
    fmt.Println("recv:" + line)
 //

运行结果图下:

 在go的sdk中提供了SendMail方法【发送邮件后这个方法会关闭链接】,实现如下:

实现如下:

func SendMailBySmtp()
    auth := smtp.PlainAuth("", "dz45693@163.com", "xxx", "smtp.163.com")
    to := []string"dz45693@sina.com"
    image,_:=ioutil.ReadFile("d:\\\\Downloads\\\\1.png")
    imageBase64:=base64.StdEncoding.EncodeToString(image)
    msg := []byte("from:dz45693@163.com\\r\\n"+
        "to: dz45693@sina.com\\r\\n" +
        "Subject: hello,subject!\\r\\n"+
        "Content-Type:multipart/mixed;boundary=a\\r\\n"+
        "Mime-Version:1.0\\r\\n"+
        "\\r\\n" +
        "--a\\r\\n"+
        "Content-type:text/plain;charset=utf-8\\r\\n"+
        "Content-Transfer-Encoding:quoted-printable\\r\\n"+
        "\\r\\n"+
        "此处为正文内容!\\r\\n"+
        "--a\\r\\n"+
        "Content-type:image/jpg;name=1.jpg\\r\\n"+
        "Content-Transfer-Encoding:base64\\r\\n"+
        "\\r\\n"+
        imageBase64+"\\r\\n"+
        "--a--\\r\\n")
    err := smtp.SendMail("smtp.163.com:25", auth, "dz45693@163.com", to, msg)
    if err != nil 
        fmt.Println(err)
    

运行效果

 

 示例如下:

func SendMailByGomailOne()
    m := gomail.NewMessage()
    m.SetAddressHeader("From", "dz45693@163.com", "dz45693")
    m.SetHeader("To", "dz45693@sina.com")
    m.SetHeader("Subject", "hello SendMailByGomailOne!")
    m.Embed("d:\\\\Downloads\\\\1.png")
    m.SetBody("text/html", "此处为正文121333!")

    d := gomail.NewDialer("smtp.163.com", 25, "dz45693@163.com", "ncs123456#")

    if err := d.DialAndSend(m); err != nil 
        panic(err)
    

运行结果:

来我们看看DialAndSend的实现如下: 

package gomail

import (
    "crypto/tls"
    "fmt"
    "io"
    "net"
    "net/smtp"
    "strings"
    "time"
)

// A Dialer is a dialer to an SMTP server.
type Dialer struct 
    // Host represents the host of the SMTP server.
    Host string
    // Port represents the port of the SMTP server.
    Port int
    // Username is the username to use to authenticate to the SMTP server.
    Username string
    // Password is the password to use to authenticate to the SMTP server.
    Password string
    // Auth represents the authentication mechanism used to authenticate to the
    // SMTP server.
    Auth smtp.Auth
    // SSL defines whether an SSL connection is used. It should be false in
    // most cases since the authentication mechanism should use the STARTTLS
    // extension instead.
    SSL bool
    // TSLConfig represents the TLS configuration used for the TLS (when the
    // STARTTLS extension is used) or SSL connection.
    TLSConfig *tls.Config
    // LocalName is the hostname sent to the SMTP server with the HELO command.
    // By default, "localhost" is sent.
    LocalName string


// NewDialer returns a new SMTP Dialer. The given parameters are used to connect
// to the SMTP server.
func NewDialer(host string, port int, username, password string) *Dialer 
    return &Dialer
        Host:     host,
        Port:     port,
        Username: username,
        Password: password,
        SSL:      port == 465,
    


// NewPlainDialer returns a new SMTP Dialer. The given parameters are used to
// connect to the SMTP server.
//
// Deprecated: Use NewDialer instead.
func NewPlainDialer(host string, port int, username, password string) *Dialer 
    return NewDialer(host, port, username, password)


// Dial dials and authenticates to an SMTP server. The returned SendCloser
// should be closed when done using it.
func (d *Dialer) Dial() (SendCloser, error) 
    conn, err := netDialTimeout("tcp", addr(d.Host, d.Port), 10*time.Second)
    if err != nil 
        return nil, err
    

    if d.SSL 
        conn = tlsClient(conn, d.tlsConfig())
    

    c, err := smtpNewClient(conn, d.Host)
    if err != nil 
        return nil, err
    

    if d.LocalName != "" 
        if err := c.Hello(d.LocalName); err != nil 
            return nil, err
        
    

    if !d.SSL 
        if ok, _ := c.Extension("STARTTLS"); ok 
            if err := c.StartTLS(d.tlsConfig()); err != nil 
                c.Close()
                return nil, err
            
        
    

    if d.Auth == nil && d.Username != "" 
        if ok, auths := c.Extension("AUTH"); ok 
            if strings.Contains(auths, "CRAM-MD5") 
                d.Auth = smtp.CRAMMD5Auth(d.Username, d.Password)
             else if strings.Contains(auths, "LOGIN") &&
                !strings.Contains(auths, "PLAIN") 
                d.Auth = &loginAuth
                    username: d.Username,
                    password: d.Password,
                    host:     d.Host,
                
             else 
                d.Auth = smtp.PlainAuth("", d.Username, d.Password, d.Host)
            
        
    

    if d.Auth != nil 
        if err = c.Auth(d.Auth); err != nil 
            c.Close()
            return nil, err
        
    

    return &smtpSenderc, d, nil


func (d *Dialer) tlsConfig() *tls.Config 
    if d.TLSConfig == nil 
        return &tls.ConfigServerName: d.Host
    
    return d.TLSConfig


func addr(host string, port int) string 
    return fmt.Sprintf("%s:%d", host, port)


// DialAndSend opens a connection to the SMTP server, sends the given emails and
// closes the connection.
func (d *Dialer) DialAndSend(m ...*Message) error 
    s, err := d.Dial()
    if err != nil 
        return err
    
    defer s.Close()

    return Send(s, m...)


type smtpSender struct 
    smtpClient
    d *Dialer


func (c *smtpSender) Send(from string, to []string, msg io.WriterTo) error 
    if err := c.Mail(from); err != nil 
        if err == io.EOF 
            // This is probably due to a timeout, so reconnect and try again.
            sc, derr := c.d.Dial()
            if derr == nil 
                if s, ok := sc.(*smtpSender); ok 
                    *c = *s
                    return c.Send(from, to, msg)
                
            
        
        return err
    

    for _, addr := range to 
        if err := c.Rcpt(addr); err != nil 
            return err
        
    

    w, err := c.Data()
    if err != nil 
        return err
    

    if _, err = msg.WriteTo(w); err != nil 
        w.Close()
        return err
    

    return w.Close()


func (c *smtpSender) Close() error 
    return c.Quit()


// Stubbed out for tests.
var (
    netDialTimeout = net.DialTimeout
    tlsClient      = tls.Client
    smtpNewClient  = func(conn net.Conn, host string) (smtpClient, error) 
        return smtp.NewClient(conn, host)
    
)

type smtpClient interface 
    Hello(string) error
    Extension(string) (bool, string)
    StartTLS(*tls.Config) error
    Auth(smtp.Auth) error
    Mail(string) error
    Rcpt(string) error
    Data() (io.WriteCloser, error)
    Quit() error
    Close() error

 DialAndSend ,首先调用Dial方法创建连接,然后发送邮件,最后关闭链接,如果要频繁发邮件,那么是否保持长连接更好了?这里的Dial 调用了smtp.NewClient 创建smtp.Client对象c,然后调用c.Hello ,c.Auth,send 实际是调用c.Mail,c.Rcpt,c.Data,那么我们可以自己调用Dial方法 然后循环调用send方法,最后在close。

代码如下:

func SendMailByGomailTwo() 
    d := gomail.NewDialer("smtp.163.com", 25, "dz45693@163.com", "xxx")
    m := gomail.NewMessage()
    m.SetAddressHeader("From", "dz45693@163.com", "dz45693")
    m.SetHeader("To", "dz45693@sina.com")
    m.SetHeader("Subject", "hello SendMailByGomailtwo!")
    m.Embed("d:\\\\Downloads\\\\1.png")
    m.SetBody("text/html", "此处为正文121333!SendMailByGomailtwo")

    s, err := d.Dial()
    if err != nil 
        panic(err)
    
    defer s.Close()

    err = gomail.Send(s, m)
    if err != nil 
        panic(err)
    

    m.Reset()
    m.SetAddressHeader("From", "dz45693@163.com", "dz45693")
    m.SetHeader("To", "dz45693@sina.com")
    m.SetHeader("Subject", "hello SendMailByGomailthree!")
    m.Embed("d:\\\\Downloads\\\\2.png")
    m.SetBody("text/html", "此处为正文1SendMailByGomailthreeSendMailByGomailthree!")
    err = gomail.Send(s, m)
    if err != nil 
        panic(err)
    

运行结果:

 

以上是关于go smtp示例的主要内容,如果未能解决你的问题,请参考以下文章

GO语言net实现SMTP

Go:连接到 SMTP 服务器并在一个连接中发送多封电子邮件?

Go发送Email

golang 使用Go(Golang)通过GMail发送电子邮件与net / smtp

golang Golang SSL SMTP示例

golang Golang SSL SMTP示例