📚 第八章 应用协议
📄 一句话概括

邮件地址是 本地部分@域名,每一段都有长度上限。 254 这个数不是「地址的天性」,是从「命令行长度上限」倒推出来的。

📝 本节和前面几节的关系

8.4.1 讲了邮件怎么走,这一节讲「地址」这个最容易被忽略的细节。 这里会把真实的正则跑一遍,看它拦得住什么、拦不住什么。

一、四个长度上限

本地部分 64 字节、域名 255 字节、整个地址 254 字节、每一级标签 63 字节。 这四个数各有各的来历,不是拍脑袋定的。 64 这个数来自 SMTP 的路径语法: 本地部分允许写成带引号的字符串,规范里给这个字段留了 64 字节。 255 来自 DNS——一个域名整体不能超过 255 字节,因为 DNS 报文里的名字字段就是这个宽度。 63 是 DNS 单个标签的上限:它来自「标签长度用一个字节表示,高两位留作压缩指针」, 所以剩下六位,最大 63。 一个协议的长度限制,常常是「另一个协议的字段宽度」的遗产。

二、254 是怎么倒推出来的

SMTP 命令里地址要写成 <a@b> 的形式,两侧尖括号占 2 字节。 而规范给整条命令行的路径字段留了 256 字节。 256 减 2 就是 254。 所以这个数字是「从命令行长度倒推出来的」,不是「地址本身的天性」。 理解这一点有个实际好处: 当你看到某个系统的邮箱字段限制成 100 字符时,那是「数据库设计者的选择」,不是「协议要求」。 很多「莫名其妙」的长度限制,都是这么来的。 顺带说一句「大小写」:域名部分大小写不敏感,本地部分理论上敏感。 所以 [email protected] 和 [email protected] 是同一个域里的两个不同信箱(理论上)。 但几乎所有服务商都按不敏感处理——因为「用户记不住自己注册时是大写还是小写」。

三、正则拦得住什么,拦不住什么

下面用的正则是 ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$, 这是「够用」的那一档,不是「正确」的那一档。 它拦得住:没有本地部分、没有域名点、两个 at、本地部分带空格。 它拦不住:真正的 RFC 允许 "a b"@c.com 这种带引号的写法, 也允许 IP 字面量 a@[192.0.2.1]。 这两种写法在现实里几乎没人用,但它们是合法的。 所以现实中的做法是「先做宽松的格式检查,再发一封验证邮件」—— 因为格式合法不等于这个信箱存在。 这也是所有注册流程都要你「点邮件里的链接」的原因: 那是唯一能证明「这个地址真的能收信」的办法。

四、动手 Demo:邮件地址为什么不能随便写

先把四个长度上限摆出来,再用正则跑一批地址。 重点是看「哪些该拦的拦住了、哪些不该拦的也拦住了」。

import re

pat = re.compile(r'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')

cases = [
    ('[email protected]', '最短的合法地址'),
    ('[email protected]', '带加号和二级域'),
    ('a@b', '域名没有点'),
    ('a [email protected]', '本地部分有空格'),
    ('@c.com', '没有本地部分'),
    ('[email protected]', '域名以点开头'),
    ('a@@c.com', '两个 at'),
]
for addr, why in cases:
    print('%-38s %-6s %s' % (addr, '通过' if pat.match(addr) else '拒绝', why))

实跑输出:

--------------------------------------------------------------------
邮件地址为什么不能随便写
--------------------------------------------------------------------
**邮件地址的形状是 `本地部分@域名`**,**看着简单,但每一段都有长度上限,而且上限不是随便定的。**

  位置           上限         为什么是这个数
  ---------------------------------------------------------------
  本地部分       64 字节      **RFC 5321 的 quoted-string 上限**
  域名部分       255 字节     **受 DNS 名字总长限制**
  整个地址       254 字节     **路径尖括号内的上限 256 减 2**
  每一级标签     63 字节      **DNS 单个标签的硬限制**

**254 这个数值得推一遍**:**SMTP 命令里地址要写成 `<a@b>` 的形式,两侧尖括号占 2 字节。**
**而整条命令行的路径字段上限是 256 字节。**
**256 减 2 就是 254。**
**所以这个数字是「从命令行长度倒推出来的」,不是「地址本身的天性」。**

  地址                                    正则判定     说明
  --------------------------------------------------------------------
  `[email protected]`                               **通过**     最短的合法地址
  `[email protected]`     **通过**     带加号和二级域
  `a@b`                                   **拒绝**     域名没有点
  `a [email protected]`                             **拒绝**     本地部分有空格
  `@c.com`                                **拒绝**     没有本地部分
  `[email protected]`                                **拒绝**     域名以点开头
  `a@@c.com`                              **拒绝**     两个 at

**这个正则只是「够用」,不是「正确」**:**真正的 RFC 允许 `"a b"@c.com` 这种带引号的写法**,
**也允许 IP 字面量 `a@[192.0.2.1]`。**
**现实中的做法是「先做宽松的格式检查,再发一封验证邮件」**——**因为格式合法不等于这个信箱存在。**

**还有一条容易忽略**:**域名部分大小写不敏感,本地部分理论上敏感。**
**所以 `[email protected]` 和 `[email protected]` 是同一个域里的两个不同信箱**(理论上)。
**但几乎所有邮件服务商都按不敏感处理**——**因为「用户记不住自己注册时是大写还是小写」。**

两条通过、五条拒绝。 被拒的五条里,有四条是「明显不对」,一条是「域名没有点」。 a@b 这一条值得单独说:在 DNS 里 b 是一个合法的顶级域吗?严格说不是(需要有那个 TLD), 但正则拦它是因为「没有点」,不是因为「TLD 不存在」。 这意味着「本地内部域名」的邮箱地址会被这个正则误拦—— 比如公司内网用 zhangsan@intranet 这种写法。 所以「正则越严格越好」是个错觉:太严格会拦掉合法地址,太宽松会放进垃圾。 真正的验证只有一条:发一封信过去,看它能不能收到。

30 秒实验

  1. 算一下:< 和 > 各占一字节,256 减 2 是多少?
  2. 想一下:为什么 DNS 单个标签最多 63 字节?
  3. 想一下:邮箱大小写不敏感,是协议要求还是实现选择?

常见误区

  1. 以为「254 是地址本身的天性」。 它是从命令行长度倒推的,换个提交协议这个数就变了。
  2. 以为「正则写得越严越安全」。 太严会拦掉合法地址,验证只能靠发信。
  3. 以为「邮件地址大小写敏感」。 域名部分不敏感,本地部分理论上敏感但实际都按不敏感处理。
自测三问(点开对答案) 1. 邮件地址的四段长度上限是多少? —— **本地部分 64、域名 255、整体 254、每级标签 63。** 2. 254 是怎么来的? —— **路径字段上限 256,减去两侧尖括号 2 字节。** 3. 怎么真正验证一个邮箱存在? —— **发一封验证邮件**,看对方能不能点开链接。

下一节看 MIME 怎么让邮件装下中文和附件。

相关笔记