SMTP协议解读以及如何使用SMTP协议发送电子邮件
yuyutoo 2024-10-15 16:52 6 浏览 0 评论
电子邮件协议中POP3协议用于接收邮件,SMTP协议用于发送邮件。SMTP的全称为Simple Mail Transfer Protocol,也就是简单邮件传输协议,字如其名。
相较于POP3而言,SMTP确实比较简单。这里的简单并不是指SMTP的命令比POP3少,而是指SMTP的命令是有序的,而POP3的命令是无序的,理解这一点很重要。也就是说SMTP的命令是要组合在一起才能完成一次邮件发送任务,单独调用每个命令的意义不大。POP3命令则不同,LIST、STAT、UIDL、TOP、RETR、DELE等命令都可以独立使用,比如用LIST命令查看邮件清单,然后用RETR命令接收邮件。
简单的另一层含义是:就socket编程而言实现发送数据要比实现接收数据简单点。
比如接收数据时要判断数据是否接收完毕。如果一条数据以回车换行结束,就需要判断是否接收到了"\r\n",从而确保读取到一条完整的消息体。而发送数据则不需要考虑上述问题,你可以按照自己的节奏发送数据,可以一次将整个消息体发送出去,也可以不用考虑服务器的死活一个字节一个字节发送数据,直至将整条消息发送完毕。
换句话说,接收数据要以流的方式进行,而不是简单的开辟一个缓冲区,进行一次recv操作。 虽然大部分情况下这种方式也没有问题,比如写个Demo程序,但如果要让你的网络程序非常健壮的话,最好以流的方式进行读取。因为并不是每次对方都会按照你期望的方式发送数据给你,比如,你开辟了1024字节缓冲区用于接收网络数据,但对方可能一次只给你发送一个字节,或者发出了1025个字节。
SMTP和HTTP协议一样都属于请求应答式协议,也就是一问一答,客户端发送命令后,服务器返回响应内容。 SMTP的响应格式和HTTP协议的基本一样,都是响应码+响应描述。响应码用三位数字表示,空格后则是响应信息的描述,只是HTTP协议会多一个版本信息。
这种一问一答式协议,在HTTP协议上体现的并不是很明显,只有HTTP连接设置为Keep-Alive时,你才有机会使用GET或POST命令反复与服务器进行交互,否则只有一次问答的机会。
但在SMTP协议下这种一问一答的交互方式就非常明显了。 主要原因是完成一次邮件的发送任务涉及到的步骤比较多,我把电子邮件的发送分为如下五个步骤:
1、建立会话;
2、身份认证;
3、发送邮件信封(发件人和收件人);
4、发送邮件内容(邮件正文和附件);
5、关闭会话;
SMTP的命令主要就分布在这五个步骤中。下面以网易的yeah邮箱(smtp.yeah.net服务器)为例,具体说明这五个步骤的实现。C代表客户端,S代表服务端。
一、建立会话
SMTP命令:HELO
该阶段用于建立客户端与SMTP服务器的连接,在此基础上,双方进行友好的问候。SMTP服务器的默认端口号是25,如果是支持SSL协议,则默认端口号是465。如果采用的是STARTTLS协议,则默认端口是587,所谓的STARTSSL其实就是SSL协议,只是开始会话前双方客套一下,问一下对方你还支持SSL啊?
连接建立后,服务器会发送一条欢迎语。接着你就需要问候一下服务器,并带上你的机器的名称。如下:
S: 220 yeah.net Anti-spam GT for Coremail System (yeah[20141016])
C: HELO your-computer-name
S: 250 OK
二、身份认证
SMTP命令:AUTH LOGIN
该命令用于进行身份验证,虽然这一步在SMTP协议中不是强制的要求,但目前几乎所有的SMTP服务器都需要进行身份认证。增加这一步可以大大减少垃圾邮件的存在,以及避免有人伪造其它发件人进行邮件的发送操作。
这一步中账号和密码需要进行base64编码,包括服务器发来的提示信息也是base64编码。
首先发送AUTH LOGIN命令,服务器会返回“334 XNlcm5hbWU6”,“dXNlcm5hbWU6”解码后为“username:”
UGFzc3dvcmQ6解码为"Password:"
也就是提示用户输入用户名和密码。认证成功后返回235,注意返回的不是二百五(250)哦。 接着根据服务器返回的提示,发送账号(发件人的邮箱账号)和密码。
C: AUTH LOGIN
S: 334 dXNlcm5hbWU6
C: base64编码后的账号(发件人的邮箱账号)
S: 334 UGFzc3dvcmQ6
C: base64编码后的密码
S: 235 Authentication successful
至于为何是base64编码,可能是SMTP协议设计时考虑到用户名和密码的重要性,所采用的最简单的“加密”手段。虽然base64只是编码方式,不是加密方式,但在早期控制台输入命令的情况下,别人还是一下无法像记住明文一样记住这些无规律的base64编码。不过随着SSL的应用,这些都已不重要了。
三、发送邮件信封
SMTP命令:MAIL FROM、RCPT TO
该阶段是告诉服务器发件人和收件人的邮箱地址,可以把这个阶段想象为你在写纸质信件的信封。MAIL FROM用于指定发件人邮箱,该邮箱地址其实就是上述身份认证中的账号,如:
MAIL FROM: <lig4961@yeah.net>
RCPT TO用于指定收件人邮箱,一次只能指定一个收件人地址,如果收件人有多个的话,可以多次发送RCPT TO命令。
C: MAIL FROM: <lig4961@yeah.net>
S: 250 Mail OK
C: RCPT TO: <syfzxm@163.com>
S: 250 Mail OK
C: RCPT TO: <lig4961@yeah.net>
S: 250 Mail OK
注意,邮件地址要用放入<>中,此外,每条命令发送完毕后,一定要判断服务器返回码是否是250。 如果返回的不是二百五,说明你发送的地址可能是二百五,也就是不正确的地址,比如邮件地址中没有@,或者在同一个邮箱系统中,SMTP服务器发现收件人的地址并不存在,也就是没有注册过。
看到这里可能有人会有疑问:我们通过邮件客户端或网页写邮件时,不是有三种身份的收件人么?即:主送人(to)、抄送人(cc)、密送人(bcc)。是否存在RCPT CC和RCPT BCC命令,用于发送抄送人和密送人的邮箱地址呢?很遗憾,没有这两个命令。也就是说抄送人(cc)和密送人(bcc),也是通过RCPT TO命令进行发送。
既然发送时不区分,那么我们在收到的邮件中怎么还能看到主送人和抄送人呢?或者说如何做到让密送人在收到的邮件中看不见的。答案在下面邮件内容中。
四、发送邮件内容
SMTP命令:DATA
这一步是发送数据最多也是最复杂的一步,但操作命令却只有一个,就是DATA,也就是数据(邮件内容),邮件内容主要包括三个部分(可能会有内嵌资源文件,也可以理解为狭义上的附件):
1、邮件头;
2、邮件正文;
3、邮件附件;
DATA命令发送后,服务器会返回354响应码,并告诉客户端,数据结束要以"\r\n.\r\n"来标识。接下来客户端就可以发送整个邮件内容了。
DATA
354 End data with <CR><LF>.<CR><LF>
SUBJECT: =?UTF-8?B?5p2l6IeqU29mdGxlZe+8jOi/meaYr+S4gOWwgea1i+ivlemCruS7tg==?=
FROM: <lig4961@yeah.net>
TO: 'softlee1' <syfzxm@163.com>, 'softlee2' <lig4961@yeah.net>
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="=NextPart_SOFTLEE_Mail_E0B1A829CB1D4f55A037AE04B6A72078"
--=NextPart_SOFTLEE_Mail_E0B1A829CB1D4f55A037AE04B6A72078
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: base64
PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBYSFRNTCAxLjAgVHJhbnNpdGlvbmFs
Ly9FTiIgImh0dHA6Ly93d3cudzMub3JnL1RSL3hodG1sMS9EVEQveGh0bWwxLXRyYW5zaXRpb25h
bC5kdGQiPg0KPGh0bWwgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiPg0KDQo8
aGVhZD4NCiAgICA8bWV0YSBodHRwLWVxdWl2PSJDb250ZW50LVR5cGUiIGNvbnRlbnQ9InRleHQv
邮件内容的格式目前基本都采用MIME格式,MIME格式比较简单,可参见文章:《如何解析EML(邮件)格式的文件以及一款小巧的EML邮件阅读工具》。
这里我们不具体介绍如何编码邮件正文和附件。主要介绍邮件头中的信息,主题(Subject)、发件人(From)、收件人(To)、抄送(Cc)。我们看查看邮件时,读到的主题、收件人和抄送人就来自于上述字段。这里收件人和抄送人仅作为邮件头的一部分进行展现,服务器并不会关心这些地址是否真实存在,或者说服务器并不关心这些地址是否跟使用RCPT TO命令发送的地址保持一致。回到第三阶段中最后的几个问题,我们可以通过邮件头来展现该邮件的抄送人是谁,并且将密送人隐藏掉。当然你也可以篡改上述信息,比如隐瞒某些收件人,或者将密送人也一并展现。
邮件正文和附件的编码可参照MIME格式的文章。最后所有数据发送完毕后,一定要发送"\r\n.\r\n",从而告诉SMTP服务器所有数据发送完毕。
五、会话结束
SMTP命令:QUIT
这一步非常简单,就是发送一条QUIT命令,QUIT命令发送完毕后,还是要判断服务器的返回码是否为250。如果返回的不是,则表明发送失败,SMTP服务器可能不会将邮件转发到收件人所在的邮箱中,这一点很重要。
至此,整个SMTP协议介绍完毕。SMTP协议比较简单,但具体的实现细节可能还有很多,需要在实践中去体验,有的服务器返回消息体是多行的,比如outlook邮箱的服务器,下面是outlook邮箱使用STARTTLS协议截图:
附一: SMTP邮件发送工具
该工具特点:
1、基于命令行方式且只有一个独立文件;
2、支持SSL、STARTSSL协议;
3、具有丰富的命令行参数;
相关推荐
- 对volatile,synchronized,AQS的加锁解锁原理的一些理解
-
一、为什么要加锁,要实现同步多线程编程中,有可能会出现多个线程同时访问同一个共享、可变资源的情况,这个资源我们称之其为临界资源;这种资源可能是:对象、变量、文件等。...
- 注意,不能错过的CAS+volatile实现同步代码块
-
前言:最近看到有人说可以使用CAS+volatile实现同步代码块。心想,确实是可以实现的呀!因为AbstractQueuedSynchronizer(简称AQS)内部就是通过CAS+...
- 面试并发volatile关键字时,我们应该具备哪些谈资?
-
提前发现更多精彩内容,请访问https://dayarch.top/提前发现更多精彩内容,请访问https://dayarch.top/提前发现更多精彩内容,请访问https://dayarch...
- 无锁同步-JAVA之Volatile、Atomic和CAS
-
1、概要本文是无锁同步系列文章的第二篇,主要探讨JAVA中的原子操作,以及如何进行无锁同步。关于JAVA中的原子操作,我们很容易想到的是Volatile变量、java.util.concurrent....
- C/C++面试题(二):std::atomic与volatile
-
volatile是C/C++中的一个关键字,用于告知编译器某个变量的值可能会在程序的控制之外被意外修改(例如被硬件、中断服务程序、多线程环境或其他外部代理)。为了防止编译器对代码进行某些可能破坏...
- VOCs(Volatile Organic Compounds)挥发性有机化合物及测试方法
-
经常看到一些三防漆、涂料、油漆类产品的介绍中提到VOC、VOCs等概念,那么什么是VOC、VOCs和TVOC,VOCs主要包括哪些物质?VOCs的来源有哪些?VOCs的危害及国家标准是什么?一、V...
- 对volatile 及happen—before的理解
-
happen—before规则介绍Java...
- 这一篇我们来了解Synchronized、Volatile、Final关键字
-
题外话:蓝银王觉醒了!!--来自于一个斗罗大陆动漫爱好者(鹅,打钱!)湿兄这两天回家了,办了点大事,回来的时候我弟弟还舍不得我,哭着不愿意让我回京(我弟还是小学),我也心里很不舍,但是还是要回京奋斗...
- 关于 Java 关键字 volatile 的总结
-
1什么是volatilevolatile是Java的一个关键字,它提供了一种轻量级的同步机制。相比于重量级锁synchronized,volatile更为轻量级,因为它不会引起线程上下文...
- 大白话聊聊Java并发面试问题之volatile到底是什么?
-
用最简单的大白话,加上多张图给大家说一下,volatile到底是什么?...
- 为什么要有volatile关键字(volatile 关键字为什么不能保证原子性)
-
在嵌入式编程和多线程编程中,我们常会见到volatile关键字声明的变量。下面说一下volatile关键字的作用:1.保持变量内存可见简而言之就是用volatile声明的变量会告诉编译器和处理器,这个...
- Java的volatile到底怎么理解?(java volatitle)
-
我们都知道,在Java中有很多的关键字,比如synchronize比如volatile,这些都是一些比较关键的,还有final,今天我们就来聊一下这个volatile因为这个vo...
- Java多线程编程中的volatile关键字:解密神秘的共享内存
-
Java多线程编程中的volatile关键字:解密神秘的共享内存在Java多线程编程的世界里,volatile关键字就像一位低调却至关重要的守护者。它默默无闻地站岗放哨,确保多个线程之间能够正确地共享...
- 你了解volatile关键字的作用吗?(关键字volatile有什么含意?并举出三个不同的例子?)
-
【死记硬背】volatile关键字主要用于保持内存的变量可见性和禁止重排序。变量可见性:当一个线程改变了变量的值,那么新的值对于其他线程也是可以立即获取到的。禁止重排序:...
- 谈谈你对volatile 关键字作用和原理的理解
-
一位6年工作经验的小伙伴,在某里二面的时候被问到“volatile”关键字。然后,就没有然后了…同样,还有一位4年的小伙伴,去某团面试也被问到“volatile关键字“。然后,也没有然后了…...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
推荐7个模板代码和其他游戏源码下载的网址
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
-
- 对volatile,synchronized,AQS的加锁解锁原理的一些理解
- 注意,不能错过的CAS+volatile实现同步代码块
- 面试并发volatile关键字时,我们应该具备哪些谈资?
- 无锁同步-JAVA之Volatile、Atomic和CAS
- C/C++面试题(二):std::atomic与volatile
- VOCs(Volatile Organic Compounds)挥发性有机化合物及测试方法
- 对volatile 及happen—before的理解
- 这一篇我们来了解Synchronized、Volatile、Final关键字
- 关于 Java 关键字 volatile 的总结
- 大白话聊聊Java并发面试问题之volatile到底是什么?
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)