0

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

我基于这个问题(只要求连续但不健壮的 TCP 连接并且不处理保活问题)、这篇文章和我自己的经验编写了以下代码。

我有两个问题:

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

  2. 这是处理连续 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_KEEPIDLE,TCP_KEEPINTVLTCP_KEEPCNT.

谢谢!

4

2 回答 2

1

我将尝试回答这两个问题。

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

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

  2. 这是处理连续 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)不应立即关闭套接字,而是启动读取循环,并且仅在接收并处理完所有内容后才关闭。

于 2020-06-02T10:26:46.800 回答
0

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

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

是否有某种方法可以让套接字为已死的连接或我可以定期检查的某些保活功能发送中断?

你听说过两将问题吗?没有可靠的方法来检测一侧是否死亡。然而,我们可以在 ping 和超时方面足够接近。

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

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

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

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

于 2020-06-02T10:14:25.277 回答