新浪微博 登陆  注册   设为首页 加入收藏

学PHP >> 网络协议 >> TCP连接关闭总结

TCP连接关闭总结

查看次数18130 发表时间2012-07-26 23:58:05

由于涉及面太广,只作简单整理,有兴趣的可参考《UNIX Networking Programming》volum 1, Section 5.7, 5.12, 5.14, 5.15, 6.6 以及7.5 SO_LINGER选项。

以一个简单的echo服务器为例,客户端从标准输入读入字符,发送给服务器,服务器收到后再原样返回,客户端收到后打印到标准输出。

那么,关于套接字的关闭有以下几种情形:

1,客户端关闭连接:

1.1,客户端调用close()

1.2,客户端进程关闭

1.3,客户端调用shutdown()

1.4,客户端调用close()+SO_LINGER选项

1.5,客户端崩溃

 

2,服务器关闭连接:

2.1,服务器调用close()

2.2,服务器进程关闭

2.3,服务器崩溃

2.4,服务器崩溃+SO_KEEPALIVE选项

========================================分割线=========================================

1.1与1.2等价,就算客户端进程关闭,系统内核也会自动close(socket),且注意,当socket引用为0时才会真正调用close(),close()总是立即返回的,然后由系统尝试发送完内核缓冲区内的所有数据,接着才发送FIN。

说道这里,不得不谈谈TCP连接关闭的四次握手。可以看成是2组FIN, ACK。主动关闭的一方先发送FIN,收到ACK后,进入FIN_WAIT2状态,此时也叫做“半关闭”状态,特别须要注意的是,此时客户端套接字依然可以接收数据包,但是不能发送数据包。 被动关闭的一方,此时收到FIN了,一般情况下都是由于read(socket)返回0,然后得知对方关闭,close(socket)后,另外一组FIN,ACK随之产生,此时主动方进入TIME_WAIT状态。即四次握手完成。

以上即是正常情况下连接关闭的情形。

再看看1.3,shutdown()与close()主要有3点区别:

shutdown()不理会引用计数与内核缓冲区内剩余待发数据包,直接发送FIN;

shutdown()可以只关闭套接字某个方向的连接,例如关闭发送,关闭接收,或者2者都关闭;

实际上shutdown(write)后,就是上面说的半关闭情形,依然可以完成四次握手。

再看看1.4,为什么要设置SO_LINGER呢

SO_LINGER的目的就是改变close()的默认行为,可以决定close()在哪个状态返回,或者让套接字立即发送RST,从而没有FIN的发送。接收方返回ECONNRESET错误,连接直接关闭。

再来总结下1.1-1.4,这么多关闭连接的方式,那么什么方式才是最好的呢?

择优选择的方式当然是考虑最恶劣的情况,对方主机崩溃或网络故障导致数据包传输停滞。

RST不用考虑了,直接TIME_WAIT状态都没,如果有网络故障,可能下次创建的套接字还会接收到已经被销毁的套接字的数据报。

close()不能保证对方一定收到FIN。

close()+SO_LINGER虽然能控制close()在收到ACK后返回,依然不能保证四次握手完成。

shutdown()先进入半关闭状态,再调用read(),返回0(收到对方FIN)则说明四次握手正常进行,此为最优方式。

其实仔细想想,一般情况也不用这么麻烦,拿网游服务器来说,客户端close()后,就算服务器不知道,那么这种情况归为1.5讨论;如果是服务端close()而客户端不知道,那么归为2.3讨论。总之都有解决办法。。

现在再讨论1.5,很简单,服务端加入链路异常检测机制即可,这也是所有大型TCP服务器必备的机制,定时发送小数据包检测客户端是否有异常退出。

========================================分割线=========================================

服务器关闭连接方面:

2.1,2.2等价,一般情况下也与1.1,1.2等价,只是主动关闭方是服务器了。

但是,在我们讨论的例子里,客户端要从标准输入读字符,这是阻塞方式,服务端关闭连接后,客户端无法知道,因为它阻塞在标准输入了,当我们再次输入字符,并发送,收到FIN或RST,此时客户端才关闭。总之,客户端由于某种原因,不能及时调用read(),所以无法得知服务器关闭了连接。

2.3,服务器崩溃,客户端由于一直收不到ACK,会一直尝试发送数据,标准socket大概是9分钟后才会返回错误。

2.3,服务器崩溃,客户端又长时间与服务器没有数据交互,此时设置SO_KEEPALIVE选项可得知。

========================================分割线=========================================

后记:网络是门复杂的学问,由此TCP连接的关闭可见一斑。普通程序员通常不会考虑这么细致,但是我相信这些问题一直困扰着他们。

 

补充说明:经试验,在Windows平台,1.2  2.2情况等同于close()+SO_LINGER选项直接发送RST,可能由于系统必须及时清理资源吧,这点与linux是不同的,有兴趣的可以试试。。


(转发请注明转自:学PHP)    


  相关推荐



1楼 Sugar说: 2016-12-22 05:43:54
Corina i.ti multumesc mult pt raspuns pe mine tot corina ma cheama (omul ansnnm)iioeamna ca am inteles eu gresit de la dna doctor poate ca se referea la faptul ca in timpul purtarii nu mai potu musca nu si dupa ce il scoti.Ma gandeam ca poate dintii raman mobili si de aceea nu mai ai voie sa musti din nimic ca ar fi groaznic asa nu ?

  发表评论
昵称:
(不超过20个字符或10个汉字)
内容: