“三次握手”:
(1)首先客户端向服务器端发送一段TCP报文,其中:
标记位为SYN,表示“请求建立新连接”;
序号为Seq=X(X一般为1);
随后客户端进入SYN-SENT阶段。
(2)服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文,其中:
标志位为SYN和ACK,表示“确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接”(即告诉客户端,服务器收到了你的数据);
序号为Seq=y;确认号为Ack=x+1,表示收到客户端的序号Seq并将其值加1作为自己确认号Ack的值;随后服务器端进入SYN-RCVD阶段。
(3)客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文。其中:
标志位为ACK,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了);
序号为Seq=x+1,表示收到服务器端的确认号Ack,并将其值作为自己的序号值;
确认号为Ack=y+1,表示收到服务器端序号Seq,并将其值加1作为自己的确认号Ack的值;随后客户端进入ESTABLISHED阶段。服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。
在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。
为什么采用三次握手而不是一次握手、两次握手?
1、采用一次握手时,客户端想要建立连接tcp连接,会向服务器发送SYN。可能遇到的问题是:
a.请求连接的SYN丢失
b.服务器拒绝连接
这无法确定是否建立起了可靠的连接,如果服务器能有一个反馈就好了,这就引出二次握手。
2、采用两次握手时,客户端向服务器发送SYN,可能遇到的问题有:
a.客户端发送的SYN因网络滞留,在该连接释放后才发送到服务器,服务器建立连接,但客户端并没有发出新的连接请求,于是不理睬服务端,造成资源浪费。
b.服务器发送给客户端的SYNACK丢失了,但当服务器发送了SYNACK后就认为连接已经建立。而客户端在长时间没收到SYNACK后,认为TCP未连接。对于客户端来说,该连接未建立在实时性要求不高的场合是无伤大雅的,无非是等待一段时间未收到SYNACK再重新发起一个连接请求,浪费一些时间而已。但对于服务器来说,这可能是致命的,因为服务器认为该连接已经建立,所以必须分配资源,并对变量进行维护,造成了资源浪费。如果服务器没有数据要发送给客户端还可能接受这条虚假连接的存在,因为服务器端在观察到这条连接长时间没有数据发送时,会强制关闭连接,释放被这条链接占用的资源。要命的是万一服务器端有数据要发送给客户端,服务器端会在发送完SYNACK包(认为连接已经建立)后发送数据包给客户端,而客户端会因为认为这条连接不存在而不理会这些数据包(即,即使收到了也不会给服务器端ACK反馈)。服务器端收不到客户端的ACK就会触发超时重传,重传后还是收不到ACK就会再次重传,重传又重传。。。。无休无止,这就是死锁。占用的资源永远得不到释放,多几条这样的连接服务器就无法再正常工作了。
显然,两次握手存在问题无法实现可靠的TCP连接。
三次握手
对三次握手的可靠性进行检测,考虑到之前各种各样可能出现的问题,看看三次握手是否能解决问题,并还能实现tcp的可靠连接。
情况一:
客户端的SYN 包丢失,这种情况无论采用哪种握手协议,都可能存在,解决也很简单,客户端在一定时间内未收到服务器端的SYNACK即认为SYN未到达服务器端,重新发起连接即可。
情况二:
服务器端发送的SYNACK丢失,同样的对于客户端,在某段时间后会再次发起tcp连接。对于服务器端,发送完SYNACK包后,未见客户端的ACK,则会认为SYNACK包丢失,服务器端就会每3s 6s 12s重发SYNACK包,若重发数次SYNACK包仍不见客户端的ACK(发送的时间和发送的次数均可配置),则会关闭连接,不会造成资源占用。
情况三:
客户端的ACK丢失,同上,服务器端收不到ack会认为SYNACK未到达客户端(即使SYNACK到客户端了),重发SYNACK。直到接收到ACK或关闭连接。
综上可见,三次握手能够实现TCP的可靠连接。
既然三次握手能实现TCP的可靠连接,那么四次 五次握手能不能实现就没有考虑的必要了,即使能实现也是费力不讨好的事,没有使用的必要了。