Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Bagaimana untuk melaksanakan pelayan e-mel mudah menggunakan golang

Bagaimana untuk melaksanakan pelayan e-mel mudah menggunakan golang

PHPz
PHPzasal
2023-04-04 17:28:031817semak imbas

Dengan perkembangan dan pempopularan Internet, e-mel telah menjadi bahagian yang sangat diperlukan dalam kehidupan orang ramai. Artikel ini akan memperkenalkan cara menggunakan bahasa pengaturcaraan golang untuk melaksanakan pelayan e-mel yang mudah.

1. Persediaan persekitaran

Pertama sekali, anda perlu menyediakan persekitaran pembangunan golang secara tempatan. Selepas memasang golang, anda perlu menetapkan GOPATH Pembolehubah persekitaran ini menentukan direktori kerja golang Semua fail yang dibuat dalam direktori ini dianggap sebagai kod sumber golang.

Seterusnya, pasang perpustakaan POP3 dan SMTP melalui arahan berikut:

go get github.com/jordan-wright/email
go get github.com/beego/mux

Dua perpustakaan di atas digunakan untuk menghantar e-mel dan mengendalikan permintaan HTTP masing-masing.

2. Laksanakan pelayan POP3

POP3 ialah protokol penerimaan mel boleh dimuat turun daripada pelayan mel menggunakan protokol POP3. Untuk melaksanakan pelayan POP3, kita perlu menulis pelayan TCP dalam golang. Dalam golang, anda boleh menggunakan pakej "bersih" untuk melaksanakan pembangunan pelayan TCP.

Berikut ialah kod pelayan POP3 yang ringkas:

package main

import (
    "bufio"
    "fmt"
    "net"
    "strings"
)

func handlePOP3(conn net.Conn) {
    fmt.Fprintf(conn, "+OK POP3 ready\r\n")
    scanner := bufio.NewScanner(conn)
    for scanner.Scan() {
        command := scanner.Text()
        if strings.HasPrefix(command, "QUIT") {
            fmt.Fprintf(conn, "+OK Bye\r\n")
            conn.Close()
            return
        }
        fmt.Fprintf(conn, "-ERR unknown command\r\n")
    }
}

func main() {
    listener, err := net.Listen("tcp", ":110")
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        return
    }
    defer listener.Close()

    fmt.Println("Listening on :110")
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting connection:", err.Error())
            return
        }
        go handlePOP3(conn)
    }
}

Kod di atas mendengar secara setempat kepada port 110 (port lalai POP3 apabila pelanggan menyambung, goroutine mula mengendalikannya). . Semua arahan yang diterima oleh pelayan POP3 adalah rentetan, jadi kami menggunakan Pengimbas yang disediakan oleh pakej bufio untuk menghuraikan arahan.

Dalam fungsi handlePOP3, kami mula-mula menghantar "+OK POP3 ready" kepada klien untuk menunjukkan bahawa pelayan sudah sedia. Kemudian, ia terus membaca arahan yang dihantar oleh klien dalam gelung Jika ia menemui arahan "QUIT", ia menghantar "+OK Bye" untuk menamatkan sesi dan menutup sambungan. Jika arahan lain yang tidak diketahui diterima, hantarkan "-ERR unknown command" untuk memberitahu klien bahawa arahan itu tidak sah.

3. Laksanakan pelayan SMTP

SMTP ialah protokol penghantaran mel boleh dihantar ke pelayan mel menggunakan protokol SMTP. Untuk melaksanakan pelayan SMTP, kami perlu menambah beberapa kod berdasarkan pelayan POP3 untuk memproses arahan SMTP.

Berikut ialah kod pelayan SMTP yang ringkas:

package main

import (
    "fmt"
    "net"
    "net/mail"
    "net/smtp"
)

func handlePOP3(conn net.Conn) {
    // ...
}

func handleSMTP(conn net.Conn) {
    fmt.Fprintf(conn, "220 localhost SMTP Ready\r\n")
    state := 0
    var from, to, data string
    for {
        buf := make([]byte, 1024)
        _, err := conn.Read(buf)
        if err != nil {
            fmt.Println("Error reading:", err.Error())
            return
        }
        line := string(buf)
        switch state {
        case 0:
            if line[:4] != "HELO" {
                fmt.Fprintf(conn, "500 Error: bad command sequence\r\n")
                continue
            }
            fmt.Fprintf(conn, "250 localhost\r\n")
            state = 1
        case 1:
            if line[:4] != "MAIL" {
                fmt.Fprintf(conn, "500 Error: bad command sequence\r\n")
                continue
            }
            from = line[5 : len(line)-2]
            fmt.Fprintf(conn, "250 OK\r\n")
            state = 2
        case 2:
            if line[:4] != "RCPT" {
                fmt.Fprintf(conn, "500 Error: bad command sequence\r\n")
                continue
            }
            to = line[5 : len(line)-2]
            fmt.Fprintf(conn, "250 OK\r\n")
            state = 3
        case 3:
            if line[:4] == "DATA" {
                fmt.Fprintf(conn, "354 End data with <CR><LF>.<CR><LF>\r\n")
                state = 4
            } else if line[:4] == "HELO" {
                fmt.Fprintf(conn, "250 localhost\r\n")
            } else {
                fmt.Fprintf(conn, "500 Error: bad command sequence\r\n")
            }
        case 4:
            if line[:3] == "QUIT" {
                fmt.Fprintf(conn, "221 Bye\r\n")
                conn.Close()
                return
            }
            if line == ".\r\n" {
                fmt.Fprintf(conn, "250 OK: message accepted\r\n")
                msg, _ := mail.ReadMessage(strings.NewReader(data))
                smtp.SendMail("localhost:25", nil, "test@test.com", []string{to}, []byte(data))
                state = 1
            } else {
                data += line
            }
        }
    }
}

func main() {
    // ...
    router := mux.NewRouter()
    router.HandleFunc("/pop3", handlePOP3)
    router.HandleFunc("/smtp", handleSMTP)
    http.ListenAndServe(":8080", router)
}

Kod di atas mendengar secara setempat pada port 25 (port lalai SMTP apabila pelanggan menyambung, goroutine mula mengendalikannya). . Semua arahan yang diterima oleh pelayan SMTP juga adalah rentetan, jadi kami menggunakan kaedah Conn.Read yang disediakan oleh pakej bersih untuk membaca arahan.

Dalam fungsi handleSMTP, kami mula-mula menghantar "220 localhost SMTP Ready" kepada klien untuk menunjukkan bahawa pelayan SMTP sudah sedia. Kemudian, kekalkan mesin keadaan untuk mengendalikan proses penghantaran mel:

  • Nyatakan 0: Proses arahan HELO pelanggan dan masukkan Negeri 1
  • Keadaan 1: Proseskan arahan MAIL pelanggan, dan Masukkan State 2
  • State 2: Proses arahan RCPT klien, dan masukkan State 3
  • State 3: Tunggu klien menghantar arahan DATA, atau proses arahan HELO/QUIT, atau ralat berlaku dan Masukkan keadaan yang sepadan
  • Keadaan 4: Terima data e-mel yang dihantar oleh pelanggan (berakhir dengan "."), hantar e-mel dan masukkan Negeri 1

Jika arahan tidak sah diterima, hantar "500 Ralat: urutan arahan buruk" memberitahu pelanggan bahawa arahan itu tidak sah. Jika arahan QUIT diterima, "221 Bye" dihantar untuk menunjukkan tamat sesi dan sambungan ditutup. Dalam Negeri 4, kami menggunakan pakej net/mel untuk menghuraikan data e-mel dan pakej net/smtp untuk menghantar e-mel.

4. Pengujian

Pelayan peti mel yang dilaksanakan menggunakan kod di atas hanyalah contoh mudah Jika ia hendak digunakan dalam persekitaran pengeluaran sebenar, banyak aspek ujian dan pengoptimuman diperlukan. Berikut ialah kod klien SMTP ringkas yang ditulis dalam python Anda boleh menggunakan klien ini untuk menghantar e-mel ke pelayan SMTP kami dan menguji sama ada pelayan SMTP berfungsi dengan betul:

import smtplib

server = smtplib.SMTP('localhost', 25)
server.ehlo()
server.mail('test@test.com')
server.rcpt('test1@test.com')
server.data('Subject: Test Mail\n\nThis is a test email.\n')
server.quit()

Ringkasan

Artikel ini memperkenalkan cara melaksanakan pelayan e-mel mudah menggunakan bahasa pengaturcaraan golang. Menggunakan golang untuk menulis kod pelayan SMTP/POP3 mudah difahami dan dikembangkan Pada masa yang sama, ciri coroutine golang boleh menyokong konkurensi tinggi dan sangat sesuai untuk kerja pembangunan pengaturcaraan rangkaian.

Atas ialah kandungan terperinci Bagaimana untuk melaksanakan pelayan e-mel mudah menggunakan golang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn