如果套接字关闭,select.poll会检测读事件(select.POLLIN)

时间:2016-05-01 12:31:42

标签: python sockets

我无法在特定网络中检测到套接字客户端关闭。我正在运行套接字服务器,一旦客户端连接,我正在保存客户端套接字并定期向客户端发送请求。我正在使用select.poll然后检查是否有任何数据要从套接字读取,如果有,将从套接字读取。到目前为止,这一切都很好。

问题是,如果远程套接字客户端被终止,将选择.poll信号在客户端套接字中读取事件。如果发生这种情况,那么我可以检查socket.recv中返回的数据长度,以检测客户端是否已断开连接 - as is described here

为select

添加代码段
    def _wait_for_socket_poller(self, read, write, message=None):
    """
    Instead of blockign wait, this polls and check if the read or write socket is ready. If so it proceeds with
    reading or writing to the socket. The advantage is that while the poll blocks, it yeilds back to the other
    waiting greenlets; poll blocks because we have not given a timeout

    :param read: The read function
    :param write: The write function
    :param message: The CrowdBox API call
    :return: The result : In case of read - In JSON format; But catch is that the caller cannot wait on the
    result being available,as else the thread will block
    """
    if not self.client_socket:
        logging.error("CB ID =%d - Connection closed", self.id)
        return

    poller = select.poll()

    # Commonly used flag setes
    READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR
    WRITE_ONLY = select.POLLOUT
    READ_WRITE = READ_ONLY | select.POLLOUT

    if read and write:
        poller.register(self.client_socket, READ_WRITE)
    elif write:
        poller.register(self.client_socket, WRITE_ONLY)
    elif read:
        poller.register(self.client_socket, READ_ONLY)

    # Map file descriptors to socket objects
    fd_to_socket = {self.client_socket.fileno(): self.client_socket, }
    result = ''
    retry = True
    while retry:
        # Poll will Block!!
        events = poller.poll(
            1)  # using poll instead of select as the latter runs out of file descriptors on load
        # Note here, Poll needs to timeout or will block ,as there is no gevent patched poll, the moment it blocks
        # neither greenlets or Twisted Deffered can help -Everything freezes,as all of this is in main thread
        if not events:
            retry = True
            gevent.sleep(0)  # This is needed to yeild in case no input comes from CB
        else:
            retry = False

    clientsock = None
    fd = None
    flag = None
    for fd, flag in events:
        # Retrieve the actual socket from its file descriptor to map return of poll to socket
        clientsock = fd_to_socket[fd]

    if clientsock is None:
        logging.error("Problem Houston")
        raise ValueError("Client Sokcet has Become Invalid")

    if flag & select.POLLHUP:
        logging.error("Client Socket Closed")
        self.client_socket.close()
        self.client_socket = None
        return None

    if flag & (select.POLLIN | select.POLLPRI):
        if read:
            result = read()
    if flag & select.POLLOUT:
        if write:
            result = write(message)
    # poller.uregister(self.client_socket)
    return result

2 个答案:

答案 0 :(得分:0)

通常,是的,套接字将被标记为"可读"当TCP连接关闭时。但这假设正常关闭,意味着TCP FIN或RST数据包。

有时TCP连接不会以这种方式结束。特别是,如果未启用TCP Keep-Alive(并且默认情况下不启用),则服务器和客户端之间的网络中断可以有效地终止连接,而无需任何一方知道,直到他们尝试发送数据。

因此,如果您想确保在TCP连接断开时立即得到通知,则需要在TCP层或应用层发送保持活动消息。

保持活动消息具有额外的好处,可以防止未使用的连接因长时间不活动而被各种网络设备自动丢弃。

有关保持活力的更多信息,请参阅此处:http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html

答案 1 :(得分:0)

想在这里添加一个anwer,以便我可以发布一些tcp转储跟踪。我们在实时网络中对此进行了测试。远程计算机中的Socket客户端进程终止,并且python socket.send(在非阻塞套接字上)client_socket.setblocking(0)没有返回任何错误,因为后续请求从服务器发送到客户端没有生成事件表示(EPOLLIN)要读的东西。

因此,为了检测客户端连接丢失,我们会定期ping客户端,如果三次重试后没有预期的响应,请断开客户端连接。基本上在应用程序层中处理这个问题。客户也改为回复我们的“你还活着”请求的一些数据而不是忽略它。

    sent = 0
    try:
        sent = self.client_socket.send(out)
    except socket.error as e:
        if e.args[0] == errno.EPIPE:
        logging.error("Socket connection is closed or broken")
    if sent == 0 and self.client_socket is not None:
        logging.error("socket connection is already closed by client, cannot write request")
        self.close_socket_connection()
    else
      # send succcessfully

下面是tcpdump wireshark跟踪,您可以在其中看到重新发送的情况。为安全起见屏蔽了IP细节 tcpdump wireshark trace