2.3 因特网中的电子邮件

先搞清几个角色:谁在干什么?

书里说因特网电子邮件系统有 3 个主要组成部分

  1. 用户代理(user agent)
  2. 邮件服务器(mail server)
  3. 简单邮件传输协议 SMTP

我用“写纸质信 + 邮局”的比喻来讲:

1)用户代理 = 邮件“写信/收信工具”

  • 就是你直接操作的那个软件
    • 比如:QQ 邮箱网页、Outlook、Foxmail、手机里的“邮件”App。
  • 它负责:
    • 写邮件:填收件人、主题、内容、附件;
    • 看邮件:列出收件箱、显示邮件;
    • 回复、转发、删除、存草稿。

比喻:用户代理就像你的“笔 + 信纸 + 家里的信箱”。
你在这上面写信、读信,但它本身不能把信送到对方城市去。

书里举的例子:Outlook、Apple Mail 都是典型的用户代理。

邮件服务器 = 邮局里的“分拣中心 + 信箱”

每个用户所属的邮件系统后台都有一台或多台 邮件服务器
比如 qq.com163.comustc.edu.cn 的邮件服务器。

一台邮件服务器有两块重要的“存储区域”(图 2-14 里画出来了):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 用户邮箱 mailbox(图例里小格子的那块)
    • 每个用户都有一个邮箱,比如 Bob 的邮箱。
    • 里面存的是“发给这个用户的邮件”。
    • 当 Bob 要收信时,他的用户代理会去“自己的邮件服务器里的 Bob 那个邮箱”把信取出来。
  2. 外出报文队列 message queue(图例里那条一条一条排队的区域)
    • 存的是“准备发往别的邮件服务器的邮件”。
    • 像邮局里准备出发、排好队等车的信袋。

比喻:

  • 邮件服务器 = 你所在城市的大邮局;
  • 用户邮箱 mailbox = 邮局里专门给你的一格小信箱;
  • 外出报文队列 message queue = 等待出城、送去外地的信袋队列。

书里说:邮件服务器是电子邮件体系结构的核心,就是这个意思——所有信最终都要先到“自己这边的服务器”,再从服务器之间互相转来转去。

SMTP 协议 = 邮局之间的“送信规矩”

SMTP(Simple Mail Transfer Protocol) 是一个应用层协议,专门负责:

让“发送方的邮件服务器”把邮件可靠地传给“接收方的邮件服务器”。

注意:

  • SMTP 是服务器之间的协议,不是你和服务器之间写邮件的界面;
  • 它跑在 TCP 之上(因为要可靠传输);
  • 当一台邮件服务器给另一台邮件服务器发邮件时,它在协议上扮演 SMTP 客户端
  • 当一台邮件服务器从别人那里收邮件时,它扮演 SMTP 服务器端

所以同一台邮件服务器,既可以是 SMTP 客户,又可以是 SMTP 服务器,取决于它是“在发”还是“在收”。

比喻:SMTP 就像“邮局之间约好的寄送规矩和格式”:
信封上要写什么、怎么确认收到了、收不到怎么办等等。

Alice 给 Bob 发一封邮件:一步一步的流程

书里要讲的是:从 Alice 发出到 Bob 收到,中间都发生了啥?

场景设定:

  • Alice 在一个单位/学校,使用自己的用户代理写信;
  • Bob 在另一个单位,使用另一个邮件系统;
  • 两边都有各自的邮件服务器(图 2-14 里三个云状的“网络”)。

我们按时间线来:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


第 1 步:Alice 在用户代理上写好邮件 → 交给自己邮件服务器

  1. Alice 打开自己电脑上的用户代理(Outlook/QQ 邮箱等),写好邮件:
    • 收件人:Bob 的邮箱地址;
    • 内容、附件等。
  2. Alice 点击“发送”(send):
    • 用户代理并不是直接把邮件送到 Bob 的电脑;
    • 而是把邮件发送给 Alice 所在的邮件服务器,放进这台服务器的**外出报文队列(message queue)**中。

类比:Alice 在家里写好信,然后把信投进“本地邮局”的信筒。
还没出城,只是到了自己城市的邮局。


第 2 步:发送方邮件服务器再用 SMTP 把信“推”到 Bob 那边

接下来轮到邮件服务器之间出场:

  1. Alice 的邮件服务器看到队列里有一封要发往 Bob 所在域名(比如 bob@schoolB.edu)的邮件;
  2. 它会查一下:Bob 的邮件服务器在哪(通过 DNS 查 MX 记录,这部分书后面会讲,现在知道有这一步即可);
  3. 找到 Bob 的邮件服务器后:
    • Alice 的邮件服务器发起一个 TCP 连接 到 Bob 的邮件服务器的 SMTP 端口;
    • 在这条 TCP 连接上,用 SMTP 协议 把邮件内容完整“推”过去。
  4. Bob 的邮件服务器接收成功后,把这封信投递到 Bob 的邮箱(mailbox)

类比:Alice 所在城市的邮局 → 用邮政的专用线路 → 把信袋送到了 Bob 城市的邮局,并放进 Bob 的信箱里。

注意:SMTP 是“推送”(push)协议
发送方服务器主动把信“推”到对方服务器,不是对方来“拉”。


第 3 步:Bob 的用户代理从自己的邮件服务器取信

现在邮件已经在 Bob 的邮件服务器的邮箱里了,还没到 Bob 的电脑上。

  1. Bob 打开自己的用户代理(邮件客户端或 Web 邮箱);
  2. 它会连接到 Bob 自己的邮件服务器,认证自己的账号(用户名/密码);
  3. 然后把 Bob 邮箱里的邮件列表取回来,展示在屏幕上;
  4. 当 Bob 双击某封邮件,就把那封邮件的数据从服务器拉下来显示。

类比:Bob 走到自家楼下的信箱,拿出里面的信来看。

这一步通常用的是 POP3 或 IMAP 协议,不是 SMTP,书在后面小节会专门讲。
你现在只要知道:SMTP 负责“服务器之间传信”,POP3/IMAP 负责“用户去自己服务器取信” 就行。

如果对方邮件服务器挂了怎么办?(报文队列的作用)

书里还专门讲了一个异常情况:

“如果 Alice 的邮件服务器不能将邮件交付给 Bob 的服务器……”

可能原因:

  • Bob 的邮件服务器暂时宕机;
  • 网络短时中断;
  • 名称解析有问题等等。

这时不会直接把信丢掉,而是:

  1. Alice 的邮件服务器把这封信放到本地的 报文队列(message queue) 中;
  2. 服务器会 定时重试(比如每 30 分钟尝试一次):
    • 每次都再尝试建立 SMTP 连接,把信发给 Bob 的服务器;
  3. 如果过了几天还是一直失败:
    • 邮件服务器一般会“放弃投递”,删除这封信;
    • 同时自动给 Alice 发一封“投递失败通知邮件”(一般题目是 Delivery Failure、Mail Delivery Subsystem 之类),告诉她:“这封信几天都没送到,对方服务器有问题”。

比喻:邮局如果一直联系不到对方城市的邮局,就会把信退回给寄件人,并附上一张纸条:“无法投递”。

这就是图 2-14 下面那段话:“在一个报文队列中保持该报文并在以后尝试再次发送。通常每 30 分钟左右进行一次尝试;如果几天后仍不能成功,服务器就删除该报文并以电子邮件的形式通知发送方。”

SMTP 的“角色互换”再强调一下

书最后一段说:

当一个邮件服务器向其他邮件服务器发送邮件时,它就表现为 SMTP 的客户端;
当邮件服务器从其他邮件服务器上接收邮件时,它就表现为一个 SMTP 的服务器。

画个简单的对话你会更容易记住:

  • Alice 服务器 → 拿起电话说:“我要给 Bob 服务器发邮件”
    → 此时它是 SMTP 客户端
  • Bob 服务器 → 接电话听并接收邮件
    → 此时它是 SMTP 服务器端

反过来,如果将来 Bob 给 Alice 回复邮件:

  • Bob 服务器在“发邮件”这边变成 SMTP 客户端;
  • Alice 服务器在“收邮件”这边变成 SMTP 服务器端。

同一台机器,既可以是客户,也可以是服务器,取决于此刻是在发还是在收。

2.3.1 SMTP

SMTP 是什么?和 HTTP 有啥不一样?

书开头几句话在说:

  • SMTP(Simple Mail Transfer Protocol)
    是因特网 电子邮件的核心应用层协议
    👉 专门负责:发送方的邮件服务器 → 接收方的邮件服务器 之间传邮件。
  • 它的标准定义在 RFC 5321 里(不用背,就是“官方说明书”的名字)。
  • SMTP 比 HTTP 还“老”:
    • 最早的 SMTP 协议 RFC 是 1982 年的;
    • HTTP/1.0、1.1 都是后来才出现的。
      所以这是一个“资历很老”的协议。

1)SMTP 的“老毛病”:只能发 7 位 ASCII 文本

书里重点吐槽了一点:

SMTP 要求:邮件报文的“体部分”(body)必须是 7 位 ASCII 码文本

什么意思?

  • ASCII 码:最早的西文字符编码,7 位能表示 128 个字符:
    • A~Z、a~z、0~9、常见标点;
    • 没有中文,也没有真正的“二进制文件”概念。
  • SMTP 最初的设计:
    只考虑“纯文本邮件”,没想到以后大家会发:
    • 超大附件、图片、音频、视频……

所以:

  • 当年:网速慢、硬盘小,大家基本只发纯文字,这个限制还可以接受;
  • 今天:多媒体时代,如果还只允许 7 位 ASCII,就很麻烦——图片、视频本来是二进制数据,不能直接塞进去。

怎么办?
→ 后来引入了 MIME 等机制,把“二进制附件”先编码成一堆 ASCII 字符(比如 Base64),再通过 SMTP 传。
书这里先点一下这个坑,后面再细讲。

对比一下 HTTP:
HTTP 没这个限制,它可以直接把任意二进制数据(图片、视频)当消息体传输,不要求先转成 ASCII。

你先记住一句话:

SMTP 原生只会“发文本”,多媒体附件要先“转成文本(编码)”再发。

Alice 给 Bob 发一封邮件(6 步)

书里列了 6 个步骤,对应图 2-15,我给你翻译成白话:

步骤 1:Alice 在用户代理上写信

1)Alice 调用邮件用户代理,填上 Bob 的邮箱地址(如 bob@someschool.edu),写好报文,点击发送。

这一点你已经熟悉了:写信 + 点发送。

步骤 2:用户代理把信交给 Alice 的邮件服务器

2)Alice 的用户代理把报文发给 发送方邮件服务器,放入该服务器的 报文队列 中。

  • 这一步信已经离开 Alice 的电脑,到了“本地邮局”;
  • 报文队列(message queue)就是“准备发往外地的信袋队伍”。

步骤 3:发送方服务器上的 SMTP 客户端开始行动

3)运行在 Alice 邮件服务器上的 SMTP 客户程序,发现队列里有这封信,于是发起到 Bob 邮件服务器上 SMTP 服务的 TCP 连接

  • SMTP 的 客户端程序 跑在 发送方的邮件服务器 上;
  • 它会主动去连 接收方邮件服务器 的 25 号端口(SMTP 端口)。

步骤 4:通过这条 TCP 连接“推”送邮件

4)初始的 SMTP 握手完成后,SMTP 客户端通过该 TCP 连接将 Alice 的报文发送出去。

  • “握手”就像两台机器先互相打个招呼,确认双方都准备好了;
  • 然后就用 SMTP 协议把整封信一行一行地发过去。

步骤 5:Bob 的服务器接收,并放进 Bob 的邮箱

5)在 Bob 的邮件服务器上,SMTP 服务器端接收该报文,随后把报文存入 Bob 的 mailbox 中。

  • 到这一步,邮件已经到达 Bob 的邮箱(在服务器上)
  • 服务器只是帮 Bob“代收”起来,放到他那一格小信箱里。

步骤 6:Bob 想看的时候,从自己的服务器取信

6)当 Bob 方便的时候,他打开自己的用户代理,从邮箱里读这封信。

  • 这时候用的是 POP3/IMAP 之类协议(前面我讲过),不是 SMTP;
  • 把服务器里已经存好的信取到 Bob 的手机/电脑上显示。

所以 SMTP 的职责只到“把邮件丢到对方服务器的邮箱里”就完事了。
至于收件人什么时候来取信,是另一个协议的事。

SMTP 的几个“重要观察点”

书中间那段话总结了几个重点,我给你翻译一下:

1)SMTP 是端到端的:中间一般没有别的邮件服务器

“SMTP 一般不使用中间邮件服务器发送邮件”。

意思是:

  • 从“发送方邮件服务器”到“接收方邮件服务器”,是 直接连一条 TCP 连接
  • 中间不会再经过什么“第三方邮件服务器”转发;
  • 就算这两台服务器隔着半个地球(一个在香港,一个在圣路易斯),TCP 连接也是直接在它们之间建立。

当然,中间还是有很多路由器、链路,但那是网络层的事,对 SMTP 来说它就只看到“一对一地连另一个服务器”。

2)如果对方服务器暂时没开机或故障

书里说:

  • 如果 Bob 的邮件服务器没开机,Alice 服务器上的 SMTP 客户端连不上;
  • 那这封信不会跑去某个“中间服务器”暂存,而是:
    • 留在 Alice 服务器的报文队列里,等一段时间再重试;
    • 每隔一段时间(例如 30 分钟)重试一次;
    • 如果几天都不行,就退信通知 Alice。

这和我们前面讲“报文队列”的作用就是一个意思。

SMTP 会话:像人类聊天一样的“打招呼 +说正文 +再见”

书接着讲 SMTP 的一次完整对话过程,你可以把它想象成:

“客户端 C 和服务器 S 打电话聊天,电话线路就是 TCP 连接。”

1)先建 TCP 连接 + 自我介绍

客户端 SMTP(C):

  • 在本机上运行(比如主机名 crepes.fr),它要去连另一台服务器 hamburger.edu
  • 它在 25 端口建立 TCP 连接,如果服务器没开机会反复尝试。

连接好后,会发生类似以下对话(书上的例子):

S: 220 hamburger.edu
C: HELO crepes.fr
S: 250 hello crepes.fr, pleased to meet you

解释:

  • S: 220
    服务器 220 响应码,意思是“服务准备好了,欢迎连接”;
  • C: HELO crepes.fr
    客户端说“HELLO,我叫 crepes.fr”,做自我介绍;
  • S: 250 …
    250 表示上一条命令 OK,并附一段人类可读解释。

这就像两个人打电话:

S:喂,这里是 hamburger.edu 邮局,有什么事?
C:你好,我是 crepes.fr 邮局。
S:好的,crepes.fr,很高兴和你通话。

2)告诉对方“谁发信、谁收信”

接下来几条命令:

C: MAIL FROM: <alice@crepes.fr>
S: 250 alice@crepes.fr ... Sender ok
C: RCPT TO: <bob@hamburger.edu>
S: 250 bob@hamburger.edu ... Recipient ok

解释:

  • MAIL FROM:
    “这封邮件的发件人是 alice@crepes.fr”;
  • RCPT TO:
    “收件人是 bob@hamburger.edu”。

服务器通过 250 表示“我认可这个发件人/收件人”。

3)开始发正文(DATA)

C: DATA
S: 354 Enter mail, end with "." on a line by itself
C: Do you like ketchup?
C: How about pickles?
C: .
S: 250 Message accepted for delivery

解释:

  • C: DATA
    客户端说:“接下来我要发邮件正文了”;
  • S: 354 …
    服务器说:“好,你可以发了,最后用单独一行的点 . 结束。”

然后客户端就一行一行发送正文内容(此处就是那两句“Do you like ketchup? …”)。

关键点:

  • 正文结束的标志:一行只有一个点 .,前面有 CRLF,最后也有 CRLF。
    这就是书里说的 “以 CRLF.CRLF 结束”。

服务器收到点这一行后,就知道正文结束,于是返回:

  • 250 Message accepted for delivery
    “我已经成功接收这封邮件,会负责投递。”

4)结束会话(QUIT)

C: QUIT
S: 221 hamburger.edu closing connection
  • QUIT
    客户端说“我发完了,要挂断了”;
  • 221
    服务器说“好,连接要关闭了”,然后关闭 TCP 连接。

这整段对话就是“在同一条 TCP 连接上,发送 一封 邮件”的过程。

