SMTP 简介

SMTP(Simple Mail Transfer Protocol) 是基于文本的简单邮件协议,默认使用 25 端口。下图展示了 SMTP 的基本操作。

sendMail

  1. Alice 通过 user agent 指定 Bob 的邮件地址以及编辑邮件,然后发送邮件;

  2. Alice 的 user agent 先把邮件发送到 Alice 的邮件服务器,邮件服务器用把放到消息队列中;

  3. 与此同时,Alice 的邮件服务器的客户端(这个邮件服务器既充当服务器也充当客户端)在队列中发现了 Alice 发向 Bob 的邮件,于是就向 Bob 的邮件服务器发起连接;

  4. 在连接初始化后,Alice 邮件服务器把 Alice 的邮件通过连接发送出去;

  5. Bob 的邮件服务器收到了来自 Alice 邮件服务器的邮件,把该邮件放到 Bob 的个人邮箱;

  6. Bob 通过 user agent 读取邮件(注意:这里的读取用的并不是 SMTP 协议)。

由此可见,SMTP 不会使用中间邮件服务器发送邮件,即使两个邮件服务器之间在地球的两端。我们可以使用 telnet 进行邮件发送,以下是使用 qq 的 SMTP 服务器发送邮件的完整流程。

与 HTTP 协议相比,HTTP 协议是pull类的协议,从服务器 pull 数据对象;而 SMTP 由是push类的协议,向 SMTP 服务器 push 邮件。

以下是 Go 语言写的 SMTP 用户端。

package main

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

func SendMail(user, password, host, to, subject, body, mailType string) error {
    hp := strings.Split(host, ":")
    auth := smtp.PlainAuth("", user, password, hp[0])

    var contentType string
    if mailType == "text/html" {
        contentType = "Content-Type: " + mailType + ";charset=utf-8"
    } else {
        contentType = "Content-Type: text/plain" + ";charset=utf-8"
    }

    sendTo := strings.Split(to, ";")
    msg := []byte(
        "To: " + to + "\r\n" +
            "From: " + user + "\r\n" +
            "Subject: " + subject + "\r\n" +
            contentType + "\r\n\r\n" +
            body)

    err := smtp.SendMail(host, auth, user, sendTo, msg)
    return err
}

func main() {
    user := "xxx@163.com"
    password := "***" // 使用邮箱的授权码,一般都不会直接使用登录密码
    host := "smtp.163.com:25"
    to := "yyy@foxmail.com"

    subject := "使用 Go 发送邮件"
    body := `
<html>
<body>
    <h1>Hello world!</h1>
</body>
</html>
    `

    fmt.Println("sending...")
    err := SendMail(user, password, host, to, subject, body, "text/html")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("has sent successfully!")
}
Tags: