go smtp实现邮件发送示例详解

目录
  • smtp指令
  • go demo
  • sdk中SendMail方法
  • DialAndSend实现

smtp指令

书接上文邮件实现详解,这里我们及我们简单复习一下smtp的指令如下:

telnet smtp.163.com 25
[outpout]
ehlo dz45693
[outpout]
auth login
[outpout]
输入用户名base64
[outpout]
输入密码base64
mail from:<dz45693@163.com>
[outpout]
rcpt to:<dz45693@sina.com>
[outpout]
data
[outpout]
from:<dz45693@163.com>
to:<dz45693@sina.com>
subject:hello world
This is the first email sent by hand using the SMTP protocol
quit  

go demo

好,那我们下现在用go实现代码让如下:这里只是一个demo,主要熟悉smtp命令

package main
import (
	"bufio"
	"encoding/base64"
	"fmt"
	"net"
	"strconv"
	"strings"
)
func main() {
	testSmtp()
}
var gConn net.Conn
var gRead *bufio.Reader
var gWrite *bufio.Writer
//可以放到这样的类里
type TcpClient struct {
	Conn  net.Conn
	Read  *bufio.Reader
	Write *bufio.Writer
} //
func Connect(host string, port int) (net.Conn, *bufio.Reader, *bufio.Writer) {
	addr := host + ":" + strconv.Itoa(port)
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, nil, nil
	}
	reader := bufio.NewReader(conn)
	writer := bufio.NewWriter(conn)
	return conn, reader, writer
} //
//收取一行,可再优化
func RecvLine() string {
	line, err := gRead.ReadString('\n') //如何设定超时?
	if err != nil {
		fmt.Print(err)
		return ""
	}
	line = strings.Split(line, "\r")[0] //还要再去掉 "\r",其实不去掉也可以
	return line
}
func SendLine(line string) {
	gWrite.WriteString(line + "\r\n")
	gWrite.Flush()
}
//解码一行命令,这里比较简单就是按空格进行分隔就行了
func DecodeCmd(line string, sp string) []string {
	tmp := strings.Split(line, sp)
	var cmds = []string{"", "", "", "", ""} //先定义多几个,以面后面使用时产生异常
	for i := 0; i < len(tmp); i++ {
		if i >= len(cmds) {
			break
		}
		cmds[i] = tmp[i]
	}
	return cmds
}
//读取多行结果
func RecvMCmd() string {
	i := 0
	rs := ""
	mLine := ""
	for i = 0; i < 50; i++ {
		rs = RecvLine() //只收取一行
		mLine = mLine + rs + "\r\n"
		if len(rs) < 4 {
			break
		} //长度要足够
		c4 := rs[4-1] //第4个字符
		if ' ' == c4 {
			break
		} //第4个字符是空格就表示读取完了//也可以判断 "250[空格]"
	}
	return mLine
}
//简单的测试一下 smtp
func testSmtp() {
	//连接
	gConn, gRead, gWrite = Connect("smtp.163.com", 25)
	defer gConn.Close()
	//收取一行
	line := RecvLine()
	fmt.Println("recv:" + line)
	//解码一下,这样后面的 EHLO 才能有正确的第二个参数
	cmds := DecodeCmd(line, " ")
	domain := cmds[1] //要从对方的应答中取出域名//空格分开的各个命令参数中的第二个
	//发送一个命令
	SendLine("EHLO" + " " + domain) //domain 要求其实来自 HELO 命令//HELO <SP> <domain> <CRLF>
	//收取多行
	line = RecvMCmd()
	fmt.Println("recv:" + line)
	//--------------------------------------------------
	//用 base64 登录
	SendLine("AUTH LOGIN")
	//收取一行
	line = RecvLine()
	fmt.Println("recv:" + line)
	s := "dz45693" //要换成你的用户名,注意 163 邮箱的话不要带后面的 @域名 部分
	s = base64.StdEncoding.EncodeToString([]byte(s))
	SendLine(s)
	//收取一行
	line = RecvLine()
	fmt.Println("recv:" + line)
	s = "xxxxx" //要换成您的密码
	s = base64.StdEncoding.EncodeToString([]byte(s))
	SendLine(s)
	//收取一行
	line = RecvLine()
	fmt.Println("recv:" + line)
	//--------------------------------------------------
	//邮件内容
	from := "dz45693@163.com"
	to := "dz45693@sina.com"
	SendLine("MAIL FROM: <" + from + ">") //注意"<" 符号和前面的空格。空格在协议中有和没有都可能,最好还是有
	//收取一行
	line = RecvLine()
	fmt.Println("recv:" + line)
	SendLine("RCPT TO: <" + to + ">")
	//收取一行
	line = RecvLine()
	fmt.Println("recv:" + line)
	SendLine("DATA")
	//收取一行
	line = RecvLine()
	fmt.Println("recv:" + line)
//发送邮件头
	SendLine("from:<dz45693@163.com>")
	SendLine("to:<dz45693@sina.com>")
	SendLine("subject:hello world")
	SendLine("") //发送空行 后面就是邮件体
	SendLine("This is the first email sent by hand using the SMTP protocol")
	SendLine(".") //邮件结束符
	//收取一行
	line = RecvLine()
	fmt.Println("recv:" + line)
	SendLine("quit") //链接推出
	line = RecvLine()
	fmt.Println("recv:" + line)
} //

运行结果如下:

sdk中SendMail方法

在go的sdk中提供了SendMail方法【发送邮件后这个方法会关闭链接】,实现如下:

实现如下:

 func SendMailBySmtp(){
	auth := smtp.PlainAuth("", "dz45693@163.com", "xxx", "smtp.163.com")
	to := []string{"dz45693@sina.com"}
	image,_:=ioutil.ReadFile("d:\\Downloads\\1.png")
	imageBase64:=base64.StdEncoding.EncodeToString(image)
	msg := []byte("from:dz45693@163.com\r\n"+
		"to: dz45693@sina.com\r\n" +
		"Subject: hello,subject!\r\n"+
		"Content-Type:multipart/mixed;boundary=a\r\n"+
		"Mime-Version:1.0\r\n"+
		"\r\n" +
		"--a\r\n"+
		"Content-type:text/plain;charset=utf-8\r\n"+
		"Content-Transfer-Encoding:quoted-printable\r\n"+
		"\r\n"+
		"此处为正文内容!\r\n"+
		"--a\r\n"+
		"Content-type:image/jpg;name=1.jpg\r\n"+
		"Content-Transfer-Encoding:base64\r\n"+
		"\r\n"+
		imageBase64+"\r\n"+
		"--a--\r\n")
	err := smtp.SendMail("smtp.163.com:25", auth, "dz45693@163.com", to, msg)
	if err != nil {
		fmt.Println(err)
	}
}

运行效果:

使用第三方库gomail实现邮件的发送更多了解,

请前往:https://pkg.go.dev/gopkg.in/gomail.v2?utm_source=godoc

示例如下:

func SendMailByGomailOne(){
	m := gomail.NewMessage()
	m.SetAddressHeader("From", "dz45693@163.com", "dz45693")
	m.SetHeader("To", "dz45693@sina.com")
	m.SetHeader("Subject", "hello SendMailByGomailOne!")
	m.Embed("d:\\Downloads\\1.png")
	m.SetBody("text/html", "此处为正文121333!")
	d := gomail.NewDialer("smtp.163.com", 25, "dz45693@163.com", "xxxx")
	if err := d.DialAndSend(m); err != nil {
		panic(err)
	}
}

运行结果:

DialAndSend实现

来我们看看DialAndSend的实现如下:

package gomail
import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"net/smtp"
	"strings"
	"time"
)
// A Dialer is a dialer to an SMTP server.
type Dialer struct {
	// Host represents the host of the SMTP server.
	Host string
	// Port represents the port of the SMTP server.
	Port int
	// Username is the username to use to authenticate to the SMTP server.
	Username string
	// Password is the password to use to authenticate to the SMTP server.
	Password string
	// Auth represents the authentication mechanism used to authenticate to the
	// SMTP server.
	Auth smtp.Auth
	// SSL defines whether an SSL connection is used. It should be false in
	// most cases since the authentication mechanism should use the STARTTLS
	// extension instead.
	SSL bool
	// TSLConfig represents the TLS configuration used for the TLS (when the
	// STARTTLS extension is used) or SSL connection.
	TLSConfig *tls.Config
	// LocalName is the hostname sent to the SMTP server with the HELO command.
	// By default, "localhost" is sent.
	LocalName string
}
// NewDialer returns a new SMTP Dialer. The given parameters are used to connect
// to the SMTP server.
func NewDialer(host string, port int, username, password string) *Dialer {
	return &Dialer{
		Host:     host,
		Port:     port,
		Username: username,
		Password: password,
		SSL:      port == 465,
	}
}
// NewPlainDialer returns a new SMTP Dialer. The given parameters are used to
// connect to the SMTP server.
//
// Deprecated: Use NewDialer instead.
func NewPlainDialer(host string, port int, username, password string) *Dialer {
	return NewDialer(host, port, username, password)
}
// Dial dials and authenticates to an SMTP server. The returned SendCloser
// should be closed when done using it.
func (d *Dialer) Dial() (SendCloser, error) {
	conn, err := netDialTimeout("tcp", addr(d.Host, d.Port), 10*time.Second)
	if err != nil {
		return nil, err
	}
	if d.SSL {
		conn = tlsClient(conn, d.tlsConfig())
	}
	c, err := smtpNewClient(conn, d.Host)
	if err != nil {
		return nil, err
	}
	if d.LocalName != "" {
		if err := c.Hello(d.LocalName); err != nil {
			return nil, err
		}
	}
	if !d.SSL {
		if ok, _ := c.Extension("STARTTLS"); ok {
			if err := c.StartTLS(d.tlsConfig()); err != nil {
				c.Close()
				return nil, err
			}
		}
	}
	if d.Auth == nil && d.Username != "" {
		if ok, auths := c.Extension("AUTH"); ok {
			if strings.Contains(auths, "CRAM-MD5") {
				d.Auth = smtp.CRAMMD5Auth(d.Username, d.Password)
			} else if strings.Contains(auths, "LOGIN") &&
				!strings.Contains(auths, "PLAIN") {
				d.Auth = &loginAuth{
					username: d.Username,
					password: d.Password,
					host:     d.Host,
				}
			} else {
				d.Auth = smtp.PlainAuth("", d.Username, d.Password, d.Host)
			}
		}
	}
	if d.Auth != nil {
		if err = c.Auth(d.Auth); err != nil {
			c.Close()
			return nil, err
		}
	}
	return &smtpSender{c, d}, nil
}
func (d *Dialer) tlsConfig() *tls.Config {
	if d.TLSConfig == nil {
		return &tls.Config{ServerName: d.Host}
	}
	return d.TLSConfig
}
func addr(host string, port int) string {
	return fmt.Sprintf("%s:%d", host, port)
}
// DialAndSend opens a connection to the SMTP server, sends the given emails and
// closes the connection.
func (d *Dialer) DialAndSend(m ...*Message) error {
	s, err := d.Dial()
	if err != nil {
		return err
	}
	defer s.Close()
	return Send(s, m...)
}
type smtpSender struct {
	smtpClient
	d *Dialer
}
func (c *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
	if err := c.Mail(from); err != nil {
		if err == io.EOF {
			// This is probably due to a timeout, so reconnect and try again.
			sc, derr := c.d.Dial()
			if derr == nil {
				if s, ok := sc.(*smtpSender); ok {
					*c = *s
					return c.Send(from, to, msg)
				}
			}
		}
		return err
	}
	for _, addr := range to {
		if err := c.Rcpt(addr); err != nil {
			return err
		}
	}
	w, err := c.Data()
	if err != nil {
		return err
	}
	if _, err = msg.WriteTo(w); err != nil {
		w.Close()
		return err
	}
	return w.Close()
}
func (c *smtpSender) Close() error {
	return c.Quit()
}
// Stubbed out for tests.
var (
	netDialTimeout = net.DialTimeout
	tlsClient      = tls.Client
	smtpNewClient  = func(conn net.Conn, host string) (smtpClient, error) {
		return smtp.NewClient(conn, host)
	}
)
type smtpClient interface {
	Hello(string) error
	Extension(string) (bool, string)
	StartTLS(*tls.Config) error
	Auth(smtp.Auth) error
	Mail(string) error
	Rcpt(string) error
	Data() (io.WriteCloser, error)
	Quit() error
	Close() error
}

DialAndSend ,首先调用Dial方法创建连接,然后发送邮件,最后关闭链接,如果要频繁发邮件,那么是否保持长连接更好了?这里的Dial 调用了smtp.NewClient 创建smtp.Client对象c,然后调用c.Hello ,c.Auth,send 实际是调用c.Mail,c.Rcpt,c.Data,那么我们可以自己调用Dial方法 然后循环调用send方法,最后在close。

