健壮的连续 TCP 连接(python 套接字)

Posted

技术标签:

【中文标题】健壮的连续 TCP 连接(python 套接字)【英文标题】:robust continuous TCP connection (python socket) 【发布时间】:2020-06-02 09:51:19 【问题描述】:

我的目标是在一台服务器和一个客户端之间建立一个连续且健壮的 TCP 连接。如果一侧出现故障,另一侧应等待恢复。

我根据this question(只要求连续的,但不是健壮的TCP连接,不处理keepalive问题)、this post和我自己的经验编写了以下代码。

我有两个问题:

    如何使 keepalive 工作?如果服务器死了,客户端只有在尝试send() 后才能识别它——这在没有KEEPALIVE 选项的情况下也可以工作,因为这会导致连接重置。是否有某种方式可以让套接字为已死的连接或我可以定期检查的某些 keepalive 函数发送中断?

    这是处理连续 TCP 连接的可靠方法吗?拥有稳定、连续的 TCP 连接似乎是一个标准问题,但是,我找不到详细介绍此问题的教程。必须有一些最佳实践

注意,我可以在应用程序级别自行处理保持活动消息。但是,由于 TCP 已经在传输层实现了这一点,因此最好依赖较低层提供的此服务。

服务器:

from socket import *
serverPort = 12000

while True:
    # 1. Configure server socket
    serverSocket = socket(AF_INET, SOCK_STREAM)
    serverSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    serverSocket.bind(('127.0.0.1', serverPort))
    serverSocket.listen(1)
    print("waiting for client connecting...")
    connectionSocket, addr = serverSocket.accept()
    connectionSocket.setsockopt(SOL_SOCKET, SO_KEEPALIVE,1)
    print(connectionSocket.getsockopt(SOL_SOCKET,SO_KEEPALIVE))
    print("...connected.")
    serverSocket.close() # Destroy the server socket; we don't need it anymore since we are not accepting any connections beyond this point.

    # 2. communication routine
    while True:
        try:
            sentence = connectionSocket.recv(512).decode()
        except ConnectionResetError as e:
            print("Client connection closed")
            break
        if(len(sentence)==0): # close if client closed connection
            break 
        else:
            print("recv: "+str(sentence))

    # 3. proper closure
    connectionSocket.shutdown(SHUT_RDWR)
    connectionSocket.close()
    print("connection closed.")

客户:

from socket import *
import time

while True:
    # 1. configure socket dest.
    serverName = '127.0.0.1'
    serverPort = 12000
    clientSocket = socket(AF_INET, SOCK_STREAM)
    try:
        clientSocket.setsockopt(SOL_SOCKET, SO_KEEPALIVE,1)
        clientSocket.connect((serverName, serverPort))
        print(clientSocket.getsockopt(SOL_SOCKET,SO_KEEPALIVE))
    except ConnectionRefusedError as e:
        print("Server refused connection. retrying")
        time.sleep(1)
        continue

    # 2. communication routine
    while(1):
        sentence = input('input sentence: ')
        if(sentence == "close"):
            break
        try:
            clientSocket.send(sentence.encode())
        except ConnectionResetError as e:
            print("Server connection closed")
            break

    # 3. proper closure
    clientSocket.shutdown(SHUT_RDWR)
    clientSocket.close()

我尽量减少这个例子。但是考虑到鲁棒性的要求,它是相对较长的。

我还尝试了一些套接字选项,例如 TCP_KEEPIDLETCP_KEEPINTVLTCP_KEEPCNT

谢谢!

【问题讨论】:

【参考方案1】:

我会尽量回答这两个问题。

    ... 是否有某种方式可以让套接字为已死的连接发送中断...

    我不知道。 TCP_KEEPALIVE 仅尝试维持连接。如果网络流上的任何设备出现超时,这将非常有用,因为它可以防止超时中止连接。但是如果连接由于任何其他原因(超时)而断开,TCP_KEEPALIVE 将无法执行任何操作。理由是在交换某些东西之前没有必要恢复断开的非活动连接。

    这是处理连续 TCP 连接的可靠方法吗?

    不是真的。

    稳健的方法是随时准备连接因任何原因失败。因此,您应该准备好在发送消息时遇到错误(您的代码是),如果发生这种情况,请尝试重新打开连接并再次发送消息(您当前的代码没有)。比如:

    def connect(...):
        # establish and return a connection
        ...
        return clientSocket
    
    clientSocket = connect(...)
    while True:
        ...
        while True:
            try:
                clientSocket.send(message)
                break
            except OSError:
                clientSocket = connect()
        ...
    

无关:您的正常关机不正确。发起者(使用shutdown的部分)不应该立即关闭套接字,而是启动一个读取循环,并且只有在接收并处理完所有内容后才关闭。

【讨论】:

【参考方案2】:

我怎样才能使keepalive工作?如果服务器死了,客户端只有在尝试 send() 后才能识别它 - 这在没有 KEEPALIVE 选项的情况下也可以工作,因为这会导致连接重置。

Keepalive 在服务器端或读取端更有用。这是一个狡猾的野兽。除非您读/写,否则套接字根本不会通知您。您可以查询它的状态(尽管我不确定这对于标准 Python 是否可行),但这仍然不能解决通知问题。无论如何,您需要定期检查状态。

套接字是否可以通过某种方式为已死的连接或某些我可以定期检查的 keepalive 函数发送中断?

你听说过the Two Generals' Problem吗?没有可靠的方法来检测一侧是否死亡。但是,我们可以在 ping 和超时方面足够接近。

请注意,我可以在应用程序级别自行处理保持活动消息。但是,由于 TCP 已经在传输层实现了这一点,因此最好依赖较低层提供的此服务。

不,不是更好。如果出于某种原因,在服务器和客户端之间存在代理,那么 TCP 功能将无法帮助您。因为按照设计,它们只控制一个连接,而使用代理你至少有两个连接。您不应该根据底层传输 (TCP) 来考虑您的连接。而是使用服务器(或客户端或两者)定期发送的 ping 命令创建您自己的协议以及超时。通过这种方式,您可以确保对等点在周期间隔内处于活动状态。

这是处理连续 TCP 连接的可靠方法吗?拥有稳定、连续的 TCP 连接似乎是一个标准问题,但是,我找不到详细介绍此问题的教程。必须有一些最佳实践。

您不会找到涵盖此问题的教程,因为该问题没有解决方案。大多数人通过 ping 和超时来模拟“我还活着”。

【讨论】:

以上是关于健壮的连续 TCP 连接(python 套接字)的主要内容,如果未能解决你的问题,请参考以下文章

Python学习之——Socket套接字(TCP连接)

python中的TCP及UDP

如何在 python asyncio 中处理 tcp 客户端套接字自动重新连接?

TCP over Bluetooth(Windows 套接字)

python网络连接之tcp通信

Python-TCP服务端程序开发