三次握手 建立起 TCP连接 的 reliable,分配初始序列号和资源,在相互确认之后开始数据的传输。有 主动打开(一般是client) 和 被动打开(一般是server)。
TCP使用3次握手建立一条连接,该握手初始化了传输可靠性以及数据顺序性必要的信息,这些信息包括两个方向的初始序列号,确认号由初始序列号生成,使用3次握手是因为3次握手已经准备好了传输可靠性以及数据顺序性所必要的信息,该握手的第3次实际上并不是需要单独传输的,完全可以和数据一起传输。详细过程如下所示:
第一步,Client会进入SYN_SENT状态,并发送Syn 消息给Server端,SYN标志位在此场景下被设置为1,同时会带上Client这端分配好的Seq号,这个序列号是一个U32的整型数,该数值的分配是根据时间产生的一个随机值,通常情况下每间隔4ms会加1。除此之外还会带一个MSS,也就是最大报文段长度,表示Tcp传往另一端的最大数据块的长度。
第二步,Server端在收到,Syn消息之后,会进入SYN_RCVD状态,同时返回Ack消息给Client,用来通知Client,Server端已经收到SYN消息并通过了确认。这一步Server端包含两部分内容,一部分是回复Client的Syn消息,其中ACK=1,Seq号设置为Client的Syn消息的Seq数值+1;另一部分是主动发送Sever端的Syn消息给Client,Seq号码是Server端上面对应的序列号,当然Syn标志位也会设置成1,MSS表示的是Server这一端的最大数据块长度。
第三步,Client在收到第二步消息之后,首先会将Client端的状态从SYN_SENT变换成ESTABLISHED,此时Client发消息给Server端,这个方向的通道已经建立成功,Client可以发送消息给Server端了,Server端也可以成功收到这些消息。其次,Client端需要回复ACK消息给Server端,消息包含ACK状态被设置为1,Seq号码被设置成Server端的序列号+1。(备注:这一步往往会与Client主动发起的数据消息,合并到一起发送给Server端。)
第四步,Server端在收到这个Ack消息之后,会进入ESTABLISHED状态,到此时刻Server发向Client的通道连接建立成功,Server可以发送数据给Client,TCP的全双工连接建立完成。
图片来自网络,侵权请告知,会及时删除
TCP的连接因为是全双工的,也就是Client和Server两端,发送消息两个方向的连接都要建立成功。如果要保证双向连接都成功的话,三次通信是最少的次数了。大于三次的话,后面的次数通信就没有必要了,是在浪费资源。
二次的话,会怎么样,可不可以呢?答案是不可以,我们来看下,下面的场景。
在谈论这个之前,我们先要知道TCP是基于IP协议的,而IP协议是有路由的,IP协议不能够保证先发送的数据先到达,这当中依赖于IP协议底层的网络质量,以及Client与Server之间的路由跳数。
Client在发送完Syn消息1,这里称作Syn1之后,假设因为网络原因,Syn1并没有到达Server端,这个时候Client端已经超时,Client之后重新发起SYN消息,这里称作Syn2。结果由于网络原因Syn2先到达Server,Server于是与Client基于Syn2建立了连接,结果没过多久Syn1又到达了Server,Server于是关掉了Syn2建立的那条连接,又重新建立了一条连接。对于Client来说新建立的这条连接是早就过时的,所以Client不会在这条连接上发送任何数据,这就导致了Server端长时间收不到数据,Client新的连接被断掉了。
这里要看是在那个阶段失败的,Client在发送SYN之后没有收到ACK消息,Client会进行重传,第一次重传时间5.5-6s之间,第二次重传会是24s,不成功还会继续尝试,伯克利系统在超过75s之后,如果还是不成功,会放弃尝试连接。(备注:这里面的重传时间设置,与底层的定时器设置有过关系,可以参考TCP/IP详解卷1,这里不做详谈。)
如果Server没有收到最后的一次Ack消息,同样的原理,Server也会进行重传第二步的Syn+Ack消息。
TCP的状态变化图如下所示,Client和Server的初始状态都是CLOSED状态,其中:
Client:
CLOSED-----发送Syn 消息--->SYN_SENT-----收到Server端发回的SYN+ACK--->ESTABLISHED---发送和接收数据-->......
Server:
CLOSED----收到Client端发送过来的SYN消息-->SYN_RCVD-----发送SYN+ACK消息--->SYN_RCVD----收到Client发回来的ACK消息--->ESTABLISHED-----发送和接收数据-->......
其中TCP的三次握手与Scoket的函数对应关系如下所示:
图片来源见图片,如有侵权请告知,会及时删除
备注:connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。--摘自:原文:https://blog.csdn.net/futurewu/article/details/76674016
TCP/IP详解 卷 I
https://blog.csdn.net/futurewu/article/details/76674016
https://blog.csdn.net/dog250/article/details/6612496
https://blog.csdn.net/varyall/article/details/80348850