代码如下:

func SendMailByGomailTwo() {
	d := gomail.NewDialer("smtp.163.com", 25, "dz45693@163.com", "xxxx")
	m := gomail.NewMessage()
	m.SetAddressHeader("From", "dz45693@163.com", "dz45693")
	m.SetHeader("To", "dz45693@sina.com")
	m.SetHeader("Subject", "hello SendMailByGomailtwo!")
	m.Embed("d:\\Downloads\\1.png")
	m.SetBody("text/html", "此处为正文121333!SendMailByGomailtwo")
	s, err := d.Dial()
	if err != nil {
		panic(err)
	}
	defer s.Close()
	err = gomail.Send(s, m)
	if err != nil {
		panic(err)
	}
	m.Reset()
	m.SetAddressHeader("From", "dz45693@163.com", "dz45693")
	m.SetHeader("To", "dz45693@sina.com")
	m.SetHeader("Subject", "hello SendMailByGomailthree!")
	m.Embed("d:\\Downloads\\2.png")
	m.SetBody("text/html", "此处为正文1SendMailByGomailthreeSendMailByGomailthree!")
	err = gomail.Send(s, m)
	if err != nil {
		panic(err)
	}
}

运行结果:

以上就是go smtp实现邮件发送示例详解的详细内容,更多关于go smtp邮件发送的资料请关注我们其它相关文章!

(0)

