传输层概述
传输层的核心协议是 TCP 和 UDP。
端口号
端口号使用整数表示,用来区分同一台主机上不同的应用程序。在网络编程中每个程序创建 socket 时都需要关联端口号。对于服务器来说,端口号由程序员手动指定;对于客户端来说,端口号由系统自动分配。
端口号由两个字节表示的无符号整数组成:
- 范围:0~65535。
并非所有数字都能使用:
- 0~1023 通常不使用,称为知名端口号,留给知名服务器。
- 80:HTTP 服务器预留端口。
- 443:HTTPS 服务器预留端口。
问题 1:一个进程是否可以绑定多个端口号? 答:完全可以。实际上不是进程绑定端口号,而是 socket 绑定端口。一个进程中可创建多个 socket,因此可同时关联多个端口号。开发服务器程序时,一般会提供至少两个端口:
- 业务端口:供用户客户端访问。
- 调试端口/管理端口:不对外公开,用于程序内部使用。
问题 2:一个端口号是否可以被多个进程绑定? 答:如果是同一种协议则不行,如果是不同协议是可以的。
UDP 协议
学习协议的重点在于其格式,通过格式可以推导特性。

整个 UDP 报头是 8 个字节,分成四个部分,每个部分占两个字节。
UDP 报头属性含义:
- 源端口、目的端口:表示数据是从哪个进程来,到哪个进程去。
- 长度:通过 2 个字节描述整个 UDP 数据报的长度(包括报头和载荷)。报头固定 8 个字节,所以整个长度减去 8 个字节的长度即为载荷长度。2 个字节最大表示 65535,即 64KB,因此一个 UDP 数据包最大也就是 64KB。
- 校验和:验证传输的数据是否出错。网络传输中电信号或光信号受外界干扰可能导致比特流错误。UDP 校验和起到发现错误的作用,即知道数据错误了,但不知道哪个位错误了。
方案对比:
- 发现错误:UDP 采用此方案。发送方计算校验和并随数据发送,接收方收到后再次计算并对比。若结果不同,则数据错误。
- 纠正错误:如海明码,知道数据错了也能定位具体位错误。
如果数据超过 64KB 怎么办?解决思路有两个:
- 手动实现拆包组包逻辑,但拆分组合复杂。
- 使用 TCP 代替 UDP。
计算校验和的方案有多种:
- CRC 循环冗余校验:简单快捷,但易出现碰撞。
- MD5/SHA 系列:定长、分散性强、不可逆,常用于加密领域。
UDP 主要应用于对效率要求高、丢包概率低的场景,如同机房内部机器通信。
TCP 协议
TCP 报文格式如下:


报文属性
- 16 位源端口和 16 位目的端口:表示数据来源和去向。
- 4 位首部长度:描述 TCP 报头长度。TCP 报头可变,包含可选的选项(Option)字段。4 位二进制最大为 15,以 4 字节为单位,因此报头最大为 4×15=60 字节。固定部分 20 字节,选项部分最多占 40 字节,且添加选项时需以 4 的倍数增加。
- 保留位:预留未来扩展,吸取 UDP 长度不可扩展的教训。
- 6 个标志位:TCP 最重要的控制位。

- 16 位校验和:检验数据是否出错,包含 TCP 首部和数据部分,发送端填充 CRC 校验,接收端校验不通过则认为数据有问题。
其余属性如序号、确认序号、窗口大小等将在后续机制讲解中结合说明。
可靠性
TCP 最核心的机制是可靠性,主要针对网络中的丢包情况。网络路径经过多个路由器或交换机,设备转发能力有上限,流量暴增会导致丢包。丢包通常是路由器的主动行为。
丢包处理对比:
- UDP:发送后不管,不可靠传输。
- TCP:关心对方是否收到,未收到则补救,可靠传输。
对抗丢包需做到:感知丢包、做出补救。
确认应答
确认应答是实现可靠传输的最核心机制。
如何感知对方收到消息?需要对方回复应答报文(ACK)。发送方收到 ACK 即可确定对方已收到消息。这对应标志位中的第二位:ACK。

示例模型: 我:女神,请你吃火锅吧。 女神:好啊!(应答报文)

问题:后发先至 若发送多条消息,可能因网络路径不同导致接收顺序错乱。例如先发的消息后到,导致理解错误。

解决方案:编号 给数据编号,根据编号完成顺序应答。真实 TCP 使用字节流,针对每个字节进行连续递增编号。

编号记录在 TCP 报头的 32 位序号字段中,填写的是当前数据包载荷中第一个字节的编号。
确认序号 接收方返回确认序号(ACK),填写在 TCP 报头的 32 位确认序号位置。

填写规则:收到的数据载荷最后一个字节序号加 1。例如收到 144 号数据,返回确认序号 45,告知发送方 144 已收,下一个从 45 开始。

注意:
- 序号和确认序号针对载荷,报头不参与编号。
- 独立编号:发送方和接收方各自编自己的号,互不影响。
- 应答(Acknowledge)与响应(Response)不同:应答是确认收到,响应是业务数据。
超时重传
超时重传是针对确认应答的重要补充,解决确认应答丢失的情况。
识别丢包:等待超时时间。若发送方未收到 ACK,认为丢包。

丢包情况:
- 数据丢了:主机 B 未收到数据,主机 A 重传。
- ACK 丢了:主机 B 已收到数据,主机 A 重传数据,主机 B 收到重复数据。

去重操作:TCP 接收方根据序号去重。操作系统内核中有接收缓冲区(类似队列),新数据放入前检查序号是否存在,存在则丢弃,不存在则放入。保证应用层读取数据不重复且有序。

超时时间设定:动态变化。网络通畅时间短,拥堵时间长。每次重传扩大超时等待时间(t0 < t1 < t2...)。达到阈值仍无 ACK 则视为严重故障,释放连接。

连接管理
连接管理辅助实现可靠传输,包括建立连接和断开连接。
三次握手
建立连接的过程称为三次握手。握手仅携带报头,不携带载荷。

- 客户端发起 SYN:客户端发送同步报文段,SYN 标记置 1。
- 服务器响应 SYN+ACK:服务器收到 SYN 后,回复 ACK 确认,同时发送自己的 SYN。
- 客户端确认:客户端收到服务器的 SYN+ACK 后,再回复 ACK。
至此双方保存了对端信息,连接建立。
合并中间两次:标准三次握手中,服务器返回 ACK 和发送 SYN 通常合并为一个包,以提高封装分用效率。
三次握手意义:
- 投石问路:验证网络通信路径畅通。
- 验证收发能力:确保双方麦克风和耳机(发送和接收能力)正常。
- 参数协商:协商初始序号等关键参数,防止'前朝剑斩本朝官'(旧连接数据干扰新连接)。
状态变化:
- closed:虚拟状态,无连接。
- listen:服务器准备就绪。
- syn_send/syn_rcvd:握手过程中短暂状态。
- established:连接建立成功。

四次挥手
断开连接使用四次挥手,同样不携带载荷。

- 客户端发送 FIN:请求断开连接。
- 服务器回复 ACK:确认收到断开请求。
- 服务器发送 FIN:服务器也准备断开。
- 客户端回复 ACK:确认收到,连接关闭。
为何不能合并:服务器返回 ACK 是操作系统自动立即返回,而发送 FIN 需等待应用程序调用 close 方法,两者时机不同。
状态变化:
- fin_wait_1/fin_wait_2:快速消失。
- close_wait:等待应用程序调用 close。
- time_wait:主动断开方进入,等待 2MSL 时间后释放连接,防止最后一个 ACK 丢失导致服务器重传 FIN 时无响应。

time_wait 等待原因:确保最后一个 ACK 不会丢失。若直接释放,ACK 丢失后服务器重传 FIN 将无法响应。等待 2MSL(最大报文生存时间)确保网络中无残留报文。


