Golang为何没有原生SMTP服务器功能?

Golang为何没有原生SMTP服务器功能? 我们有 net/http 包,并且我看到还有一个 net/smtp 包,但这似乎只是一个 SMTP 客户端。难道没有原生的 SMTP 服务器可以用来发送电子邮件吗?

此致

4 回复

承接这个话题,构建SMTP服务器也是一件非常罕见的事情。

几乎所有使用Go语言的人都会在某个阶段构建某种类型的Web服务器。而几乎没有人会使用Go语言去构建SMTP服务器。因此前者被纳入标准库是合理的,但后者则不然。

更多关于Golang为何没有原生SMTP服务器功能?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


SMTP客户端用于发送电子邮件。SMTP服务器则负责接收电子邮件(当然之后可能还会继续转发这些邮件)。

构建SMTP服务器是一项极其庞大的工程,这其中有诸多原因,其中很多都与垃圾邮件和滥用行为相关。标准库中并未提供SMTP服务器。

一般来说,您并不需要SMTP服务器。如果确实需要,请安装现有且备受推崇的解决方案。您绝对不应该尝试自行构建SMTP服务器。

同意Jon的观点,这里有几个用Go语言编写的SMTP服务器,适合追求新潮的人士))

GitHub

flashmob/go-guerrilla

头像

go-guerrilla - 用Golang编写的迷你SMTP服务器


GitHub

toorop/tmail

头像

tmail - Golang SMTP服务器


GitHub

emersion/go-smtp

头像

go-smtp - 📤 用Go编写的SMTP客户端和服务器库


GitHub

chrj/smtpd

头像

smtpd - Go SMTP服务器库

Go语言标准库中的net/smtp包确实只提供了SMTP客户端功能,而没有内置SMTP服务器实现。这主要是由于以下几个原因:

  1. 设计哲学:Go语言标准库专注于提供网络协议的基础构建块,而不是完整的应用服务器
  2. 复杂性:完整的SMTP服务器需要处理认证、队列管理、TLS、反垃圾邮件等复杂功能
  3. 使用场景:大多数应用只需要发送邮件(客户端功能),而非接收邮件

不过,你可以使用第三方库来构建SMTP服务器。以下是一个使用github.com/flashmob/go-fastcgi-server的简单示例:

package main

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

type SMTPHandler struct{}

func (h *SMTPHandler) Handle(conn net.Conn) {
    defer conn.Close()
    
    // 发送欢迎消息
    conn.Write([]byte("220 localhost ESMTP Go SMTP Server\r\n"))
    
    buf := make([]byte, 1024)
    for {
        n, err := conn.Read(buf)
        if err != nil {
            break
        }
        
        cmd := strings.TrimSpace(string(buf[:n]))
        fmt.Printf("Received: %s\n", cmd)
        
        switch {
        case strings.HasPrefix(cmd, "HELO"), strings.HasPrefix(cmd, "EHLO"):
            conn.Write([]byte("250 Hello\r\n"))
        case strings.HasPrefix(cmd, "MAIL FROM"):
            conn.Write([]byte("250 OK\r\n"))
        case strings.HasPrefix(cmd, "RCPT TO"):
            conn.Write([]byte("250 OK\r\n"))
        case strings.HasPrefix(cmd, "DATA"):
            conn.Write([]byte("354 End data with <CR><LF>.<CR><LF>\r\n"))
        case cmd == ".":
            conn.Write([]byte("250 OK: Message accepted\r\n"))
        case strings.HasPrefix(cmd, "QUIT"):
            conn.Write([]byte("221 Bye\r\n"))
            return
        default:
            conn.Write([]byte("500 Command not recognized\r\n"))
        }
    }
}

func main() {
    listener, err := net.Listen("tcp", ":2525")
    if err != nil {
        log.Fatal(err)
    }
    defer listener.Close()
    
    handler := &SMTPHandler{}
    
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Printf("Accept error: %v", err)
            continue
        }
        go handler.Handle(conn)
    }
}

对于生产环境,推荐使用更成熟的第三方SMTP服务器库,如:

  • github.com/flashmob/go-guerrilla
  • github.com/emersion/go-smtp

以下是使用github.com/emersion/go-smtp的示例:

package main

import (
    "log"
    
    "github.com/emersion/go-smtp"
)

type Backend struct{}

func (bkd *Backend) NewSession(_ *smtp.Conn) (smtp.Session, error) {
    return &Session{}, nil
}

type Session struct{}

func (s *Session) AuthPlain(username, password string) error {
    return nil
}

func (s *Session) Mail(from string, opts *smtp.MailOptions) error {
    return nil
}

func (s *Session) Rcpt(to string, opts *smtp.RcptOptions) error {
    return nil
}

func (s *Session) Data(r io.Reader) error {
    return nil
}

func (s *Session) Reset() {}

func (s *Session) Logout() error {
    return nil
}

func main() {
    be := &Backend{}
    
    s := smtp.NewServer(be)
    s.Addr = ":1025"
    s.Domain = "localhost"
    s.ReadTimeout = 10 * time.Second
    s.WriteTimeout = 10 * time.Second
    s.MaxMessageBytes = 1024 * 1024
    s.MaxRecipients = 50
    s.AllowInsecureAuth = true
    
    log.Println("Starting SMTP server at :1025")
    log.Fatal(s.ListenAndServe())
}

这些第三方库提供了完整的SMTP服务器实现,包括认证、TLS支持等高级功能。

回到顶部