TCP协议(一)
TCP协议
TCP
是传输层中的协议。
包头格式

特点
- 面向连接。客户端与服务端都需要建立一定数据结构来维护双方的状态
- 提供可靠交付。无差错、按顺序、不重复、不丢失,尽最大努力传送
- 面向字节流。传送的数据并不是完整有头有尾的内容,而是按字节计算的
- 可以流量控制。发送方根据接收方的接收能力调整发送速度
- 可以拥塞可控制。根据网络情况调整发送的速度
三次握手
TCP
的连接与包头中的信号位、序号、确认序号有关。以下是一次 TCP
三次握手连接的客户端与服务器的状态时序图。

- 发送一个
SYN
被置为1
,ACK
为0
的请求则视为连接请求 - 如果对方同意连接,则在应答报文中将
SYN
和ACK
都置为1
- 请求方收到应答报文后,会再发送一个应答报文给对方
TCP
规定在连接建立后的所有传输报文都必须把ACK
置1
经历上述三次握手后,才算是个完整的 TCP 连接。下图是使用 wireshark 抓取的 TCP 三次握手数据包。

为什么是三次握手
可靠的连接就是双方都可到达对方。为什么需要三次,而不是两次?按理说,两个人说话,你一句我一句这对话算是对上了?为了可靠,那为什么不是四次?
两次握手
A 发送请求给 B,B 发送应答给 A,就完成了两次握手,双方建立连接。
会出现的问题:
- A 发送请求给 B,B 发送应答给 A,然后 A 挂了,B 懵了
- A 发送请求给 B,因为网络不好,A 没收到 B 的应答,继续尝试发送请求包。终于收到应答包,两人愉快通信,然后关闭连接。这时,A 上次尝试发送的请求包绕路回来了,B 一看,美滋滋应答了,但是 A 并没打开连接,所以 B 就会懵了
所以两次握手不可靠。
四次握手
第三次握手是为了解决 B 是否真的可以建立连接状态,需要有 A 的回应。那有小朋友就问了,那第三次没握上怎么办?
其实对于 A 来说,在第二次握手后即收到 B 的应答后,就可以进入连接状态了,因为对于 A 发送的消息是有回应的。即使第三次握手的数据包丢失了,B 没有收到 A 发送的应答,但是在大部分情况下 A 在进入连接状态后会马上发送数据,到达 B 后,B 就可以认为这个连接已经建立。
消息有来有回
还有情况是,A 连接后不发送数据,开启 keepalive 机制,但是即使没有真实的数据,也有探活包。对于 B,可以主动关闭不发包的客户端。
包序号的问题
三次握手除了建立连接,还有沟通包的起始序号的作用。下图为客户端发送 TCP 数据包的 wireshark 抓包截图。

- 每次连接的起始序号是随时间变化的计数器,每4微秒加一。为了避免遇到上一次连接的包,而错误接收
- 第二个包的序号为起始序号加一。
- 其他包的序号为上一个序号加 TCP payload 的字节长度。

四次挥手
简单来说,TCP
的报文传输是需要有回应的,A 发送带关闭位 FIN
请求给 B 后,B 回应 A,就算两次挥手。因为 B 是被通知的一方,总是有可能会有些事情没有处理完而不能关闭,所以 B 可以关闭时,发送关闭请求 FIN
给 A,而 A 也需要回应,总共就四次挥手。

当进入 FIN_WAIT
状态后,如果对方没有响应,在 Linux 中可以设置 tcp_fin_timeout 这个参数确定超时时间。TIME_WAIT
是为了等待对方没有收到回应而再发送 FIN
时可以做出回应,还有确保下个使用该端口的应用不会收到还在路上未到的包,简单来说就是等待包都超过最大生存时间(MSL)。
我断开你知道,你断开我知道,你知道『我知道』