TCP是什么?
TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
关于TCP具体的知识这儿就不介绍了,下面挑出一些我们需要了解的基本知识进行重温
TCP头部


上面就是TCP协议头部的格式,由于它太重要了,是理解其它内容的基础,下面就将每个字段的信息都详细的说明一下。
-
源端口号(Source Port)/ 目标端口号(Destination Port):
表示发送端端口号和接受端端口号,字段长度均为16位(这也解释了为什么端口号的范围是0~65535),源端口号和目标端口号配合上IP首部中的源IP地址和目标IP地址就能唯一地确定一个TCP连接 -
序列号(Sequence Number):
字段长度为32位,用来指示发送数据的位置,需要注意的是它不一定从0或1开始,而是在建立连接时由计算机生成的随机数作为初始值;主要用来解决网络报乱序的问题 -
确认应答号(Acknowlegdement Number):
字段长度为32位,用来指示下一次应该收到的数据的序列号,实际上它代表了已收到确认应答号减一为止的数据。不过,只有当标志位中的ACK标志为1时该确认序列号的字段才有效。主要用来解决丢包问题 -
数据偏移(Data Offset):
给出首部的长度,需要这个值是因为选项字段的长度是可变的。这个字段占4位,每一位表示4个字节(即32位)。因此TCP最多有60字节的首部。然而,没有任选字段,正常的长度是20字节 -
保留(Reserved):
该字段主要是为了以后扩展时使用 -
控制位(Control Flag):
-
URG(Urgent Flag):
该位为1时,表示包中有需要紧急处理的数据,用来保证TCP连接不被中断,对于需要紧急处理的数据,会在后面的紧急指针中再进行解释 -
ACK(Acknowledgment Flag):
该位为1时,确认应答的字段变为有效。TCP规定除了最初建立连接时的SYN包之外该位必须置为1 -
PSH(Push Flag):
这个标志位表示Push操作。所谓Push操作就是指在数据包到达接收端以后,立即传送给上层应用协议,而不是在缓冲区中排队 -
RST(Reset Flag):
该位为1时表示TCP连接中出现异常必须强制断开连接,用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包 -
SYN(Synchronize Flag):
用于建立连接,置1时表示希望建立连接,并在其序列号的字段进行序列号初始值的设定,通常与ACK搭配使用 -
FIN(Finish Flag):
表示数据传送完成,没有数据可以传送了,当通信结束希望断开连接时,通信双方的主机之间就可以交换FIN位置为1的TCP段
-
URG(Urgent Flag):
-
窗口大小(Window Size):
该字段长为16位,用于通知从相同TCP首部的确认应答号所指位置开始能够接收的数据大小(8位字节)。TCP不允许发送超过此处所示大小的数据。另外,窗口为0时可用于发送窗口探测,以了解最新的窗口大小,但这个数据必须是1字节
三次握手及四次挥手

接下来就是今天的重头戏,首先是借由上图对两个“手”的过程的流程解读
三次握手
当我们需要通过TCP来传输数据时,就必须让客户端和服务端建立连接,这就是所谓的“三次握手”
1.第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Seq为x;接着客户端进入SYN_SEND状态,等待服务端的确认
2.第二次握手:服务端收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Ack为x+1(即Seq+1);同时,自己还要发送SYN请求信息,将SYN位置为1,Seq为y;服务端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态
3.第三次握手:客户端收到服务器的SYN+ACK报文段后将Ack设为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手
完成了三次握手,客户端和服务器端就可以开始愉快地传送数据啦~
四次挥手
那么当数据传输完毕后,我们必然会面临断开连接的问题,这个时候就需要“四次挥手”出场了~
1.第一次挥手:主机1(客户端或服务器端均可),设置Seq和Ack,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了
2.第二次挥手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求,但我可能还有数据需要传输,所以不要立即关闭
3.第三次挥手:主机2向主机1发送FIN报文段,表示我这已经准备好了,随时可以关闭连接了,同时主机2进入LAST_ACK状态
4.第四次挥手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明服务端已正常关闭,那么主机1也可以关闭连接了
到这里,一个完整的TCP建立连接 => 传送数据 => 断开连接的流程就结束了,然后就是处理一些常见的疑问(别称:面试题)
疑问1:为什么必须是四次挥手呢?把第二次挥手和第三次挥手合为一次不行吗?
答:关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET(因为此时数据很可能未传输完毕),暂且只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
疑问2:三次握手中最后一次握手不能直接去掉吗?
答:在谢希仁著《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。什么意思呢?假设我们将其改为两次握手,那么如果A发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求,而后收到确认后建立好了连接,接着该干嘛该干嘛,最后把连接断开就完事儿了。但是,这时候上一个丢失的报文突然又冒出来传到服务端去了,服务端接收后返回确认报文,按照我们假设的情况,这时候本不该建立的连接便建立起来了,可客户端本就无意建立连接,也就不会发送数据给服务端,服务端就一直干等着,浪费资源。
疑问3:为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSED状态?
答:TIME_WAIT状态其实是用来重发可能丢失的ACK报文,为啥呢?你想啊,要是最后一次挥手发出去的ACK报文不见了咋办,这时候服务端就不断地抛FIN给客户端,问“我可以结束了呀,你咋不理我了呢”,但客户端早早地就进入CLOSED态,谁还理你呀~因此需要客户端设置一个定时器来防止这种窘境的出现,在这段时间里如果你没重复发送FIN给我,那我就默认你收到我的报文了,于是断开连接
参考资料(感谢各位前辈大佬):
网友评论