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
相关产品推荐
相关产品推荐

