You need to enable JavaScript to run this app. Python socket同机运行正常 跨电脑连接异常问题求助
优惠活动
大模型
产品
解决方案
定价
更多

Python socket同机运行正常 跨电脑连接异常问题求助

跨设备部署聊天程序连接失败排查

基础问题描述

  • 同设备本地运行服务端、客户端脚本时,程序运行完全正常
  • 跨设备分别部署服务端、客户端时,客户端发起连接请求会随机抛出以下错误:
    WinError[10061]: No connection could be made because the target machine actively refused it. 
    WinError[10051]: A socket operation was attempted to an unreachable network
    socket.timeout
    
  • 已完成的初步排查操作:关闭服务端设备防火墙、将服务端绑定地址替换为0.0.0.0或本机实际IPv4地址、查阅公开的同类问题资料,问题始终未解决
  • 特殊现象:以另一台设备作为服务端(保持其防火墙开启)、故障设备作为客户端时,整套系统可正常通信;反过来以故障设备作为服务端运行时,所有外部客户端均无法建立连接

现有网络层代码逻辑已校验可支撑基础通信能力,未展示的业务逻辑代码不影响网络连接层的问题定位

核心排查方向

  • 多网卡IP路由冲突:故障设备同时接入多个网络(有线局域网、WiFi、VPN虚拟网卡等)时,socket.gethostbyname(socket.gethostname())返回的主机解析IP,和外部设备可实际访问到的网卡IP不匹配。即使服务端绑定0.0.0.0,也可能因为系统路由优先级配置问题,导致外部发起的连接请求被直接丢弃
  • 防火墙残留规则拦截:Windows Defender防火墙临时关闭后,若之前曾将Python进程标记为“阻止所有入站连接”,对应规则不会随防火墙临时关闭自动失效。需要在防火墙入站规则中单独为Python解释器添加允许接收入站连接的权限
  • 网关端口映射缺失:跨网段连接服务端时,若未在路由器上配置5050端口的入站映射规则,外部发起的连接请求会直接被网关拦截,触发连接超时或网络不可达错误
  • 客户端配置错误:客户端填写的服务端IP为仅本机可访问的回环地址、或同网段不可达的内网IP时,会直接触发网络不可达、连接拒绝的报错

现有核心代码参考

服务端实现

import socket
import threading
import pickle

class Server:
    """Represents a server which clients can connect to and send messages to"""
    def __init__(self, port=5050, header=1048, server_pass="", format_="utf-8"):
        """
        :param port: int
        :param header: int
        :param format: str
        """
        self.PORT = port
        self.HEADER = header
        self.SERVER = socket.gethostbyname(socket.gethostname())
        self.ADDR = ("0.0.0.0", self.PORT) # 该地址已验证替换为本机实际IPv4地址也无法解决问题
        print (self.ADDR)
        self.server_password = server_pass
        self.FORMAT = format_
        self.DISCONNECT_MSG = "!DISCONNECT".encode(self.FORMAT)
        self.started = False
        self.lock = threading.Lock()

        # {"username": [conn, messages]}
        self.clients = {}
        # ordered messages
        self.messages = []
        self.banned_ips = []

        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def bind_server(self):
        """
        Attempts to bind the server to self.ADDR, returns true if binding wsa successful
        :returns: bool
        """
        try:
            self.server.bind(self.ADDR)
            return True

        except:
            return False

    def start(self):
        """
        Starts the server
        :returns: None
        """
        self.started = True

        try:
            self.server.listen(5)

        except:
            return None

        print ("server has started")

        # Wait for any incoming connections, and handle that connection
        while True:
            try:
                conn, addr = self.server.accept()

            except OSError as e:
                print (f"OSError: {str(e)}")
                break

            # 客户端连接建立前,该分支逻辑不会被触发
            client_thread = threading.Thread(target=self.handle_client, args=(conn, addr))
            client_thread.setDaemon(True)
            client_thread.start()

        self.close()

    def handle_client(self, conn, addr):
        print (f"connection from {str(addr)}")
        # 客户端消息处理逻辑未完全展示

    def close(self):
        print ("server closed")
        self.server.close()

if __name__ == "__main__":
    server = Server()
    bound = server.bind_server()
    print (bound)
    server.start()

客户端实现

import socket
import threading
import pickle
from _pickle import UnpicklingError

class Client:
    """Represents a client which can join and send messages to a Server"""
    def __init__(self, username, server_ip, port=5050, header=1048, format_="utf-8", server_pass=""):
        """
        :param port: int
        :param header: int
        :param format_: str
        """
        self.PORT = port
        self.HEADER = header
        self.FORMAT = format_
        self.SERVER = server_ip
        self.DISCONNECT_MSG = "!DISCONNECT"

        self.server_password = server_pass
        self.ADDR = (self.SERVER, self.PORT)
        self.username = username
        self.connected = False

        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.clients = {}
        self.messages = []

    def join_server(self):
        """
        Connects the client to the server
        :returns: bool
        """

        self.client.settimeout(20)

        try:
            self.client.connect(self.ADDR) # 所有连接错误均在该行抛出

        except ConnectionRefusedError as e:
            return self.connected

        except socket.timeout:
            print ("timed out :/")
            return self.connected

        except WindowsError as e:
            if e.winerror == 10060:
                print ("couldn't connect to server because it took too long to respond or it has failed to respond")
            elif e.winerror == 10051:
                print ("network is unreachable")

            return self.connected

        else:
            self.client.settimeout(None)

        # 连接建立后启动消息接收线程
        receive_clients_thread = threading.Thread(target=self.receive_clients)
        receive_clients_thread.start()

        self.connected = True
        self.send_message(self.username)
        self.send_message(self.server_password)

        return self.connected

    def receive_clients(self):
        # 消息接收逻辑未完全展示
        pass

if __name__ == "__main__":
    username = input("Username: ")
    server_pass = input("Server password: ")
    server_ip = "IPV4" # 服务端IPv4地址需填写在该位置

    client = Client(username, server_ip, server_pass=server_pass)
    client.join_server()

    while client.connected:
        message = input(f"{client.username}: ")
        client.send_message(message)

        if message == "q":
            break

    print ("not connected")
    if client.connected:
        client.close()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 19:54:06