相关推荐

  • go语言发送smtp邮件的实现示例

    最近看了下go发送smtp邮件,于是总结一下 简单示例 先上一个最简单的代码 (网上搂的代码改了改) package main import ( "fmt" "net/smtp" ) const ( // 邮件服务器地址 SMTP_MAIL_HOST = "smtp.qq.com" // 端口 SMTP_MAIL_PORT = "587" // 发送邮件用户账号 SMTP_MAIL_USER = "134858167

  • django中SMTP发送邮件配置详解

    Django中内置了邮件发送功能,被定义在django.core.mail模块中.发送邮件需要使用SMTP服务器,常用的免费服务器有:163.126.QQ,下面以qq邮箱为例. 注册qq邮箱,然后登录设置 找到设置里面POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 3.需要发送验证码生成授权码 4.找到settings.py文件,中点击下图配置 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBacken

  • django 邮件发送模块smtp使用详解

    前言 在Python中已经内置了一个smtp邮件发送模块,Django在此基础上进行了简单地封装,让我们在Django环境中可以更方便更灵活的发送邮件. 所有的功能都在django.core.mail中. 一.快速上手 两行就可以搞定一封邮件: from django.core.mail import send_mail send_mail( 'Subject here', 'Here is the message.', 'from@example.com', ['to@example.com'

  • Golang利用自定义模板发送邮件的方法详解

    前言 在几周前,我开始工作于一个证券投资组合网站.虽然我只能使用 React 完成整个网站,但我决定使用 Go 来创建一个可以处理某些任务(例如发送 email)的 API 服务器,相信这是一个很好的做法. 我其中的一个页面是一个 contact 页面,目前看起来像这样: contact me 我想使用专门为此 contact 表单申请的 Gmail 帐户发送一封邮件.除了我以前用过 Javascript 发送电子邮件的事实,我没有特别选择 Go.但为什么不尝试 Go 呢?我觉得 Go 很棒.

  • go smtp实现邮件发送示例详解

    目录 smtp指令 go demo sdk中SendMail方法 DialAndSend实现 smtp指令 书接上文邮件实现详解,这里我们及我们简单复习一下smtp的指令如下: telnet smtp.163.com 25 [outpout] ehlo dz45693 [outpout] auth login [outpout] 输入用户名base64 [outpout] 输入密码base64 mail from:<dz45693@163.com> [outpout] rcpt to:<

  • JavaMail邮件发送机制详解

    这篇文章主要介绍了JavaMail邮件发送机制详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 概念 JavaMail,顾名思义,提供给开发者处理电子邮件相关的编程接口.它是Sun发布的用来处理email的API.它可以方便地执行一些常用的邮件传输.我们可以基于JavaMail开发出类似于Microsoft Outlook的应用程序. 应用场景 一般在系统的注册模块,当用户填入注册信息的邮箱时,点击保存.系统根据用户的信息会自动给用户发送一封

  • Python实现自动化邮件发送过程详解

    使用Python实现自动化邮件发送,可以让你摆脱繁琐的重复性业务,可以节省非常多的时间. 操作前配置(以较为复杂的QQ邮箱举例,其他邮箱操作类似) 单击设置-账号,滑倒下方协议处,开启IMAP/SMTP协议(IMAP,即Internet Message Access Protocol(互联网邮件访问协议),可以通过这种协议从邮件服务器上获取邮件的信息.下载邮件等.IMAP与POP类似,都是一种邮件获取协议.) (ps.开启需要验证) 记住端口号,后续写代码发送邮件时候需要 生成授权码,前期配置完

  • Python自动化办公之邮件发送全过程详解

    使用Python实现自动化邮件发送,可以让你摆脱繁琐的重复性业务,可以节省非常多的时间.操作前配置(以较为复杂的QQ邮箱举例,其他邮箱操作类似) 单击设置-账号,滑倒下方协议处,开启IMAP/SMTP协议(IMAP,即Internet Message Access Protocol(互联网邮件访问协议),可以通过这种协议从邮件服务器上获取邮件的信息.下载邮件等.IMAP与POP类似,都是一种邮件获取协议.) (ps.开启需要验证) 记住端口号,后续写代码发送邮件时候需要: 生成授权码,前期配置完

  • springboot实现自动邮件发送任务详解

    目录 1.导入jar包 2.配置文件 3.测试 复杂的邮件发送 springboot可以很容易实现邮件的发送 具体实现步骤: 1.导入jar包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> <version>2.5.2</version> </depe

  • Python实现邮件发送功能的示例详解

    想实现发送邮件需要经过以下几步: 1.登录邮件服务器 2.构造符合邮件协议规则要求的邮件内容 3.发送 Python对SMTP支持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件,它对smtp协议进行了简单的封装. 这里我们用qq邮箱为例,并且自己是可以给自己发邮件的. 在开始前我们先做准备工作: 登录qq邮箱,后点击“设置” 点击“账户” 确保前两项已开启,然后点击生成授权码. 因为我们网页登录时的密码是不可以用来python上使用:qq为了安全,我们平

  • Python实现邮件自动下载的示例详解

    开始码代码之前,我们先来了解一下三种邮件服务协议: 1.SMTP协议 SMTP(Simple Mail Transfer Protocol),即简单邮件传输协议.相当于中转站,将邮件发送到客户端. 2.POP3协议 POP3(Post Office Protocol 3),即邮局协议的第3个版本,是电子邮件的第一个离线协议标准.该协议把邮件下载到本地计算机,不与服务器同步,缺点是更易丢失邮件或多次下载相同的邮件. 3.IMAP协议 IMAP(Internet Mail Access Protoc

  • python爬虫使用requests发送post请求示例详解

    简介 HTTP协议规定post提交的数据必须放在消息主体中,但是协议并没有规定必须使用什么编码方式.服务端通过是根据请求头中的Content-Type字段来获知请求中的消息主体是用何种方式进行编码,再对消息主体进行解析.具体的编码方式包括: application/x-www-form-urlencoded 最常见post提交数据的方式,以form表单形式提交数据. application/json 以json串提交数据. multipart/form-data 一般使用来上传文件. 一. 以f

  • Python实现日志实时监测的示例详解

    目录 介绍 观察者模式类图 观察者模式示例 1.创建订阅者类 2.创建发布者类 3.应用客户端-Map_server_client.py 4.测试 介绍 观察者模式:是一种行为型设计模式.主要关注的是对象的责任,允许你定义一种订阅机制,可在对象事件发生时通知多个"观察"该对象的其他对象.用来处理对象之间彼此交互. 观察者模式也叫发布-订阅模式,定义了对象之间一对多依赖,当一个对象改变状态时,这个对象的所有依赖者都会收到通知并按照自己的方式进行更新. 观察者设计模式是最简单的行为模式之一

  • jQuery.Validate表单验证插件的使用示例详解

    jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求. 请在这里查看示例 validate示例 示例包含 验证错误时,显示红色错误提示 自定义验证规则 引入中文错误提示 重置表单需要执行2句话 源码示例 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <

随机推荐