TCP全称为"传输控制协议(Transmission Control Protocol)"
TCP协议格式
注意: TCP协议中如上的内容是在一行的,这里为了便于绘图,才将协议分为了多行!
源/目的端口号: 表示数据从哪里来,到哪里去;
32位序号/确认序号: 为实现可靠传输;
4位TCP报头长度: 表示这个TCP头部有多少个 32位比特位(有多少4字节),所以TCP头部最大长度是 15*4 = 60个字节;
6位标志位:
URG: 紧急指针是否有效;
ACK: 确认号是否有效,一般在三次握手以后 ACK 会一直被置为有效;
PSH: 提示接收端应立刻从TCP缓冲区中读取数据;
RST: 对方要求重新建立连接,;将携带 RST 标识的报文称为复位报文段;
SYN: 请求建立连接;将携带 SYN 标识的报文称为同步报文段;
FIN: 同志对端,本端要断开连接了;将携带 FIN 标识的报文称为结束报文段;
16位窗口大小: 为实现流量控制/滑动窗口;
16位校验和: 发送端填充,CRC校验,接收端校验不通过,则认为数据有问题.此处的检验和不光包含TCP首部,也包含TCP数据部分;
16位紧急指针: 标识哪部分数据是紧急数据;
40字节头部选项: 是可变长的可选信息;
连接管理机制
正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接;
三次握手
建立完毕后的数据交互

四次挥手
服务端状态转化
[CLOSED -> LISTEN] 服务器调用listen之后进入LISTEN状态,等待客户端连接;
[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段),就将该链接放入内核等待队列中,并向客户端发送SYN确认报文;
[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了;
[ESTABLISHED -> CLOSE_WAIT] 当客户端调用close主动关闭连接,服务器会收到结束报文段,服务器返回确认报文(这个确认ACK是由内核自主发送的,即只要收到对端的FIN,会直接发出ACK,与代码无关)并进入CLOSE_WAIT状态;
[CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT以后说明服务器准备关闭连接(需要处理完之前的数据),当服务器真正调用close关闭连接以后,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(即客户端收到FIN以后回应的ACK);如果一直保持在CLOSE_WAIT状态,那么证明代码编写出错,没有调用close函数;
[LAST_ACK -> CLOSED] 服务器收到了FIN的ACK,彻底断开连接。
客户端状态转化
[CLOSE -> SYN_SENT] 客户端调用connect,发送同步报文段;
[SYN_SENT -> ESTABLISHED] connect调用成功,进入ESTABLISHED状态,可以开始读写数据了;
[ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时,向服务器发送结束报文段,同时进入FIN_WAIT_1;
[FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文的确认,则进入FIN_WAIT_2状态,开始等待服务器的结束报文;
[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文,则进入TIME_WAIT状态,并发出LAST_ACK;
[TIME_WAIT -> CLOSE] 客户端要等待一个2MSL(Max Segment Life,报文最大生存时间)的时间才会进入CLOSE状态,彻底断开连接;
理解TIME_WAIT状态
为了避免最后一次的ACK应答丢包;
如果没有 TIME_WAIT 状态,ACK丢包以后:
这时候客户端认为自己断开了并且成功了,就不会再向服务器端发送数据;但是服务器端会认为客户端没有确认断开连接,那么服务器也会继续保持连接,消耗着服务器资源;
有 TIME_WAIT 状态,ACK丢包以后:
客户端在向服务器发送最后一个ACK以后就进入了 TIME_WAIT 状态,如果服务器端收到了 ACK,就会断开连接,客户端等待 2MSL 以后也会自动断开连接; 如果服务器没有收到 ACK, 就会再次向客户端发送 FIN,并且等待客户端的 ACK响应,这样保证了服务器端一定会彻底断开连接;
理解2MSL
MSL是TCP报⽂的最⼤⽣存时间, 因此TIME_WAIT持续存在2MSL的话,就能保证在两个传输⽅向上的尚未被接收或迟到的报⽂段都已经消失(否则服务器⽴刻重启, 可能会收到来⾃上⼀个进程的迟到的数据, 但是这种数据很可能是错误的);
同时也是在理论上保证最后⼀个报⽂可靠到达(假设最后⼀个ACK丢失, 那么服务器会再重发⼀个 FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK);