C#异步客户端Socket连接被目标机器主动拒绝问题排查
问题背景
本地Windows机器(127.0.0.1)运行Python编写的TCP服务器,监听端口10200,已通过telnet等工具确认服务正常(无防火墙拦截)。但Unity环境下的C#异步TCP客户端连接时,调用EndConnect方法抛出异常:
System.Net.Sockets.SocketException (0x80004005): No connection could be made because the target machine actively refused it.
客户端核心代码片段:
public void Connect (string host, int port) { try { IPHostEntry ipHostInfo = Dns.GetHostEntry (host); IPAddress ipAddress = ipHostInfo.AddressList[0]; IPEndPoint remoteEP = new IPEndPoint (ipAddress, port); state.socket = new Socket (ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); state.socket.ReceiveTimeout = TcpState.RecvTimeout; state.socket.BeginConnect (remoteEP, new AsyncCallback (ConnectCallback), state); } catch (Exception e) { Debug.Log (e.ToString ()); Disconnect (); } } // ... private void ConnectCallback (IAsyncResult ar) { try { TcpState state = (TcpState) ar.AsyncState; state.socket.EndConnect (ar); // 异常抛出位置 // ... }
注:state为封装的异步操作状态对象,Wireshark抓包显示会话不完整(flag 37),且相同代码在Linux环境下运行正常。
可能的原因
DNS解析返回IP地址不匹配服务器监听地址
Windows下localhost会同时解析出IPv6(::1)和IPv4(127.0.0.1)地址,Dns.GetHostEntry返回的列表中IPv6地址可能排在首位。如果Python服务器仅监听IPv4的127.0.0.1,客户端用IPv6地址连接时会被直接拒绝。可以直接指定IPAddress.Loopback(对应IPv4回环地址),或修改Python服务器使其同时监听IPv4和IPv6。Socket地址族不兼容
若客户端通过DNS解析拿到IPv6地址,创建的Socket为AddressFamily.InterNetworkV6类型,而Python服务器仅监听IPv4端口,跨地址族的连接请求会被服务器主动拒绝(Wireshark抓到的flag 37包含RST标志,正是服务器拒绝连接的信号)。可强制客户端创建IPv4类型的Socket(指定AddressFamily.InterNetwork),或修改Python服务器开启双栈监听(使用socket.AF_INET6并设置IPV6_V6ONLY为False)。Unity平台网络权限配置问题
Windows平台的Unity应用若未正确配置网络权限,可能限制回环地址的连接。检查Player Settings中的Internet Access选项是否设置为Require或Allow;同时尝试直接传入127.0.0.1作为连接地址,绕过DNS解析环节。异步状态对象被意外篡改
若state对象在BeginConnect到ConnectCallback的异步过程中被其他线程修改、释放或重新赋值,调用EndConnect时会触发异常。需确保state对象在异步操作完成前保持稳定,无外部线程的非法修改。Windows与Linux TCP栈行为差异
两者TCP栈对回环连接、SYN包响应的处理逻辑存在细节差异。比如Windows下对跨地址族的连接请求会直接返回RST,而Linux可能做了兼容处理,这也是代码在Linux下正常、Windows下报错的原因之一。
内容的提问来源于stack exchange,提问作者pion3k

