TCP 随笔
本文章记录TCP的一些杂七杂八的知识点,比较零碎。
ICMP 可用于发现链路上的最小 MTU
一条 sock 连接有五元组(Proto, SourceIp, DestIp, SourcePort, DestPort) 组成,任意一个改变都是可以是一个新的连接。
比如两台机器都有 2个IP,2个端口,用于TCP连接。那么他们之间可以创建 (2222)2 = 32 个 TCP 连接。传输层协议除了TCP 还有 UDP/SCTP/DCTP
TCP中的序号 是字节流编号,而不是报文的序号。比如第一个报文序号是0,然后该报文有300个字节的数据,那么第二个报文的序号就该是300
确认号表示期望收到的下一个报文的第一个字节编号是多少,比如面收到第一个报文之后,确认号就回设置为300,表示接下来希望收到第一个字节编号为300的报文
TCP的第一个序号通常假设为0, 但实际上可以随机的选择初始序号
如果发送端收到同一个序号的重复确认3次(冗余ACK)即可认为序号之后的报文已经丢失,可以进行快速重传
TCP 接受方重传的时候可以跳过那些已被选择确认的报文,需要接收方支持选择确认。(SACK,在TCP首部的 options 字段里)
TCP 接受方会维护一个 LastByteRead, 用户层已读取的最后一个字节的编号; LastByteRecv, 放入到缓存区的最后一个字节的编号。还有一个缓存区大小RecvBuffer。滑动窗口大小,就是缓存区大小(Buffer)减去已缓存的大小(Recv-Read)
TCP 发送方会维护一个 LatByteSent, 已发送的序号; LastByteAcked, 已被确认的序号。Sent-Acked 需要小于滑动窗口的大小。来保证接口方的缓存区不会被溢出。
如果接收方缓存区满了之后,窗口会设置为0;之后发送方会发送只有一个字节的报文段,用来 “轮询” 窗口更新。
TCP第三次握手确认的时候可以携带一些数据。
现在的主流操作系统都支持syn-cookie,在第三次握手之前,服务器并不维护客户端的信息, 可以有效的防御syn-flood攻击。
MSS 最大报文段长度,避免物理层分片,通常比MTU小一点。(在options里协商)
RTT 连接往返时间,即发出后到收到ACK的时间。
拥塞控制
- 慢启动,刚开始以一个MSS的值传输,后面2个,4个,成指数增长。直到发生拥塞,把此时窗口值的1/2,叫做慢启动阈值。
- 发生拥塞的时候,重新从1个MSS开始增加,直到增加到慢启动阈值, 之后不再指数增加,而是一个MSS一个MSS的增加。这个过程叫拥塞避免。
- 快速恢复,收到3次冗余ACK的时候,窗口不再从1个MSS开始,而是从慢启动阈值+3开始,然后开始重传。
TCP 字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
190 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+在RFC 3168 中 TCP 的 reserved 位置又使用了两位(CWE, ECE 用于处理拥塞控制和显式拥塞通知)
1
2
3
4
5
6
70 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |C|E|U|A|P|R|S|F| |
| Offset|Reser. |W|C|R|C|S|S|Y|I| Window |
| | |R|E|G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+TCP与网络编程
- 首先是服务端 bind,listen 初始化接受队列
- 客户端 connect 选择本地端口,发起 syn 握手请求,同时启动重传定时器
- 服务端 回应 syn ack, 将连接加入到半连接队列,启动重传定时器
- 客户端收到 ack,清除定时器,设置为已连接,发送ack
- 创建 sock 从半连接队列中取出放到全连接队列。
- accept 从全连接队列中取出 socket