书上还提到一个点:

  • 如果客户端要在同一条连接上发送多封给 同一个服务器 的邮件,它可以:
    • 在前一封发送完成后,再发新的 MAIL FROMRCPT TODATA 指令;
    • 不用每一封都重新建一次 TCP 连接。

这样就更加高效。

Telnet 练习是干嘛的?

最后书说:

“我们强烈推荐你使用 Telnet 与一个 SMTP 服务器进行一次直接对话。”

命令类似:

telnet serverName 25
  • serverName:你本地邮件服务器的名字(或 IP);
  • 25:SMTP 使用的端口号。

这样做会发生什么?

  • 你相当于“假装成一个 SMTP 客户端”,直接连上邮件服务器;
  • 然后你可以手动敲那些命令:HELO、MAIL FROM、RCPT TO、DATA、QUIT;
  • 服务器会像上面例子那样,给你回 220、250、354、221 等响应码。

这个练习的目的是:

让你亲自体验“邮件服务器之间其实就是在互发一行一行的文本命令和文本正文”。

2.3.2 SMTP与HTTP的对比

SMTP 和 HTTP 有啥区别?

书说:这俩协议都是“一台主机给另一台主机传文件”:

  • HTTP:Web 服务器 → Web 客户(浏览器) 传“对象”(网页、图片……)
  • SMTP:发送方邮件服务器 → 接收方邮件服务器 传“邮件报文”

它们共同点:
都用 TCP 持续连接,在同一条 TCP 连接上传一大堆数据。

但关键是 三大不同点


1)第一个区别:谁来发起连接?“拉”还是“推”

书里用两个词:

  • HTTP 是 拉协议(pull protocol)
  • SMTP 是 推协议(push protocol)

HTTP:拉

场景:你想看网页。

  • 浏览器(客户端) 主动去连 Web 服务器:
    “我想要这个网页 /index.html,给我拉过来。”
  • TCP 连接由“想要文件的人”发起。

**理解:**你去食堂端菜——“我饿了,我自己去拿”。

SMTP:推

场景:Alice 的服务器要给 Bob 的服务器发邮件。

  • 发送方邮件服务器 主动去连接收方邮件服务器:
    “我这儿有一封信,要推过去给你。”
  • TCP 连接由“拥有文件的人”发起。

**理解:**快递员送快递——“我有你的包裹,我主动送到你家门口”。

记忆小句:

  • HTTP:我想要 → 我去找你 数据
  • SMTP:我有东西 → 我主动 给你

2)第二个区别:传输内容的限制(ASCII vs 任意数据)

你前面已经看到:SMTP 最原始的设计只支持 7 位 ASCII 文本
书里又把这个和 HTTP 做了对比:

SMTP 的限制

  • 每一封邮件报文(包括头和体)都要用 7 位 ASCII 编码
  • 如果你要发:
    • 带重音符号的法文字符
    • 二进制文件(图片、音频、视频等)
      都不能直接塞进去,必须 先进行编码 → 变成 ASCII 文本 再发(比如 Base64)。

就像快递公司只允许你寄“纸片”,你想寄 U 盘,就得先把 U 盘的数据打印成一堆文本再寄过去……

HTTP 的情况

  • HTTP 对“数据内容”没这个限制:
    • JPG、MP4、ZIP、PDF……随便直接扔进 HTTP 响应体里;
    • 不要求一定是 ASCII 文本。

所以 HTTP 原生就适合传多媒体,而 SMTP 要靠各种“打补丁”的方式(MIME 等)才能支持附件。


3)第三个区别:一个报文里怎么放“多个对象”

书里提到:

  • HTTP:
    • 每个对象单独做一个 HTTP 响应报文
    • 浏览器要一个 HTML,再要上面的图片,就会一条条发 HTTP 请求,服务器一条条回相应对象。
  • SMTP:
    • 把“这封邮件涉及到的东西”——文本 + 附件 + 其他媒体——都封装在同一个邮件报文里
    • 也就是:一个 SMTP 报文 = 一封邮件(里面可以包含多个“部分”)。

简单记:

  • HTTP:一个对象一个报文
  • SMTP:一个报文可以是“多部分”的整封邮件

2.3.3 邮件报文格式

这一小节不再讲 SMTP 的命令,而是讲:

一封电子邮件本身的结构 是怎样的?”

就像纸质信:

  • 上半部分:收件人地址、寄件人地址、日期……(信封/抬头)
  • 下半部分:正文内容

电子邮件也差不多,被分成两块:

  1. 首部行(header)
  2. 邮件体(body)

1)首部行:各种环境信息(写在前面)

书里说:当 Alice 写一封纸质长信小时,会在上部写:

  • Bob 的地址
  • 自己的回邮地址
  • 日期……

同理:电子邮件也需要这些“环境描述信息”,就放在报文体前面的一堆 首部行 里。

这些首部行的语法在 RFC 5322 里定义。

格式:

  • 每一行是:关键字: 值
  • 有的首部是必选,有的是可选。

典型的几条:

  • From: —— 发件人是谁(必须有)
  • To: —— 收件人是谁(必须有)
  • Subject: —— 主题标题(可选,但几乎人人都会写)

书里给了一个例子:

From: alice@crepes.fr
To: bob@hamburger.edu
Subject: Searching for the meaning of life.

看到这个你就会想到现实中的邮件头部:
发件人、收件人、主题就是这么来的。

注意:

  • 这些首部行是 邮件本身的一部分
  • 和刚才学的 SMTP 命令 MAIL FROMRCPT TO 不是同一层次的。
    • SMTP 命令是“服务器之间沟通用的控制语言”;
    • 邮件首部是“信封信息,发件人写给收件人看的”。

