ThreadedTCPServer 工作,除非客户端是一个单独的进程

ThreadedTCPServer works unless client is a separate process

我从 Python 文档 here 中改编了下面的代码。

# filename: example.py
from datetime import datetime
import socket
import socketserver
import sys
import threading
import time

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        data = str(self.request.recv(1024), 'ascii')
        cur_thread = threading.current_thread()
        response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
        self.request.sendall(response)

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    allow_reuse_address = True

def client(ip, port, message):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((ip, port))
        sock.sendall(bytes(message, 'ascii'))
        response = str(sock.recv(1024), 'ascii')
        print("Received: {}".format(response))

if __name__ == "__main__":
    HOST, PORT = "127.0.0.1", 9999
    if sys.argv[1] == "server":
        server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
        with server:
            ip, port = server.server_address
            print(f'{ip} {port}')
            server_thread = threading.Thread(target=server.serve_forever)
            server_thread.start()

            # these work
            client(ip, port, "Hello World 1 at " + str(datetime.now()))
            time.sleep(1)
            client(ip, port, "Hello World 2 at " + str(datetime.now()))
            time.sleep(2)
            client(ip, port, "Hello World 3 at " + str(datetime.now()))

    if sys.argv[1] == "client":
        # these do not work, why?
        client(HOST, PORT, "Hello World 1 at " + str(datetime.now()))
        time.sleep(1)
        client(HOST, PORT, "Hello World 2 at " + str(datetime.now()))
        time.sleep(2)
        client(HOST, PORT, "Hello World 3 at " + str(datetime.now()))

运行 此代码作为服务器按预期工作:

$ python3 example.py server
127.0.0.1 9999
Received: Thread-2: Hello World 1 at 2021-09-14 20:02:25.135218
Received: Thread-3: Hello World 2 at 2021-09-14 20:02:26.140889
Received: Thread-4: Hello World 3 at 2021-09-14 20:02:28.143664

我离开此终端选项卡 运行ning(程序正在等待其他客户端连接),打开一个新选项卡并 运行 以下内容,但它产生错误:

$ python3 example.py client
Traceback (most recent call last):
  File "/Users/ashroyer-admin/repo/phd-courses/2021-F-cloudcomp/thred.py", line 45, in <module>
    client(HOST, PORT, "Hello World 1 at " + str(datetime.now()))
  File "/Users/ashroyer-admin/repo/phd-courses/2021-F-cloudcomp/thred.py", line 20, in client
    sock.connect((ip, port))
ConnectionRefusedError: [Errno 61] Connection refused

ConnectionRefused 通常意味着没有服务器在指定的 IP/Port 监听(或者它的积压已满,这在这个例子中不太可能)。很可能,当您尝试连接其他终端客户端时,server 对象不再是 运行。仅仅因为您让服务器的终端保持打开状态并不意味着服务器本身仍在该终端中 运行。

服务器可能会在其主线程对 client() 进行 3 次调用后终止。您在另一个线程中 运行 serve_forever(),而不是在创建 server 并调用 client() 的主线程中。之后没有任何东西 - 没有循环,没有事件等 - 代码正在等待保留脚本 运行 因此 server_thread 可以继续为新客户提供服务。

with server: 块结束时,server 对象将关闭。当主线程完成时,脚本退出。

我的建议是:

  • 完全摆脱 server_thread 和对 client() 的 3 次调用。只需在 print(f'{ip} {port}') 之后调用 server.serve_forever(),然后使用其他终端测试您的 client() 代码。

  • 或者,至少删除 with server: 块。显然,“服务器线程不是守护进程,因此当主线程退出时它保持 运行。删除 with server: 并且它工作正常。”(谢谢
    @MarkTolonen
    )