tcp为什要三次握手
准备知识:
单工:信息只能单向传递。发送-->接收,单向,不能返回响应。
双工:指的是信息可双向发送。
全双工:信息可同时双向传递。
半双工:不能同时,单行道,一边传输完了,另一边才能发起传输。
由于IP协议是不可靠的,为了在不可靠信道上传输可靠数据,就要进行三次握手,准确的说是发送三次预备信息,这样就完成了信息传递的双工准备。(HTTP协议是需要双向传递的)。
双方都需要确认自己的发信和收信功能正常,收信功能通过接收对方信息得到确认,发信功能需要发出信息—>对方回复信息得到确认。
第一次发信息: CLIENT --> SERVER
第二次发信息: SERVER --> CLIENT 客户端接收到了,确信自己可以收,并且收到的信息里有上一步发过去信息里的内容,说明也是可以发送的。
第三次发信息: CLIENT --> SERVER 同理,服务端也确信了可以收和发的能力。
如果没有第三次发信息,只是保证了一个单向的连接畅通。客户端可以大胆地单向给服务端发信息。服务端由于没收到过“回音”,不能确定对方能不能收到,tcp协议是保证了传输可靠性的,不允许这种不确定性存在,就不允许服务器给客户端发送响应了。
当然UDP协议就不保证输出的可靠性了,就算没有收到过“回音”也敢发出信息。
-------------------------------连接断开的时候为什么要四次呢-----------------
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。
因为可以同时双向传输,A B 两个终端上有两个通道四个端口。分别是A端的读端口,A端的写端口和B端的读端,B端的写端口。
TCP采用四次挥手关闭连接如图2所示。
以客户机发起关闭连接为例:
1.Client发送FIN给Server,Server收到Fin信息后知道已经没有要再接收的东西了,就关闭自己的读端口,然后发送ACK给CLient --Server 读端关闭
2.Client收到ACK后,知道对面已经关闭读通道了,现在自己可以把写端口关闭了 --Client 写端关闭
3.Server发送FIN给Client表示自己没有要发送的了,Client收到FIN后,关闭自己的读端口,然后发送ACK给Server --Client 读端关闭
4.Server接受到ACK后,关闭自己的写端口,并且Server的Conection状态变为Closed,彻底关闭可以重用了。 --Server 写端关闭
5.Client在最后发给Server的ACK报文后等待两个MSL时间,然后Client端的onection状态变为Closed。
* 可以看出,读端口的关闭,是收到对方发来的FIN信号即可。而写端口的关闭是,发送一个FIN给对方,收到对方的ACK回复才可以关闭。这是确保数据包都发送过去的重发机制要求的。
* 第2步和第3步很多时候都可以合并,就是收到对方的FIN的时候,自己没有要发过去的东西,就把ACK和FIN一起返回去了。
* 主动发起关闭连接的一方发出最后的ACK报文后达到TIME_WAIT状态,而且这个状态要保持Maximum Segment Lifetime的两倍时间。这是因为两点:
1 保证TCP协议的全双工连接能够可靠关闭
2 保证这次连接的重复数据段从网络中消失
先说第一点,如果Client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。这种情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候再发一个ACK给对方。保证对方收到,最后正确的关闭连接。(MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间,为操作系统决定大小,windows默认MSL值为2分钟。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。)
再说第二点,如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。
------------------TCP的粗暴关闭连接方式--------------------
除了如上可靠的关闭方式之外,TCP还提供了另外一种不可靠的关闭方式RST(Reset)
(CLOSED) A ---RST--> B (CLOSED)
A端发送RST状态之后,TCP进入CLOSED状态,B端接收到RST后,也即可进入CLOSED状态。
在上面第一关闭方式上(可靠的),非常遗憾,A端在最后发送一个ACK请求后,并不能马上将该Socket回收,因为A并不能确定B一定能够接收到这个ACK报文,因此A端必须对这个Socket维持TIME_WAIT状态2MSL。
如果A端是Client,这并不会成为问题,但如果A端是Server,那就很危险了,如果连接的Socket非常多,而又维持如此多的TIME_WAIT状态的话,那么有可能会将Socket耗尽(报Too Many Open File)。
服务端为了解决这个问题,可选择的方式有三种:
Ø 保证由客户端主动发起关闭(即做为B端)
Ø 关闭的时候使用RST的方式
Ø 对处于TIME_WAIT状态的TCP允许重用
一般我们当然最好是选择第一种方式,实在没有办法的时候,我们可以使用SOCKET参数【SO_LINGER】开启第二种方式,使用SOCKET参数【SO_REUSEADDR】开启第三种方式