你可以这样理解:

  • 打电话时,你对话中的“我是谁、你是谁、我要说什么”是一回事(SMTP 命令);
  • 信封上写的“寄件人地址 / 收件人地址 / 主题”是一封信的一部分(邮件首部)。

2)空行 + 邮件体(正文)

书里说:

在报文首部之后,紧接着一个空白行,然后就是用 ASCII 文本表示的报文体。

也就是说一封邮件的文本结构像:

From: alice@crepes.fr
To: bob@hamburger.edu
Subject: Searching for the meaning of life.

Hi Bob,
Do you like ketchup?
How about pickles?
...
  • 上面那三行是 首部
  • 中间那一行 完全空白(只含 CRLF)是 分隔符
  • 下面开始的就是 正文内容(body)

在 SMTP 传输过程中:

  • 客户端在发送 DATA 命令后,会把 “首部 + 空行 + 正文” 整块发给服务器
  • 服务器照单全收,存进 mailbox。

3)再强调一次:首部行 vs SMTP 命令

书里提醒你一个容易混的点:

  • 2.3.1 那节,我们看到的命令有:
    • MAIL FROM:
    • RCPT TO:
    • DATA 等等
  • 这些是 SMTP 在“握手”阶段用的控制命令,属于 SMTP 协议的一部分;
  • 而本节说的 From:To:Subject: 等首部行,是 邮件报文本身里的一部分

关系大概是:

SMTP 命令帮两台服务器交流:“我要发一封这样的邮件给谁谁谁”;
邮件首部则是“这封邮件内容里附带的说明文字”。

两者都有 From/To 这种词,但层次不同,不要搞混。

2.3.4 邮件访问协议

为什么还要“邮件访问协议”?SMTP 不够吗?

1. 邮件到哪儿为止和 SMTP 有关?

前面已经讲过:

  • Alice 发信 → 她的邮件服务器用 SMTP 把报文推给 Bob 的邮件服务器;
  • Bob 的服务器把报文放进 Bob 的 mailbox(邮箱)

到这儿为止,SMTP 的任务就完成了:信已经“到达 Bob 的邮件服务器”。

接下来还有一件事:

Bob 怎么把“服务器上的邮件”拿到“自己的终端设备”上来看?

这个过程就不是 SMTP 负责了,而是“邮件访问协议”的工作。


2. 早期的办法:直接登录到邮件服务器主机上

书一开始回顾了一个“上古时代”的做法:

直到上世纪 90 年代早期,
Bob 通常是 “登录到邮件服务器那台主机”,
在那台主机上运行一个读取邮件的程序。

也就是说:

  • 服务器是一台大型机 / Unix 主机;
  • 所有人都通过 telnet / ssh 进这台机;
  • 在服务器上运行 mail 之类的命令行程序,看邮件。

特点:

  • 需要对方机器有登录账号;
  • 所有操作都在服务器那台主机上完成。

3. 今天的典型方式:客户端–服务器结构

现在更常见的是:

用户在自己的终端系统上运行一个“邮件客户端程序”(用户代理),
通过网络“访问远处的邮件服务器”。

  • 终端系统可以是:
    • 办公室的 PC
    • 家里的 PC
    • 笔记本电脑
    • 手机
  • 优点:
    • 有漂亮图形界面;
    • 支持多媒体邮件、附件、拖拽、搜索等丰富功能。

问题:
客户端怎么从“远处的邮件服务器”把 Bob 的邮件安全地拿下来?
→ 这就需要 邮件访问协议(POP3、IMAP、HTTP)。


4. 如果 Bob 自己在本地 PC 上也跑一个邮件服务器呢?

书中假设一种特殊情况:

Bob 在自己的 PC 上同时运行 用户代理 + 邮件服务器

那会怎样?

  • Alice 的邮件服务器完全可以直接通过 SMTP
    把邮件送到 Bob 这台 PC 上的邮件服务器
  • Bob 在这台 PC 上用本地用户代理读邮件。

这时似乎就 不需要 POP3 / IMAP 了,因为:

  • Bob 就在“自己的邮件服务器”那台机器上。

但书马上指出:这种方式在现实里很少用,原因:

  1. 让普通用户在自己 PC 上长期运行一个邮件服务器,很不现实:
    • 需要运维、配置、安全防护;
    • 家用电脑经常关机 / 断网,服务器不可用。
  2. 对运营商 / 学校 / 公司来说,更合理的做法是:
    • 在机房里运行一套共享的邮件服务器;
    • 所有用户共享这套服务器,方便统一维护。

于是现实世界里的模式是:

收件人的邮箱通常放在 ISP / 学校 / 公司 的邮件服务器上
Bob 的 PC / 手机只是客户端,要通过网络去访问那台服务器。

这就引出了下面的问题。


5. 邮件“传输路径”回顾 + 为什么需要 POP3 / IMAP

书中回顾完整路径(图 2-16):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • Alice 的用户代理 → 把邮件交给 Alice 的邮件服务器
  • Alice 的邮件服务器(SMTP 客户端) →
    用 SMTP 把邮件推送到 Bob 的邮件服务器
  • Bob 的邮件服务器 → 把报文放进 Bob 的邮箱(mailbox)。

难点:

像 Bob 这样的普通用户,
如何在自己的 PC / 手机上通过一个“用户代理”,
ISP 的邮件服务器 把邮件拿回来?

注意:

  • Bob 的用户代理 不能用 SMTP 来取邮件
    • SMTP 是 推协议,发送方推给接收方服务器;
    • Bob 的客户端是要“从服务器拉邮件”,这是另一类操作。
  • 为了解决这个问题,引入了专门的 邮件访问协议
    • POP3(Post Office Protocol v3)
    • IMAP(Internet Mail Access Protocol)
    • HTTP(Web 邮箱,其实是用 HTTP 来访问邮件服务器)

书后半句总结:

“图 2-16 总结了应用于因特网电子邮件的一些协议:
SMTP 用来把邮件从发送方的邮件服务器传输到接收方的邮件服务器;
SMTP 也用来把邮件从发送方用户代理传送到发送方邮件服务器;
像 POP3 这样的邮件访问协议则用来把邮件从接收方邮件服务器传送到接收方用户代理。”

看着图 2-16:

  • 左边:Alice 的用户代理 ↔(SMTP)↔ Alice 的邮件服务器
  • 中间:Alice 的邮件服务器 ↔(SMTP)↔ Bob 的邮件服务器
  • 右边:Bob 的邮件服务器 ↔(POP3 / IMAP / HTTP)↔ Bob 的用户代理
2.3.4.1 POP3

POP3 是一个“极为简单”的邮件访问协议,
标准是 RFC 1939,文档短、易读,所以实现简单但功能有限。

使用场景:

  • 当用户代理(客户端)和邮件服务器建立 TCP 连接(端口 110)后,POP3 开始工作;
  • 它分 三个阶段
    1)授权(authorization)
    2)事务处理(transaction)
    3)更新(update)

2. 三个阶段具体干什么?

阶段 1:授权(authorization)

目的:验证你是谁

流程:

  1. 客户端发用户名和密码命令,通常是:
    • user <username>
    • pass <password>
  2. 服务器根据这两个命令检查账号是否正确。

如果通过,就进入下一阶段;否则返回错误。

阶段 2:事务处理(transaction)

目的:操作邮箱里的邮件

在这个阶段,客户端可以做:

  • 取邮件(下载)
  • 标记删除、取消删除
  • 获取统计信息(比如总共多少封、多大)
  • 列出邮件编号和大小等

注意:
在事务处理阶段,邮件还 留在服务器上,只是在做标记和下载。

阶段 3:更新(update)

当客户端发出 quit 命令后进入这个阶段:

  • POP3 服务器会真正执行“删除那些被标记为删除的邮件”;
  • 然后关闭会话。

所以真正删除是在 会话结束时统一做


3. 服务器的响应形式:+OK / -ERR

在整个 POP3 会话中:

  • 客户端发出命令;
  • 服务器对每个命令都要给一个“回复行”,形式一般是:
    • +OK ... → 表示命令执行成功,后面可能跟数据;
    • -ERR ... → 表示有错误,说明命令出错原因。

4. 授权阶段命令示例(Telnet 手动练习)

书建议用 Telnet 连到 POP3 服务器 110 端口,模拟客户端。
例子:

telnet mailServer 110
+OK POP3 server ready
user bob
+OK
pass hungry
+OK user successfully logged on

解释:

  • telnet mailServer 110:和名为 mailServer 的机器的 110 端口建立 TCP 连接;
  • +OK POP3 server ready:服务器说“我准备好了”;
  • user bob:客户端说“用户名 bob”;
  • +OK:服务器说“这个用户名可以”;
  • pass hungry:客户端发密码;
  • +OK user successfully logged on:服务器说“用户验证成功”。

如果用户名/密码写错,服务器会回 -ERR


  1. 事务处理阶段:两种工作模式

POP3 客户端通常可以被配置为两种模式之一:

  1. 下载并删除(download-and-delete)
  2. 下载并保留(download-and-keep)

书后面会详细比较两种模式的优缺点,我们先看命令流程。


6. 事务处理阶段命令示例(下载并删除)

书给了一段对话(C = 客户端,S = 服务器):

C: list
S: 1 498
S: 2 912
S: .
C: retr 1
S: (blah blah ... 邮件1内容 ...)
S: ................. 
S: ..........blah)
S: .
C: dele 1
C: retr 2
S: (blah blah ... 邮件2内容 ...)
S: ................
S: ..........blah)
S: .
C: dele 2
C: quit
S: +OK POP3 server signing off

我们逐行翻译:

  1. C: list
    • 客户端请求“列出所有邮件的编号和大小”。
  2. 服务器回复:
    • S: 1 498 → 第 1 封邮件大小 498 字节
    • S: 2 912 → 第 2 封邮件大小 912 字节
    • S: . → 一个只有点 . 的行,表示列表结束(POP3 协议的惯用做法)
  3. C: retr 1
    • 客户端请求“下载第 1 封邮件”。
  4. 服务器发送第 1 封邮件的全部内容(多行):
    • S: (blah blah ...
    • S: ..........
    • S: ..........blah)
    • S: . → 用单独一个点表示这封信结束。
  5. C: dele 1
    • 客户端要求“将第 1 封邮件标记为删除”(还没真正删)。
  6. C: retr 2 + 服务器发送邮件 2 内容 + 结尾点。
  7. C: dele 2
    • 标记第 2 封邮件为删除。
  8. C: quit
    • 客户端结束会话;
    • 服务器进入“更新阶段”,真正删除 1、2 号邮件;
    • 回应 +OK POP3 server signing off

这就是“下载并删除”模式
每次拿完邮件就从服务器上删掉。


7. “下载并删除”的问题 & “下载并保留”的出现

书指出一个现实问题:

Bob 可能是“移动用户”,想在多台设备上访问自己的邮件:

  • 办公室的 PC
  • 家里的 PC
  • 手机 / 平板……

如果采用“下载并删除”模式:

  • 早上 Bob 在公司 PC 上收了一封信并删除;
  • 晚上他在家用手机收信时,这封信已经从服务器删掉了;
  • 手机就再也看不到这封邮件了。

