gevent关闭socket操作

时间:2022-06-03 00:18:54

最近在分析系统的时候,发现在压测的时候服务端出现了比较多的CLOSE_WAIT状态的tcp连接,

需要等一会才能消除掉。。。。

恩,CLOSE_WAIT状态的tcp状态是怎么出现的呢。。:


首先客户端首先调用的close方法,那么将会发送fin数据包给服务端。。。

服务端收到fin数据包之后,将会返回ack确认,然后服务器的tcp连接就进入了CLOSE_WAIT状态了,这个时候服务端的socket再调用一次close,那么服务器将会给客户端发送fin数据包,这个时候服务器进入LAST_ACK状态,等待客户端返回ack数据包。。。

当服务端收到了客户端的ack数据包之后,那么对于服务端来说就相当于连接关闭了,可以释放文件描述符资源啥的了。。但是客户端在发送了最后一个ack之后需要进入TIME_WAIT状态,因为有可能服务端没有收到ack数据包,将会超时重传fin数据包。。。。


那么在压测时候,因为是客户端首先调用close方法,但是服务端却又很多CLOSE_WAIT状态的tcp连接,那么说明服务端并没有调用close方法,


但是自己代码里面又确实调用了gevent的socket的close方法,那么出现这种情况的原因是什么呢。。?

先来看一下gevnet的socket的close方法的实现:

    # 注意,这里其实并没有调用socket的close
def close(self, _closedsocket=_closedsocket, cancel_wait_ex=cancel_wait_ex):
# This function should not reference any globals. See Python issue #808164.
self.hub.cancel_wait(self._read_event, cancel_wait_ex)
self.hub.cancel_wait(self._write_event, cancel_wait_ex)
self._sock = _closedsocket()

上面的代码其实只是取消了读写时间而已,并没有真正调用底层socket的close方法,但是这里会将_sock设置为另外一个对象,那么这个时候底层socket的引用就没有了。。。。

那么,引用计数变成0,就将会被python收了。。。。

那么看看Python的socket模块的析构是怎么写的吧:

static void
sock_dealloc(PySocketSockObject *s)
{
if (s->sock_fd != -1)
(void) SOCKETCLOSE(s->sock_fd);
if (s->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *)s);
Py_TYPE(s)->tp_free((PyObject *)s);
}


因此可以知道,如果sock正常被回收的话,肯定是会调用close的,那么这里并没有调用。。只能说明还有对底层python的socket对象的引用,恩。。确实是这样子的。。。因为代码循环引用,而且直接引用了底层socket,所以导致底层sock并没有及时释放,从而导致服务端有很多CLOSE_WAIT状态的连接。。。


我擦。。还好这其实对于服务端来说没问题并不大。。。不会引起什么大问题。。。改了一下就好了。。。。