在Go项目里通过net/smtp发送邮件是常见需求,但实际开发中常常碰到各种报错,例如超时、认证不通过或服务端直接断开连接。要高效排查这些问题,需要先理解SMTP会话的握手过程,尤其是EHLO命令的作用。

一、net/smtp常见错误类型
使用net/smtp时,典型错误可以分为以下几类:
- 拨号阶段错误:如连接超时、网络不可达,一般来自Dial或DialTLS。
- 握手阶段错误:服务端不支持EHLO返回错误,或TLS升级失败。
- 认证阶段错误:AUTH命令被拒绝,账号或密码不正确。
- 发信阶段错误:MAIL FROM、RCPT TO被服务端拒绝。
二、EHLO命令到底是什么
EHLO是扩展的HELO命令,客户端连接SMTP服务器后首先发送EHLO加上本机标识,服务器会返回它支持的能力,比如STARTTLS、AUTH LOGIN等。Go的net/smtp在Client.Hello方法里自动发送EHLO,如果失败再尝试HELO。
EHLO交互示例
假设我们连接到一个支持扩展的SMTP服务,原始对话类似:
C: EHLO localhost S: 250-mail.ippipp.com S: 250-STARTTLS S: 250-AUTH LOGIN PLAIN S: 250 8BITMIME
从中可以看到服务器允许明文认证并支持STARTTLS加密。
三、在Go中打印调试信息
net/smtp本身不会输出会话日志,但我们可以通过包装连接来读取原始数据。下面示例展示如何连接服务器并手动发送EHLO,然后打印服务端响应,用于调试。
package main
import (
"bufio"
"fmt"
"net"
"strings"
)
func main() {
// 连接到测试SMTP服务,此处使用ipipp.com示例域名
conn, err := net.Dial("tcp", "mail.ipipp.com:25")
if err != nil {
fmt.Println("拨号错误:", err)
return
}
defer conn.Close()
reader := bufio.NewReader(conn)
// 读取服务端的欢迎信息
line, _ := reader.ReadString('n')
fmt.Print("服务端:", line)
// 发送EHLO命令
cmd := "EHLO localhostrn"
conn.Write([]byte(cmd))
// 读取多行响应,直到状态码后没有短横线
for {
res, _ := reader.ReadString('n')
fmt.Print("服务端:", res)
if len(res) >= 4 && !strings.HasPrefix(res[3:4], "-") {
break
}
}
}利用Client对象查看能力
如果不想手写协议,也可以用smtp.NewClient之后调用Hello并查看Extensions字段:
package main
import (
"fmt"
"net/smtp"
)
func main() {
c, err := smtp.Dial("mail.ipipp.com:25")
if err != nil {
fmt.Println("连接失败:", err)
return
}
defer c.Quit()
// 发送EHLO并获取扩展能力
if err := c.Hello("localhost"); err != nil {
fmt.Println("EHLO错误:", err)
return
}
fmt.Println("支持的能力:", c.Extensions)
}四、常见报错与EHLO的关系
| 现象 | 可能原因 | 与EHLO关联 |
|---|---|---|
| 504 5.5.2 EHLO command failed | 域名无法解析或标识不合法 | EHLO参数应使用合法主机名 |
| 530 Must issue STARTTLS | 服务器要求加密 | EHLO返回中包含STARTTLS,需要先升级 |
| 235 Authentication failed | 认证信息错误 | EHLO返回AUTH说明支持认证方式 |
五、调试建议
遇到发送失败时,建议按下面步骤处理:
- 用上面包装连接的代码打印原始EHLO及后续响应。
- 确认服务端返回的能力是否包含你使用的认证或加密方式。
- 如果是TLS问题,使用smtp.DialTLS或调用StartTLS。
- 将错误中的状态码对照RFC或服务商文档分析。
提示:很多云服务商屏蔽25端口,需要使用587提交端口并配合STARTTLS,这时EHLO返回的内容会明显不同。
六、小结
Go语言net/smtp发送邮件的错误大多能在EHLO阶段看出端倪。通过主动读取会话日志、理解EHLO返回的能力列表,开发者可以快速判断是网络、TLS还是认证问题,从而减少盲目试错的时间。