不方便!

于是有了另一种模式:下载并保留

  • 客户端从服务器拉一封邮件,但不发 dele 命令;
  • 会话结束时服务器不删除邮件;
  • 这样邮件仍保留在服务器上,可以被不同机器反复访问。

书还提到:

POP3 服务器在两个会话之间并不持久保存太多“状态信息”(比如哪些邮件已读、哪些已删)。
它记录的主要是:哪些邮件被标记为删除等。
会话结束后,POP3 侧重的是“邮箱内容”本身,而不是“会话历史”。

换句话说,POP3 把服务器当成一个“只存邮件,不记你阅读状态”的存储仓库。

2.3.4.2 IMAP

IMAP:解决 POP3 的不足

书接下来讲的是 IMAP(Internet Mail Access Protocol)。

1. POP3 的另一个缺点:本地文件夹难以同步

假设使用 POP3(下载后删或保留),Bob 想:

  • 把某些邮件分类放进“工作”、“家人”、“学习”等文件夹;
  • 这些文件夹通常是 建立在本地 PC 上 的。

问题来了:

  • 如果 Bob 有多个终端(办公室 PC、家里 PC、笔记本),
    每台机器上都各自有一套本地文件夹;
  • 邮件在 A 机器上归档了,但在 B 机器上看不到这个结构;
  • 对移动用户特别不方便。

所以:

POP3 没有提供“在服务器端创建文件夹 / 在服务器端管理分类”的能力。
用户邮件组织方式基本都在本地完成,难以跨设备共享。


2. 为了解决这些问题 → 引入 IMAP

IMAP 的目标:

让“邮件的存储和组织”更多地留在 服务器端
客户端只是一个“远程窗口”,
这样你从哪台设备上看,看到的都是同一套文件夹和状态。

IMAP 的标准是 RFC 3501。

特点概览(相对于 POP3):

  • 功能更强大;
  • 客户端和服务器实现也更复杂(要维护更多状态)。

3. IMAP 的主要特性(一)——服务器端文件夹

IMAP 服务器会:

  • 把每封邮件和一个“文件夹(folder)”关联起来;
  • 最典型的默认文件夹叫 INBOX,存收件箱的邮件;

客户端通过 IMAP 可以:

  • 创建新的文件夹(比如 Work、Family、School);
  • 把邮件从一个文件夹移动到另一个;
  • 重命名 / 删除文件夹;
  • 读取邮件、删除邮件。

IMAP 定义了相应的命令用来:

  • 新建文件夹;
  • 把某封邮件从文件夹 A 移动到文件夹 B;
  • 在文件夹里查询匹配特定条件的邮件等。

关键区别:

  • 这些操作都发生在 服务器端
  • 所以不管你从哪里(哪台设备)连上来,都看到同一套文件夹和分类结果。

4. IMAP 的主要特性(二)——服务器维护“用户状态信息”

和 POP3 不同,IMAP 服务器会 在会话之间保留用户的一些状态,比如:

  • 每封邮件属于哪个文件夹;
  • 哪些邮件已经读过、哪些还未读;
  • 某些邮件是否加了标记(星标、重要等)。

这样:

  • 你在办公室把一封邮件标记为已读;
  • 晚上回家用手机开邮箱时,仍然看到它是已读状态;
  • 不会每台设备都“重新来一遍”。

5. IMAP 的主要特性(三)——可以只取“部分内容”

IMAP 另一个非常重要的特性:

允许客户端只获取邮件的某些部分,而不用一次性取回全部。

例如:

  • 只取邮件首部(From / To / Subject),暂不取正文;
  • 只取某个 MIME 多部分邮件中的某一部分(比如只看文本,不下大附件)。

这在什么场景非常有用?

  • 当前网络带宽很低(比如手机流量差、移动网络很慢);
  • 邮箱里有一些巨大的附件(几十 MB 的视频);
  • 你可能 只想先大致看一下标题和前几行内容,不想把所有附件都下载到本地。

IMAP 支持类似“按需加载”的访问方式,
减少带宽浪费,提升用户体验。

POP3 则更偏向于“整封下载”,不擅长做这种细粒度控制。

特性 POP3 IMAP
主要思想 把服务器当成“临时仓库”,邮件一般被下载到本地 把服务器当成“主存储 + 文件夹系统”,客户端是远程视图
文件夹 通常在本地建立文件夹分类 文件夹(INBOX、Work 等)在服务器上维护
多终端访问 难以同步分类和已读状态(如果用下载并删除更糟) 所有终端看到的是同一套文件夹和状态,非常适合多设备
状态保持 服务器只记哪些邮件被标记删除等,很“无状态” 服务器维护用户状态(已读、文件夹、标记等)
细粒度获取 一般整封下载 可以只取首部、只取邮件部分内容
实现复杂度 简单,命令少(user/pass/list/retr/dele/quit) 功能强,命令多,实现复杂

POP3:简单粗暴——“拉下来,爱咋弄咋弄”;
IMAP:服务器端为主——“所有设备共享同一个云端邮箱视图”。

2.3.4.3 基于Web的电子邮件

今天越来越多的用户使用他们的 Web 浏览器收发电子邮件。

这句话其实就是说:
很多人根本没装 Outlook、Foxmail 这些客户端,而是:

  • 打开浏览器(Chrome、Edge、手机浏览器),
  • 访问 mail.qq.commail.163.comgmail.com
  • 在网页里写邮件/看邮件,这就叫 基于 Web 的电子邮件(Webmail)

再往下:

