【问题标题】:How to limit the number of connections to a socket and trigger timeout on client (Python)如何限制到套接字的连接数并在客户端(Python)上触发超时
【发布时间】:2016-02-04 14:33:56
【问题描述】:

如何设置服务器套接字一次可以接受的连接数限制?我希望能够设置最大连接数,然后一旦达到该限制,客户端的任何进一步连接尝试都将导致超时。到目前为止,我已经为服务器尝试过这样的事情:

sock = socket.socket()
sock.setblocking(0)
sock.bind(address)
sock.listen(0)

connections = []

while True:
    readable, writable, exceptional = select.select([sock], [], [])

    if readable and len(connections) < MAX_CONNECTIONS:
        connection, client_address = s.accept()
        connections.append(connection)
        # Process connection asynchronously

对于客户:

try:
    sock = socket.create_connection(self.address, timeout=TIMEOUT)
    sock.settimeout(None)
    print "Established connection."
except socket.error as err:
    print >> sys.stderr, "Socket connection error: " + str(err)
    sys.exit(1)

# If connection successful, do stuff

由于程序其余部分的结构,我选择在服务器上使用非阻塞套接字,我不想更改它。现在,客户端能够连接到服务器,即使在达到限制并且服务器停止接受它们之后。我该如何解决这个问题?谢谢。

【问题讨论】:

  • 如果您接受了连接但立即关闭它会有帮助吗?
  • 我想让它一旦达到连接限制,进一步的连接不会立即失败,因为在达到超时之前另一个客户端可能会断开连接。
  • 您的 select.select 语句基本上已经接受了连接。考虑根据当前正在服务的连接数将该语句放在条件中。

标签: python sockets


【解决方案1】:

我相信这里可能对select() 有一点误解。根据手册页,select() 返回的文件描述符是“准备好进行某种类型的 IO 操作”,其中准备好意味着“可以在不阻塞的情况下执行相应的 IO 操作”。 监听套接字上对应的 IO 操作是accept(),只有在操作系统已经为您进行了完整的 TCP 握手时才能在不阻塞的情况下执行该操作,否则您可能会阻塞等待客户端的最终 ACK。

这意味着只要监听套接字打开,操作系统就会接受连接,即使应用程序没有处理。 如果您想在设定数量后拒绝连接,您基本上有两种选择:

  • 直接接受,接受后直接关闭。
  • 达到限制时关闭侦听套接字,完成后重新打开。

第二个选项更复杂,需要使用SO_REUSEADDR 选项,这在您的情况下可能不正确。它也可能不适用于所有操作系统,尽管它似乎在 Linux 上可靠地工作。

这是第二个解决方案的简要草图(因为第一个解决方案非常简单)。

def get_listening_socket():
    sock = socket.socket()
    sock.setblocking(0)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('0.0.0.0', 5555))
    sock.listen(0)
    return sock

sock = get_listening_socket()

LIMIT = 1
conns = {sock}
while True:
    readable, writable, exceptional = select.select(conns, [], [])
    if sock in readable: # new connection on the listening socket
        conn, caddr = sock.accept()
        conns.add(conn)
        if len(conns) > LIMIT: # ">" because "sock" is also in there
            conns.remove(sock)
            sock.close()
    else: # reading from an established connection
        for c in readable:
            buf = c.recv(4096)
            if not buf:
                conns.remove(c)
                sock = get_listening_socket()
                conns.add(sock)
            else:
                print("received: %s" % buf)

但是,您可能想要重新考虑为什么您首先要这样做。如果只是为了在服务器上节省一些内存,那么您可能会过度优化,应该考虑使用 syn-cookies。

【讨论】:

  • 我传递给listen 的号码完全不影响这个吗?如果是这样,将数字传递给listen 有什么意义?
  • 另外,socket.SOL_SOCKET 是什么?我找不到关于它的文档
  • listen() 的联机帮助页回答了您的问题:“如果在队列已满时连接请求到达,则客户端可能收到带有 ECONNREFUSED 指示的错误,或者, 如果底层协议支持重传,则该请求可能会被忽略,以便稍后重新尝试连接成功。"由于您可能正在使用 TCP,并且 TCP 支持重传,因此我们在第二种情况下必须明确拒绝新连接(这实际上有点破坏了 TCP 的一个不错的功能)。
  • 至于SOL_SOCKET,这仅意味着您正在更改套接字 API 级别的选项。例如,您可以在 TCP 级别更改特定套接字的选项。 (再次来自手册页;))
猜你喜欢
  • 2011-08-03
  • 2013-04-17
  • 2019-08-22
  • 1970-01-01
  • 2013-11-30
  • 1970-01-01
  • 2021-12-22
  • 2011-05-04
  • 1970-01-01
相关资源
最近更新 更多