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

11 回复

哦,确实,我当时也把它关掉了。

更多关于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 时正确配置了服务器设置。

使用Charter服务器时,邮件无法发送。将服务器更改为Gmail:

hostname := "smtp.gmail.com"
.
.
.
err := smtp.SendMail(hostname+":465", auth, from, recipients, msg)

同时修改了邮件信息以包含“收件人:”和“发件人:”字段。

现在,程序不再卡住,但邮件仍然无法发送。以下是输出信息: 2020/06/25 10:01:07 EOF exit status 1

我没有任何网络经验,但我猜想应该可以通过 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("邮件发送成功")
}

关键修改:

  1. 使用smtp.Dial建立连接而非直接使用SendMail
  2. 显式调用StartTLS()进行TLS升级
  3. 分步执行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服务器的配置。

回到顶部