首页 > 解决方案 > ThreadedTCPServer 工作,除非客户端是一个单独的进程

问题描述

我从这里的 Python 文档改编了下面的代码。

# 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

我让这个终端选项卡保持运行(程序正在等待其他客户端连接),打开一个新选项卡并运行以下命令,但它会产生错误:

$ 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

标签: pythonsocketspython-multithreadingsocketserver

解决方案


ConnectionRefused通常意味着在指定的 IP/端口上没有服务器正在侦听(或者它的 backlog 已满,这在本示例中不太可能发生)。很有可能,当您尝试连接其他终端客户端时,该server对象根本不再运行。仅仅因为您将服务器的终端保持打开状态并不意味着服务器本身仍在该终端中运行。

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

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

我的建议是:

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

  • 或者,至少删除with server:块。显然,“服务器线程不是守护进程,因此当主线程退出时它会继续运行。删除with server:它就可以正常工作。 ”(感谢
    @MarkTolonen


推荐阅读