You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python线程中TCP连接建立失败,Mac/Linux报ConnectionRefusedError

多线程TCP聊天程序:Mac/Linux下ConnectionRefusedError问题排查

问题场景

编写支持多客户端的Python聊天程序时,使用_thread模块实现多客户端连接,但线程内建立TCP连接时,客户端的tcp_socket.connect((IP, TCP_PORT))抛出ConnectionRefusedError: [Errno 61] Connection refused错误。

  • 不使用线程时代码正常运行,排除端口本身问题
  • 相同代码在Windows运行正常,仅在Mac/Linux出现错误

简化代码

server.py

from _thread import *
import socket

def createClientConnection(client_id, client_address):
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket.bind((IP, TCP_PORT))
    tcp_socket.listen()
    client_socket, address = tcp_socket.accept() # 此处未执行,线程直接结束
    print("\n* TCP socket bound to %s\n" %(TCP_PORT))
    # 后续通信逻辑...

IP = '127.0.0.1'
TCP_PORT = 1234

# 启动线程处理连接
start_new_thread(createClientConnection, (client_id, client_address))
# 如果在主线程执行TCP连接逻辑,一切正常

client.py

import socket

IP = '127.0.0.1'
TCP_PORT = 1234

tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.connect((IP, TCP_PORT))  # 此处抛出Errno 61错误
print("\n* TCP socket created\n")

问题根源

  1. 错误的多线程逻辑:你在每个线程中重复创建、绑定并监听同一个端口TCP_PORT。在Unix-like系统(Mac/Linux)中,默认不允许多个socket绑定到同一端口,第一个线程绑定端口后,后续线程的bind操作会静默失败(未做错误捕获),导致线程直接退出,没有实际的监听socket在运行,客户端自然无法连接。
  2. Windows与Unix-like的socket差异:Windows默认允许端口复用(默认开启SO_REUSEADDR),多个线程的bind操作不会失败,因此代码能“正常”运行,但这本身是错误的逻辑——多个监听socket会导致连接被随机分配,造成资源浪费。

修正方案

正确的多客户端TCP服务端逻辑应该是:主线程只做一次绑定和监听,循环接受新连接,每收到一个连接就启动新线程处理该连接的通信。

修正后的server.py代码

from _thread import *
import socket

def handle_client(client_socket, client_address):
    # 处理与单个客户端的通信逻辑
    print(f"* 已连接客户端: {client_address}")
    # 示例:接收客户端消息并回显
    while True:
        data = client_socket.recv(1024)
        if not data:
            break
        client_socket.sendall(data)
    client_socket.close()
    print(f"* 客户端 {client_address} 断开连接")

IP = '127.0.0.1'
TCP_PORT = 1234

# 主线程创建监听socket,仅执行一次绑定和监听
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置SO_REUSEADDR,避免端口释放后短时间内无法复用
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((IP, TCP_PORT))
server_socket.listen(5)  # 设置等待连接的队列长度
print(f"* 服务端已启动,监听端口 {TCP_PORT}")

# 循环接受新客户端连接
while True:
    client_socket, client_address = server_socket.accept()
    # 启动新线程处理该客户端的通信
    start_new_thread(handle_client, (client_socket, client_address))

关键说明

  • 监听socket只需要在主线程创建一次,绑定并监听端口后,通过accept()循环等待新连接
  • 每个新连接对应一个线程,线程负责处理该连接的具体通信,无需再创建监听socket
  • 添加SO_REUSEADDR选项可以避免服务端重启时出现“端口已被占用”的错误,提升鲁棒性

内容的提问来源于stack exchange,提问作者Zia Kim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 11:09:55