Archive

Posts Tagged ‘tcp’

正确关闭一个 tcp socket

February 11th, 2015 No comments

在将现有的阻塞同步socket库转换为异步非阻塞socket库(好吧,其实我们叫它非阻塞异步事件库)的时候,我们发现了一个问题。 就是当数据发送端发送完大量数据(例如20G)的最后一段数据以后,close掉socket(tcp socket)以后客户端收到的数据总是不能达到预期的数据大小,总是比预期的数据少一部分,而且具体少多少数据看起来还是随机的。debug多次以后我们认为是我们最后一次send以后就调用了close(socket_fd)造成的。 
我们知道, send其实只是把要发送的数据放到kernel的buffer中去,具体的发送动作是由kernel自己触发的。当我们发送最后一段数据以后立即调用close的时候可能还有很多没法发送出去的数据在内核的buffer中,而内核可能会直接丢弃掉未发送的数据,这样对方就可能无法接收到完整的数据。由于我们的异步事件库都是非阻塞的,所以我们不能设置SO_LINGER来等待socket把剩下的数据发送出去。否则这个close可能会阻塞调用很长一段时间。所以解决办法是当socket需要被close的时候,首先调用shutdown(socket_fd,SD_WR),也就是关闭写, 这个时候应该会向tcp socket kernel buffer中添加一个FIN的数据包。 然后我们通过epoll监视EPOLL_IN事件来接收对方的close事件(也就是对方发送FIN). 当收到对方的FIN以后我们就可以真正的关掉这个socket了。 当然了实际的代码是在发送shutdown(fd,SD_WR)以后就设置一个timer(例如5秒timeout)。如果timer过期了,那么无论是否收到对方的FIN都会关掉这个socket。这样可以防止对方程序出现永远不关闭socket的情形发生进而影响本机的socket的关闭。

Categories: programming Tags: ,