基本概念
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议。是互联网协议族中最核心的协议之一。
TCP报文段格式

源端口与目的端口:各占16位,标识本地应用进程和对方应用进程,与进程所在主机的IP(32位)一起构成该应用进程在因特网上的48位唯一标识。
序号(也叫顺序号,seq):占32位,用来标记数据部分第一个字节的在原始字节流中的位置(注意:起始序号是通信双方在建立连接时自己设置并双方确定的一个序号,并不一定从0开始)。
确认号(ack或ack_seq):占32位,用于反馈给对方,表示在该确认号之前的所有字节已经被正确接收,下一次期望收到从该序号开始的字节数据。
数据偏移(即TCP头部长度):以4B为单位,占4位(,可表示范围为0~15,头部固定部分最小占20B,最大占15*4=60B,因此头部大小为20B~60B),标识头部大小。
保留:占6位,该字段暂时保留未用,常全置为0。
URG(紧急位):占1位,表示是否携带紧急数据。
ACK(确认位):占1位,用于指示确认号ack是否有效。
PSH(推送位):占1位,置为1表示希望接收方收到该报文要尽快递交给上层应用进程。
SYN(同步位):占1位,置为1时表示是一个连接请求或连接接收报文。
RST(复位位):占1位,置为1时表示该TCP连接中存在错误,需要拆除该连接重新建立。
FIN(终止位):占1位,置为1时表示发送该报文方数据已经发送完,断开该方向的连接。
窗口(即接收窗口大小,rwnd或rcvwnd):占16位,用于向接收方说明在本报文段中的确认号后还允许发送多少字节(用来限制发送方的发送速率,实现流量控制)。
检验和:占16位,校验原数据片和伪头之和(计算与UDP一样,先不在此处说明)。
紧急指针:占16位,用于标识字节流中紧急数据的结束位置(最后一个紧急字节的下一位),紧急数据的起始位置就是当前报文的起始序号。
可选字段:用于一些可选项,比如协商双方能传输的最大报文段长度(Maximum Segment Size,MSS。需要注意的是,MSS通常又受限于本地主机的链路层参数"最大传输单元(Maximum Transmission Unit,MTU)",MTU要保证一个TCP报文段+TCP报文段头部+IP数据报头部能存入单个数据帧中,而以太网中,MTU最大为1500B,TCP与IP的首部最小各占20B,因此,MSS最大为1460B)。
在TCP的三次握手建立连接与四次挥手释放连接中,主要关注上方加粗字段即可。
TCP三次握手建立连接
建立连接的报文段中,主要关注SYN,ACK,seq和ack的值。

- 握手1和握手2中的起始序号seq是各自决定的,用于标识自己之后要发送的数据部分第一个字节的位置;
- SYN字段表示当前TCP报文是请求连接还是同意连接,因此只有握手1和握手2时才置为1,其他均置为0;
- TCP全过程中,只有握手1的ACK=1表示确认后无效,因为在此连接建立之前都不存在属于此连接的通信;
- 虽然握手1和握手2不携带数据(即只有TCP首部,数据部分长度为0),但是仍然需要消耗一个序号,因此,当接收到握手1(seq=666)后,服务器端返回的握手2中,ack=握手1的seq+1(666+1=667,表示667之前的序号已经被正确接收,下一次期望接收667为起始序号的字节数据);
- 握手3可携带数据也可不携带数据,如果不携带数据,则不消耗序号。
握手3不携带数据的情况
握手3携带数据的情况
此时返回的TCP报文段ack=767(667+100,表示已经接收到这100B的字节数据,下次期望接收的字节序号为767)
TCP四次挥手释放连接
释放连接的报文段中,主要关注FIN,ACK,seq和ack的值。

- FIN段表示发送方数据已经发送完毕,主动释放连接,因此,只有挥手1和挥手3的FIN段才置为1,当某一方发出FIN段时,表示断开该方的单向传输;
- 挥手1和挥手3一般不携带数据,仍然需要消耗一个序号;
- 挥手2是可以携带数据的,而挥手4不可以携带数据,因为挥手1断开的为客户端->服务器端的数据传输,此时服务器端->客户端的数据传输通道还没有断开,仍然可以传输数据,而挥手4是客户端发出挥手1之后发出的,已经断开单向传输通道,不可再携带数据;
- 释放连接也可以是服务器端先发起。
为什么最后需要等待?因为挥手4的报文段可能因为某些原因迟达或丢失,服务器端会超时重传挥手3的报文段,2倍MSL可以兼顾丢包+重传FIN,保证连接释放的可靠性。因此,只要在2倍MSL内没有收到任何TCP报文段,则认为挥手4被正确接收,连接关闭。
结尾
终止,TCP的连接管理只有记住几个关键的字段并且知道它的意思,就可以很轻松。
注意:虽然图中所有报文段都只用一条线画出表示,但是当该报文段有携带数据时,应该考虑它的传输时延(也叫发送时延,是指将数据全部发送到信道上所需要的时间),当它只有TCP头部时,可以忽略它的传输时延。

最后是TCP连接管理的完整流程图:


