1. 3次握手
tcp连接建立需要3次握手:
1. C(syn)--->S
2. S(syn+ack)--->C
3. C(ack)--->S。
仅一次握手肯定是不行的,因为一次握手C无法知道S的状态,若S不可达,C就在那白白的发送数据,浪费资源,不过C在重发了N次会释放连接。
就两次的话。问题是:C-->S的syn包被网络阻塞了,若这个syn包被S收到时,C跟S已经通信完毕,则S收到syn会认为重新建立连接。但保活定时器可在其超时后回收该无效连接。
而三次握手不见得就100%可靠了,比如上诉2次握手的问题就被解决了。同样,最后一个ack包被网络阻塞,C重发ack后,C跟S通信完毕。此时S又收到该ack包,此时S处于lisiten状态,对ACK包无视,问题解决。而四次握手就显得多余了,因为ack包是不需要确认的。
2. backlog
但C跟S3次握手成功后,TCP层会把该连接放入一个接受队列。应用程序通过accept调用,从该队列取出连接。backlog规定了该队列的大小.若队列满,则无法建立新的连接,S将不理会C的syn包,C将开启重传机制,重传syn包。
3. 延迟确认
在Tcp中收到一个数据,通常需要回复一个ack包。一个不包含数据的ack报文需要tcp首部,ip首部,链路首部,而实际发送数据可能为零。所以TCP通常会开启一个定时器,将ack跟后续的数据做为一个包传送过去。可以通过TCP_QUICKACK选项快速发送 ACK确认。但Java中貌似没有该选项。
4. Nagle算法
为了能够减少网络中小分组的数量,避免网络拥塞,可采用nagle算法。该算法规定一个连接上只能有一个未确认的分组,那么在确认收到的时,tcp可以收集很多小分组,并将他们做一个分组发送数据,减少了数据量。它的问题是,确认的到来受到网络的影响,存在时延,所以对待实时性要求高的应用需要关闭该算法。socket.setTcpNoDelay(true)。
5. keepalive
正常的连接终止,对方会受到终止连接的包。但异常断开,比如网线被拔出、断电等,对方无法感知,仍会保持一个无效连接。若双方在很长的一段时间无数据交互,则该连接会一直保持。Tcp的解决方案是开启keepalive机制,在双方超过规定时间无数据交互时,发送一个数据包确认对方是否存活。
通常,判断对方是否存活,可通过应用层自己来实现,比如很久没收到对方数据,可认为断开等。有些应用会自行开发心跳包机制来维持双方的连接状态。
6. 断开连接
断开一个连接需要四次握手,这是由于TCP是全双工的原因造成的。
1. A->B fin。
此时A进入Fin_wait_1,B收到fin后进入Close_wait状态,此时向A发送一个ack,并向应用程序提交一个EOF(也就是read返回-1)。A发送fin后表示不继续发送数据,但仍能接受数据。
2. B->A ack
A进入fin_wait_2
3. B->A fin
B进入last_ack状态,A进入time_wait状态。此时双方都不能收发数据
4. A->B ack
B进入Closed状态。
java中有对应的半关闭方法。
7. TIME_WAIT
一个socket处于TIME_WAIT状态则该socket在2msl时间内不能被重用。这是为了防止之前连接迟到的报文被接收。SO_REUSEADDR选项可使地址在2msl内也可重用。
阅读(849) | 评论(0) | 转发(0) |