TCP/IP 协议中的一些要点
引子
项目遇到一个需求,抽象出来就是需要 app 通过 socket 与另一个设备(非 Android)之间传输数据。由于数据传输是建立在裸的 socket 之上,因此双方传输数据需要私下拟定一个传输协议。我们参考 TCP 协议中的丢包重传机制,拟定了一个私有协议。下面主要解释下 TCP 协议中确认传机制和超时机制的要点。
TCP 协议
数据报格式
TCP 数据报格式源端口、目的端口:标识端口号
数据序号:32位。表明发送数据报的顺序。一个字节对应一个序号。该序号表示该数据报中的数据字段的第一个字节对应的序号。用于记录发送方已发数据。
确认序号:期望收到的下一个数据报的序号。注意,这个确认号与上面的序号没有关系。因为TCP是全双工的协议。这个确认号是由发送方根据本地已接收的数据,计算出来的。用于记录发送方已收数据。
偏移:即报文头长度。之所以有这个字段是因为数据报中有可选选项这个不定长的数据。该字段以 32 比特为单位计数。没有可选选项时,该字段为 5。
标志位
- ACK:该位为1则表明确认序号生效,0则表明无效。
- PSH:带该标志位则表示接收方收到数据报便可送往应用程序,不必等缓冲区满才送。
- RST:复位标志。用于由于崩溃或者其他原因而出现的错误连接。还可以用于拒绝非法的数据报或拒绝连接请求。
- SYN:仅用于三次握手建立连接。
- FIN:用于四次挥手释放链接。使通信双方释放连接资源。
窗口字段:16位长。用于告知对方当前的接收窗口大小。
校验和:校验 TCP 数据报首部、数据和伪 TCP 首部。伪首部包括了 32 位源 IP 地址,32 位目的 IP 地址,8 位填充 0,8 位协议,16 位 TCP 数据报长度。
紧急指针:URG为1时有意义
可选项:长度可变、最长40字节。比较重要的可选项:
- MMS:最大报文长度
- SACK:选择确认
- 时间戳:计算往返时间;
由上可见,TCP 数据报最小就只有头,一共 20 字节。
确认机制
- 采用累计确认机制:接收方对收到的数据报进行缓存,并对每一个收到的数据报都回复ACK。
- 如果缓存中没有丢包空洞,那么回复ACK中确认字段就是期望收到的下一个数据报序号
- 如果缓存中有丢包空洞,那么回复ACK中确认字段就是第一段空洞的第一字节序号。并且现在普遍实现都会带上 SACK 选项:数据字段就会有丢包区间 [startSeq_1, endSeq_1], [startSeq_2, endSeq_2]...
- Delay ACK:延时确认:https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment
延时确认机制允许接收方延迟 500ms 再对多个收到的数据报进行确认。但是如果一个数据报中携带的数据达到了 MSS,那么接收方需要立即回复 ACK 进行确认。
重发超时如何确定?
超时重传
server 端通过计算每个数据报的ACK时间来动态计算 RTT,从而动态计算出超时时间 RTO
快速重传
如果server端连续收到3个相同ACK数据报,那么就立刻重传,不用等RTO超时
由上可见,采用的是动态计算超时时间 + 快速重传
每个数据报长度
每个数据报都不一样,但是尽可能以MSS的长度进行发送。理想情况是, MSS正好是IP中不会被分片处理的最大数据长度。
流控制
协议中带有窗口大小字段,由接收端通知发送端它的接收窗口大小,以避免发送端发送过多的数据。
接收端窗口满时,窗口大小字段为 0