计算机网络 4:传输层
本层……
提供逻辑上「端到端」的连接,中间路径不管。
采用「端口」进行多路复用,不同进程(服务)工作在不同的端口上。
二元组 {目的 IP,目的端口号}(UDP)或四元组 {源 IP,源端口号,目的 IP,目的端口号}(TCP)确定一个连接。
UDP
一种无连接、尽力而为的传输层协议。UDP 头部如下:
字节 | 2 | 2 | 2 | 2 | ? |
---|---|---|---|---|---|
结构 | 源端口号 | 目的端口号 | 包含头部的长度 | 校验和 | 数据 |
其中校验和计算时,还需要在 UDP 段前方加上「伪头部」:
字节 | 4 | 4 | 1 | 1 | 2 |
---|---|---|---|---|---|
结构 | 源 IP 地址 | 目的 IP 地址 | 全零 | 协议号(从 IP 头部取出) | UDP 包含头部的长度 |
TCP
一种可靠的、点对点的、全双工的、面向连接的传输层协议。TCP 头部如下:
(Wikipedia,本站没有实现复杂表格排版所以画不出来这种表格)
TCP 使用滑动窗口的确认机制实现可靠传输。
序列号(SEQ):指 TCP 段中第一个字节的编号(不是第几个段)。
确认号(ACK):指希望接收到的下一个段,第一个字节的编号——减 1 即本机已经收到的字节编号。
与 UDP 一样,校验和的计算需要加上「伪头部」。
连接建立
采用「3 次握手」方式建立连接。连接只能由客户端主动建立。
方向 | 标志位 | SEQ | ACK | 作用 | |
---|---|---|---|---|---|
第 1 次握手 | C \(\to\) S | SYN | 随机数 \(x\) | 0 | 请求发起连接 |
第 2 次握手 | S \(\to\) C | SYN, ACK | 随机数 \(y\) | \(x+1\) | 对第 1 次握手进行确认,允许连接 |
第 3 次握手 | C \(\to\) S | ACK | \(x+1\) | \(y+1\) | 对第 2 次握手进行确认,连接建立 |
消息交互
通过三次握手建立连接后,双方可以进行全双工的通信。在上面的连接建立后,第一条消息的 SEQ、ACK 值为:
SEQ | ACK | 说明 | |
---|---|---|---|
C \(\to\) S | \(x+1\) | \(y+1\) | 与它发送的第三次握手一致,因为服务器不会回复它的第三次握手,中间没有产生过新的消息,所以序号与上一次消息是一样的。 |
S \(\to\) C | \(y+1\) | \(x+1\) | ACK 是 \(x+1\),表示希望客户端下一次发送的 SEQ 是 \(x+1\)(因为第三次握手的 ACK 段 的长度为零) |
TCP 的序号是基于字节的:
本次的 SEQ = 上次本机发送的段的 SEQ + 上次本机发送的段的长度
单纯的 ACK 包,长度为零;
FIN / SYN 包,长度为 1。
本次的 ACK = 希望下次收到对方的段的 SEQ。
重传
针对每个报文段设置一个计时器,当发送后过了一段时间还没有收到 ACK 即认为丢包,进行重传。超时时间是通过测量 RTT 进行加权得到的。
如果发送方连续收到同一段的三次 ACK,认为此段的下一段丢包,直接重传(快速重传)。
流量控制
接收方通过段首的窗口大小字段 rwnd
来告诉发送方自己最大接收能力。发送方「已经发送但没有收到 ACK」的数据大小不超过 rwnd
。
拥塞控制
发送方需要知道 rwnd
即接收方的窗口大小和 cwnd
即当前网络的拥塞程度,并取小者作为自己的发送窗口大小。
慢开始算法:TCP 刚建立时,
cwnd
设为 1。此后每完成一次发送 1 个 MSS,就加上 1。直到cwnd
达到ssthresh
。如果每次发送都用尽发送窗口(即「发送方有足够多的数据发送」),那么直观上的表现就是:每经过 1 个 RTT,
cwnd
就会翻倍。拥塞避免算法:
cwnd
达到ssthresh
后,每经过 1 个 RTT 再将cwnd
加上 1,而不是每收到 1 个 MSS 的确认就加 1。拥塞发生:当出现丢包时,将
ssthresh
设为此时cwnd
的一半,然后cwnd
重新置为 1。
连接断开
采用「4 次挥手」方式断开连接。双方都可以主动断开连接。