如何知道非阻塞插座是否关闭?

时间:2019-06-30 22:00:10

标签: python sockets python-sockets

我试图编写我自己的 TCP非阻塞服务器来处理多个持久的套接字连接,而不是打开许多线程来处理它们。

我已经编写了过于复杂,难以使用的语法,但是当我尝试检测关闭的套接字时遇到了问题。

在普通的线程化TCP套接字服务器中,我将使用从b''函数中检测到的socket.read(size),但是对于无阻塞套接字,这是不可能的,因为它将始终返回BlockingIOError < / p>

我还尝试在事件发生后捕捉泰斯语

except BrokenPipeError:
    conn.abort()
except ConnectionResetError:
    conn.abort()
except ConnectionAbortedError:
    conn.abort()
except socket.error:
    conn.abort()

conn是容纳socket.accept()中客户端套接字和地址的类

我不确定该怎么做,但这是我的代码中的一个大大简化的摘录:

def loop_listen(self):
    while self.running == True:
    cr, addr = self.server.accept()
    crs = SocketHandler(self, cr, addr)
    self.client_handler(crs)
    self.connections.append(crs)
    crs.events["open"]()
    crs.cr.setblocking(0)

def loop_recv(self):
    while self.running == True:
        time.sleep(self.poll_time)

        for conn in self.connections:
        try:
            data = conn.cr.recv(self.poll_size)
            print(data)
            if (data == b''):
                conn.abort()
        except BlockingIOError:
            data = None
        except BrokenPipeError:
            conn.abort()
        except ConnectionResetError:
            conn.abort()
        except ConnectionAbortedError:
            conn.abort()
        except socket.error:
            conn.abort()
        if (data != None):
            conn.events["msg"](data)

(两个循环都是单独的线程)

如果您需要它,这里是conn

class SocketHandler:
    def __init__(self, server, cr, addr):
        self.server = server
        self.cr = cr
        self.addr = addr
        self.events = {"msg": emptyCallback, "close": "emptyCallback","open":emptyCallback}
        self.cache = b""

    def message(self, func):
        self.events["msg"] = func

    def close(self, func):
        self.events["close"] = func

    def open(self, func):
        self.events["open"] = func

    def send(self, data):
        self.cr.send(data)
    def abort(self):
        self.cr.close()
        self.events["close"]()
        self.server.connections.remove(conn)

这在Windows上可以正常使用,但在Ubuntu上不会调用conn.abort()

任何帮助将不胜感激。

谢谢,山姆。

1 个答案:

答案 0 :(得分:1)

检测非阻塞套接字上的关闭连接的官方方法与阻塞套接字完全相同。它们从recv()返回空数据。

示例:

# Server
import socket
import time

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 12345))
s.listen(1)

while True:
    conn, addr = s.accept()
    conn.setblocking(0)
    print("New connection from " + str(addr) + ".")
    while True:
        try:
            data = conn.recv(1024)
            if not data:
                break
            print("Received:", data)
        except BlockingIOError:
            time.sleep(0.001) 

    print("Closed.")
# Client
import socket
import time

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 12345))

for i in range(5):
    time.sleep(0.3)
    s.send(str(i).encode('utf-8'))
s.close()

在一种特殊情况下,如官方文档第When Sockets Die节中所述,这种方法将不起作用。当套接字无法正常关闭时,就会发生这种情况。如果没有正常关机,recv()基本上无法检测套接字何时死亡。可能就是您所看到的。

有多种解决方法。首先,创建某种超时,如果在合理的时间内未收到消息,则该超时将关闭并丢弃套接字。其次,您可以主动发送消息。与send()相比,recv()检测死套接字要容易得多。

此外,这在Linux上有效。我没有在Windows上测试过。套接字类的内部实现非常依赖于平台,因此可能是Windows错误。