20 世纪 90 年代中期 Hotmail 引入了基于 Web 的接入。
今天,谷歌、雅虎以及几乎所有重要的大学或者公司也提供了基于 Web 的电子邮件。

意思:

  • 很早以前(90 年代)Hotmail 是最早搞“网页邮箱”的之一;
  • 现在几乎所有大厂、学校、公司都会给你一个网页邮箱入口。

结论:
Webmail = 用浏览器当“邮件客户端(用户代理)”

  1. 使用 Webmail 时,谁是“用户代理”?协议是什么?

书里说:

使用这种服务,用户代理就是普通的浏览器,
用户和他远程邮箱之间的通信是通过 HTTP 进行的。

翻译成人话:

  • 以前:
    • 用户代理(User Agent)是 Outlook、手机邮件 App,这些专门的“邮件客户端”;
    • 它们和服务器之间用的是 POP3 / IMAP
  • 现在用 Webmail 时:
    • 用户代理 = 浏览器(比如 Chrome);
    • 浏览器与邮件服务器之间用的是 HTTP 协议(就跟访问普通网页一样)。

所以:

你在 QQ 邮箱网页上点“收取”、“写信”、“发送”,
本质上就是浏览器在发 HTTP 请求给 QQ 的邮件服务器。


3. Bob 用 Webmail“收信”时,协议怎么走?

书上说:

当一个收件人(如 Bob),想从他的邮箱中访问一个报文时,
该电子邮件报文从 Bob 的邮件服务器发送到他的浏览器,
使用的是 HTTP 而不是 POP3 或者 IMAP 协议。

把这句话拆开来:

  1. 场景:Bob 是收件人,他打开浏览器,登录自己的 Web 邮箱(比如 https://mail.xxx.com)。
  2. 他在网页上点“收件箱”,想看某一封邮件。
  3. 这时会发生:
    • 浏览器发一个 HTTP 请求给 Bob 的邮件服务器
      “把这封 ID=xxx 的邮件内容给我。”
    • 邮件服务器查到对应的报文,从服务器的 mailbox 中取出;
    • 通过 HTTP 响应 把邮件内容(按 HTML 渲染好的页面)发回给浏览器。
  4. 整个过程里面:
    • 浏览器 ↔ Bob 邮件服务器 用的是 HTTP
    • 完全没用到 POP3 / IMAP。

所以当你在网页邮箱里看信时:
“取邮件”的这段路是 HTTP,不是 POP3/IMAP。


4. Alice 用 Webmail“发信”时,协议怎么走?

书里接着说:

当发件人(如 Alice)要发送一封电子邮件报文时,
该电子邮件报文从 Alice 的浏览器发送到她的邮件服务器,使用的是 HTTP 而不是 SMTP。

这段讲的是 用户 → 自己邮件服务器 这部分:

  1. Alice 登陆自己的网页邮箱,在浏览器里写好一封邮件(收件人为 Bob)。
  2. 她点击“发送”按钮:
    • 浏览器通过 HTTP POST 把“这封邮件的内容(首部 + 正文)”打包成请求,
    • 发给 Alice 自己的邮件服务器。
  3. 这一步 用的是 HTTP,不是 SMTP,因为:
    • 浏览器(用户代理)就是一个普通 Web 客户端;
    • 它只会说 HTTP 这门“语言”。

但是! 邮件要真正到 Bob 那里,还需要跨邮件服务器传输——这就轮到 SMTP 出场了。


5. 邮件服务器之间还是谁说了算?——仍然是 SMTP

书上最后一句关键话:

然而,Alice 的邮件服务器在与其他的邮件服务器之间发送和接收邮件时,仍然使用的是 SMTP。

意思是:

  • 不管 Alice 是用 Outlook 还是用浏览器;
  • 一旦邮件到了 Alice 的邮件服务器上
    接下来 **“Alice 服务器 → Bob 服务器”**这一段 一律用 SMTP

完整路径,可以画成这样:

  1. Alice 在浏览器写信 → 浏览器用 HTTP 把信交给 Alice 的邮件服务器;
  2. Alice 的邮件服务器作为 SMTP 客户端 → 用 SMTP 把信推到 Bob 的邮件服务器;
  3. Bob 的邮件服务器把信放到 Bob 的邮箱(mailbox)里;
  4. Bob 在浏览器里打开 Web 邮箱 → 浏览器用 HTTP 从 Bob 的邮件服务器把信取回来显示。

所以 Webmail 只是把:

用户代理 ↔ 自己邮件服务器 这一段的协议”
从 POP3 / IMAP 换成了 HTTP

而:

邮件服务器 ↔ 邮件服务器 这一段”
依然老老实实用 SMTP,一点没变。


6. 和前面 POP3 / IMAP 的关系再帮你对一下

到目前为止,你可以把整套邮件系统脑子里这样分类:

  • 传输协议(服务器 ↔ 服务器、发件人 UA ↔ 发件人服务器):
    • 永远是 SMTP(推送)。
  • 访问协议(收件人 UA ↔ 收件人服务器): 下面三种选一类:
    1. POP3:传统的“下载到本地”;
    2. IMAP:服务器端文件夹 + 多终端同步;
    3. HTTP:浏览器访问 Webmail(从应用层看是 HTTP,底下服务器内部再去用 IMAP / 其它方式读 mailbox 这是内部实现,书里不管)。

你现实中看到的情况:

  • 打开 Outlook,配置 POP3/IMAP → 用的是 POP3/IMAP;
  • 打开浏览器 QQ 邮箱网页 → 用的是 HTTP;
  • 不管哪种方式,对方给你发信时,中间邮件服务器 ↔ 邮件服务器之间永远跑的是 SMTP
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