简单邮件传输协议(SMTP)是一种基于文本的电子邮件传输协议,是在因特网中用于在邮件服务器之间交换邮件的协议。SMTP是应用层的服务,可以适应于各种网络系统。SMTP的命令和响应都是基于文本,以命令行为单位,换行符为CR/LF(\r\n)。响应信息一般只有一行,由一个3位数的代码开始,后面可附上很简短的文字说明。
SMTP要经过建立连接、传送邮件和释放连接3个阶段。具体为:
(1)建立TCP连接。
(2)客户端向服务器发送HELLO命令以标识发件人自己的身份,然后客户端发送MAIL命令。(EHLO 是扩展指令,不同服务器有不同指令)
(3)服务器端以OK作为响应,表示准备接收。
(4)客户端发送RCPT命令。
(5)服务器端表示是否愿意为收件人接收邮件。
(6)协商结束,发送邮件,用命令DATA发送输入内容。
下面是以smtp.163.com 服务器为例:
package mail import ( "encoding/base64" "fmt" "net" "time" ) const ( tb string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ) type Sender struct { UserName string Password string From string To string Subject string Text string Host string } func NewSender(host, userName, password string, to string, subject, text string) (r_sender Sender) { r_sender.UserName = userName r_sender.Password = password r_sender.From = userName r_sender.To = to r_sender.Subject = subject r_sender.Text = text r_sender.Host = host return } func (this *Sender) SendMail() (e error) { var ( deadline time.Duration = 5 * time.Second encoding *base64.Encoding buf []byte = make([]byte, 512) r int ) encoding = base64.NewEncoding(tb) conn, e := net.Dial("tcp", this.Host) if e != nil { return } defer conn.Close() conn.SetDeadline(time.Now().Add(deadline)) r, e = conn.Read(buf) if e != nil { return } fmt.Println(string(buf[:r-1])) conn.Write([]byte("EHLO Juxuny\r\n")) conn.SetDeadline(time.Now().Add(deadline)) r, e = conn.Read(buf) if e != nil { return } fmt.Println(string(buf[:r-1])) conn.Write([]byte("AUTH LOGIN\r\n")) conn.SetDeadline(time.Now().Add(deadline)) r, e = conn.Read(buf) if e != nil { return } fmt.Println(string(buf[:r-1])) conn.Write([]byte(encoding.EncodeToString([]byte(this.UserName)) + "\r\n")) conn.SetDeadline(time.Now().Add(deadline)) r, e = conn.Read(buf) if e != nil { return } fmt.Println(string(buf[:r-1])) conn.Write([]byte(encoding.EncodeToString([]byte(this.Password)) + "\r\n")) conn.SetDeadline(time.Now().Add(deadline)) r, e = conn.Read(buf) if e != nil { return } fmt.Println(string(buf[:r-1])) conn.Write([]byte("MAIL FROM: <" + this.From + ">" + "\r\n")) conn.SetDeadline(time.Now().Add(deadline)) r, e = conn.Read(buf) if e != nil { return } fmt.Println(string(buf[:r-1])) conn.Write([]byte("RCPT TO <" + this.To + ">\r\n")) conn.SetDeadline(time.Now().Add(deadline)) r, e = conn.Read(buf) if e != nil { return } fmt.Println(string(buf[:r-1])) conn.Write([]byte("DATA\r\n")) conn.Write([]byte("\r\n")) conn.Write([]byte("Message-ID: <" + this.From + ">\r\n")) conn.Write([]byte("X-Mailer: <MMail 1.0>")) conn.Write([]byte("MIME-Version: 1.0")) conn.Write([]byte("Content-Type: text/plain")) conn.Write([]byte("From: <" + this.From + ">\r\n")) conn.Write([]byte("To: <" + this.To + ">\r\n")) conn.Write([]byte("Subject: " + this.Subject + "\r\n")) conn.Write([]byte("\r\n")) conn.Write([]byte(this.Text)) conn.Write([]byte("\r\n.\r\n")) time.Sleep(5e9) conn.Write([]byte("QUIT\r\n")) conn.SetDeadline(time.Now().Add(deadline)) r, e = conn.Read(buf) if e != nil { fmt.Println(e) return } fmt.Println(string(buf[:r-1])) return }
package main import ( "./mail" "fmt" ) //NewSender(host, userName, password string, to []string, subject, text string) func main() { s := mail.NewSender("smtp.163.com:25", "killuaxyz@163.com", "**********", "976813280@qq.com", "hello mail", "Test mail") e := s.SendMail() if e != nil { fmt.Println(e) } }
输出结果:
220 163.com Anti-spam GT for Coremail System (163com[20121016]) 250-mail 250-PIPELINING 250-AUTH LOGIN PLAIN 250-AUTH=LOGIN PLAIN 250-coremail 1Uxr2xKj7kG0xkI17xGrU7I0s8FY2U3Uj8Cz28x1UUUUU7Ic2I0Y2Urz5c00UCa0xDrUUUUj 250-STARTTLS 250 8BITMIME 334 dXNlcm5hbWU6 334 UGFzc3dvcmQ6 235 Authentication successful 250 Mail OK 250 Mail OK 354 End data with <CR><LF>.<CR><LF> 250 Mail OK queued as smtp10,DsCowEBJW2Q4BQJTGF3hBA--.94S2 1392641336 421 closing transmission channel
连接成功就返回:
220 163.com Anti-spam GT for Coremail System (163com[20121016])
conn.Write([]byte("AUTH LOGIN\r\n")) 之后,服务器返回:334 dXNlcm5hbWU6
(使用base64Decode ,去解密 "dXNlcm5hbWU6" 其实就是“username:” )
这里就发送经过base64加密的用户名
对应的,收到 334 UGFzc3dvcmQ6 之后,就要发送经过base64加密的帐号密码过去,如果验证成功,服务器返回:235 Authentication successful
有疑问加站长微信联系(非本文作者)