gpt4 book ai didi

python-3.x - 在Python套接字上使用select()和TCP Keepalive的问题

转载 作者:行者123 更新时间:2023-12-03 11:53:43 25 4
gpt4 key购买 nike

我希望有一个TCP服务器等待客户端连接,并在客户端连接后立即连续向它们发送一些数据。我还希望服务器注意到客户端是否突然消失,没有任何痕迹,并将其从打开的套接字列表中删除。

我的代码如下所示:

#!/usr/bin/env python3

import select, socket

# Listen Port
LISTEN_PORT = 1234

# Create socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Setup the socket
server.setblocking(0)
server.bind(('0.0.0.0', LISTEN_PORT))
server.listen(5)

# Make socket reusable
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Setup TCP Keepalive
server.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3)
server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)

# Tell user we are listening
print("Listening on port %s" % LISTEN_PORT)

inputs = [server]
outputs = []

while True:
# Detecting clients that disappeared does NOT work when we ARE
# watching if any sockets are writable
#readable, writable, exceptional = select.select(inputs, outputs, inputs)

# Detecting clients that disappeared works when we aren't watching
# if any sockets are writable
readable, writable, exceptional = select.select(inputs, [], inputs)

for s in readable:
if s is server:
connection, client_address = s.accept()

print("New client connected: %s" % (client_address,))

connection.setblocking(0)

inputs.append(connection)
outputs.append(connection)
else:
try:
data = s.recv(1024)
except TimeoutError:
print("Client dropped out")

inputs.remove(s)

if s in outputs:
outputs.remove(s)
continue

if data:
print("Data from %s: %s" % (s.getpeername(), data.decode('ascii').rstrip()))
else:
print("%s disconnected" % (s.getpeername(),))

for s in writable:
s.send(b".")

如您所见,我正在使用TCP Keepalive来查看客户端是否消失。我看到的问题是这样的:
  • 当我没有select()监视可写套接字时,当客户端消失时,select()将在TCP Keepalive超时到期后停止阻塞,并且该套接字将在readable列表中,因此我可以删除该客户端从inputoutput中消失了(这很好)
  • 当我有select()监视可写套接字时,当客户端消失时,select()将不会在TCP Keepalive超时到期后停止阻止,并且客户端套接字永远不会出现在readablewritable列表中,因此它永远不会删除了

  • 我正在使用另一台计算机上的telnet作为客户端。为了复制消失的客户端,我正在使用iptables阻止客户端在客户端连接时与服务器通信。

    有人知道发生了什么吗?

    最佳答案

    正如您对问题的评论所提到的那样,TCP_KEEPALIVE东西对您的用例没有任何影响。 TCP_KEEPALIVE是一种用于在程序的TCP连接另一端的对等点在其他情况下处于空闲的TCP连接消失时通知程序的机制。由于您是定期在TCP连接上发送数据,因此永远不会调用(或不需要)TCP_KEEPALIVE功能,因为通过连接发送数据的行为本身已经足以使TCP堆栈在以下情况下识别ASAP:远程客户端已消失。

    也就是说,我修改/简化了示例服务器代码,以使其(尽可能正确)在我的机器(Mac,FWIW)上正常工作。我所做的是:

  • socket.setsockopt(SO_REUSEADDR)移动到bind()行的前面,这样在杀死并重新启动程序后bind()不会失败。
  • 更改了select()调用以监视可写套接字。
  • 添加了有关send()调用的异常处理。
  • 将remove-socket-from-lists代码移至单独的RemoveSocketFromLists()函数中,以避免冗余代码

  • 请注意,TCP的预期行为是:如果您轻轻地退出客户端(例如,通过控制C将其退出,或者通过任务管理器将其杀死,或者以其他方式导致其退出,以使其主机TCP堆栈仍然能够退出与服务器进行通信以告知服务器该客户端已死),则该服务器应立即或多或少地识别出该死亡的客户端。

    另一方面,如果客户端的网络连接突然断开(例如,由于有人拉出了客户端计算机的以太网或电源线),则服务器程序可能需要几分钟才能检测到客户端已消失,这是预期的行为,因为服务器无法(在这种情况下)告诉客户端是否死亡。 (即,它不希望仅仅因为路由器丢弃了一些TCP数据包而导致与尚处于 Activity 状态的客户端之间的通信临时中断而终止了可行的TCP连接)

    如果您想在这种情况下快速删除客户端,则可以尝试要求客户端每隔一秒钟左右的时间对服务器向数据库 send()一点伪数据。服务器可以跟踪它最后一次从每个客户端接收到任何数据的时间的时间戳,并强制关闭任何尚未接收到来自任何客户端的数据的客户端(时间太长)(无论您认为太长了如何)。如果您将超时阈值设置得太低,尽管可能会带来误报(例如,丢弃仍在运行中的客户端,只是速度较慢或遭受数据包丢失的攻击),但这或多或少会起作用。
    #!/usr/bin/env python3

    import select, socket

    # Listen Port
    LISTEN_PORT = 1234

    # Create socket
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # Make socket reusable
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # Setup the socket
    server.setblocking(0)
    server.bind(('0.0.0.0', LISTEN_PORT))
    server.listen(5)

    # Tell user we are listening
    print("Listening on port %s" % LISTEN_PORT)

    inputs = [server]
    outputs = []

    # Removes the specified socket from every list in the list-of-lists
    def RemoveSocketFromLists(s, listOfLists):
    for nextList in listOfLists:
    if s in nextList:
    nextList.remove(s)

    while True:
    # Detecting clients that disappeared does NOT work when we ARE
    # watching if any sockets are writable
    readable, writable, exceptional = select.select(inputs, outputs, [])

    for s in readable:
    if s is server:
    connection, client_address = s.accept()

    print("New client connected: %s" % (client_address,))
    connection.setblocking(0)
    inputs.append(connection)
    outputs.append(connection)
    else:
    try:
    data = s.recv(1024)
    print("Data from %s: %s" % (s.getpeername(), data.decode('ascii').rstrip()))
    except:
    print("recv() reports that %s disconnected" % s)
    RemoveSocketFromLists(s, [inputs, outputs, writable])

    for s in writable:
    try:
    numBytesSent = s.send(b".")
    except:
    print("send() reports that %s disconnected" % s)
    RemoveSocketFromLists(s, [inputs, outputs])

    关于python-3.x - 在Python套接字上使用select()和TCP Keepalive的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54430516/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com