Golang SMTP问题:程序挂起如何解决
Golang SMTP问题:程序挂起如何解决
我在使用SMTP时遇到了问题。我是一个新手用户,尝试修改了文档中的代码:
package main
import (
"log"
"net/smtp"
)
// 变量用于使ExamplePlainAuth能够编译,避免在那里添加不必要的噪音。
var (
from = "MYEMAIL_at_charter.net"
msg = []byte("dummy message")
recipients = []string{"MY_EMAIL_at_gmail.com"}
)
func main() {
// hostname被PlainAuth用于验证TLS证书。
hostname := "mobile.charter.net"
auth := smtp.PlainAuth("", "MYEMAIL_at_charter.net", "MY_CHARTER_PASSWD", hostname)
err := smtp.SendMail(hostname+":587", auth, from, recipients, msg)
if err != nil {
log.Fatal(err)
}
}
这个程序永远不会终止或给出错误信息。
我使用的参数是通过cURL成功发送邮件的参数:
curl --url smtps://mobile.charter.net:587 --ssl-reqd --mail-from MYEMAIL_at_charter.net --mail-rcpt MY_EMAIL_at_gmail.com --upload-file mail.txt --user MYEMAIL_at_charter.net:MY_CHARTER_PASSWD --insecure
任何帮助都将不胜感激。
更多关于Golang SMTP问题:程序挂起如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你能通过使用 fmt.Println() 调用来定位程序卡住的位置吗?
也可能只是超时设置得太长了。如果存在这种情况,我会查阅文档,因为这个问题可能再次出现。
在这里失败了:
err := smtp.SendMail(hostname+":587", auth, from, recipients, msg)
将端口从465改为25后,使用Gmail成功了!感谢你的提示。我还关闭了仅安全应用设置。在Charter上也同样有效。
我自己尝试了一下,对我来说,Gmail 使用端口 25 可以工作。但前提是,我需要在 Gmail 账户设置的 POP/IMAP 标签页中开启了 IMAP,并且在使用 Thunderbird 时正确配置了服务器设置。
我没有任何网络经验,但我猜想应该可以通过 printf 调试或使用 Wireshark 来查看为什么 smtp.SendMail 试图从服务器读取比实际获得更多的数据。或者使用(通过 exec.Command)支持超时的 SMTP 客户端,或者更换邮件服务,或者——如果你的邮件确实能发送出去——将这个阻塞操作放入一个 goroutine 中,并忽略它没有完美工作的事实。
func main() {
fmt.Println("hello world")
}
Go 新手在此,我不太确定,但我相信添加这段代码
import (
"os"
"os/signal"
"runtime/pprof"
"syscall"
)
func main() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
os.Exit(0) // 放在这里只是为了确保按 CTRL+C 时程序仍然会退出。
}()
}
将允许你按下 CTRL+C 并查看它在何处阻塞。
问题出在smtp.SendMail默认使用25端口,而你的SMTP服务器需要587端口并使用TLS。smtp.SendMail不会自动处理587端口的TLS升级,导致连接挂起。以下是修复后的代码:
package main
import (
"crypto/tls"
"log"
"net/smtp"
)
func main() {
from := "MYEMAIL_at_charter.net"
to := []string{"MY_EMAIL_at_gmail.com"}
msg := []byte("Subject: Test\n\nThis is a test email.")
host := "mobile.charter.net"
port := "587"
auth := smtp.PlainAuth("", from, "MY_CHARTER_PASSWD", host)
// 创建TLS配置
tlsConfig := &tls.Config{
ServerName: host,
InsecureSkipVerify: false, // 生产环境设为false
}
// 连接到服务器
conn, err := smtp.Dial(host + ":" + port)
if err != nil {
log.Fatal("连接失败:", err)
}
defer conn.Close()
// 启动TLS
if err = conn.StartTLS(tlsConfig); err != nil {
log.Fatal("TLS握手失败:", err)
}
// 认证
if err = conn.Auth(auth); err != nil {
log.Fatal("认证失败:", err)
}
// 设置发件人
if err = conn.Mail(from); err != nil {
log.Fatal("设置发件人失败:", err)
}
// 设置收件人
for _, addr := range to {
if err = conn.Rcpt(addr); err != nil {
log.Fatal("设置收件人失败:", err)
}
}
// 发送邮件内容
w, err := conn.Data()
if err != nil {
log.Fatal("准备数据写入失败:", err)
}
_, err = w.Write(msg)
if err != nil {
log.Fatal("写入数据失败:", err)
}
err = w.Close()
if err != nil {
log.Fatal("关闭写入器失败:", err)
}
// 退出
conn.Quit()
log.Println("邮件发送成功")
}
关键修改:
- 使用
smtp.Dial建立连接而非直接使用SendMail - 显式调用
StartTLS()进行TLS升级 - 分步执行SMTP协议命令(EHLO、STARTTLS、AUTH、MAIL FROM等)
如果服务器支持SSL/TLS,也可以使用465端口并直接建立TLS连接:
package main
import (
"crypto/tls"
"fmt"
"log"
"net/smtp"
)
func main() {
from := "MYEMAIL_at_charter.net"
to := []string{"MY_EMAIL_at_gmail.com"}
msg := []byte("Subject: Test\n\nThis is a test email.")
host := "mobile.charter.net"
port := "465"
auth := smtp.PlainAuth("", from, "MY_CHARTER_PASSWD", host)
// 直接建立TLS连接
tlsConfig := &tls.Config{
ServerName: host,
}
conn, err := tls.Dial("tcp", host+":"+port, tlsConfig)
if err != nil {
log.Fatal("TLS连接失败:", err)
}
defer conn.Close()
client, err := smtp.NewClient(conn, host)
if err != nil {
log.Fatal("创建SMTP客户端失败:", err)
}
defer client.Close()
if err = client.Auth(auth); err != nil {
log.Fatal("认证失败:", err)
}
if err = client.Mail(from); err != nil {
log.Fatal("设置发件人失败:", err)
}
for _, addr := range to {
if err = client.Rcpt(addr); err != nil {
log.Fatal("设置收件人失败:", err)
}
}
w, err := client.Data()
if err != nil {
log.Fatal("准备数据写入失败:", err)
}
_, err = w.Write(msg)
if err != nil {
log.Fatal("写入数据失败:", err)
}
err = w.Close()
if err != nil {
log.Fatal("关闭写入器失败:", err)
}
client.Quit()
fmt.Println("邮件发送成功")
}
使用第一个方案(587端口+STARTTLS)通常更符合现代SMTP服务器的配置。


