C# UDP转TCP程序报错10054:远程主机强制关闭现有连接
问题背景
我编写了一款简易数据转发程序,核心逻辑为监听指定UDP端口接收数据,过滤掉首字节为0x02的数据包后,将其余数据转发至目标TCP地址。在CMD中启动程序并传入监听端口、目标TCP地址两个参数时,程序运行抛出未处理异常。
异常信息:
Unhandled exception. System.Net.Sockets.SocketException (10054): An existing connection was forcibly closed by the remote host.
(中文释义:未处理异常。System.Net.Sockets.SocketException (10054):远程主机强迫关闭了一个现有的连接)
异常堆栈指向程序Program.Main(String[] args)方法,原实现代码如下:
static void Main(string[] args) { IPEndPoint listening = new IPEndPoint(IPAddress.Any, int.Parse(args[0])); UdpClient udp = new UdpClient(listening); System.Console.WriteLine($"Listening on {listening}"); var parts = args[1].Split(':'); IPEndPoint newrelic = new IPEndPoint(IPAddress.Parse(parts[0]), int.Parse(parts[1])); TcpClient tcp = new TcpClient(); bool stop=false; Console.CancelKeyPress+=(object? sender, ConsoleCancelEventArgs e) => { stop = true; tcp.Dispose(); udp.Close(); udp.Dispose(); }; tcp.Connect(newrelic); System.Console.WriteLine($"Connected to {newrelic}"); var dummy = new IPEndPoint(IPAddress.Any,0); while (!stop) { var bytes = udp.Receive(ref dummy); if (bytes.Length > 0) { if (bytes[0] != 0x02) { System.Console.WriteLine($"Received {bytes.Length} bytes"); var sent = tcp.Client.Send(bytes); } } } }
异常产生原因
- 核心原因是TCP连接可靠性处理缺失:程序仅在启动时建立一次TCP连接,后续全程复用该连接转发数据。一旦出现目标TCP服务重启、网络闪断、服务端空闲超时主动断开连接、中间链路丢包导致连接失效等情况,程序不会主动感知到连接已断开,调用
Send方法发送数据时就会触发10054错误,且代码没有任何异常捕获逻辑,错误直接抛出导致程序崩溃。 - 隐藏触发点:Windows平台下UDP套接字默认会将收到的ICMP端口不可达报文转换为SocketException抛出,UDP本身是无连接协议,这类错误和业务逻辑无关,但原代码没有做屏蔽处理,也可能在UDP接收环节触发同类10054异常。
- 退出逻辑存在竞态:触发Ctrl+C退出时,事件处理中直接释放TCP、UDP对象,但此时收发循环可能刚好执行到发送/接收逻辑,调用已释放的套接字对象也会触发同类异常。
修复方案
针对上述问题做以下调整即可解决:
- 关闭UDP套接字的ICMP错误抛出,屏蔽UDP层面的无关10054错误
- 增加TCP连接状态检测逻辑,连接断开时自动重试建立连接,不需要人工重启程序
- 为收发逻辑增加异常捕获,遇到非退出类错误时记录日志后继续运行,不直接崩溃
- 调整退出逻辑,触发退出信号后先终止循环,再统一释放资源,避免竞态问题
修复后的完整代码:
static void Main(string[] args) { int listenPort = int.Parse(args[0]); IPEndPoint listening = new IPEndPoint(IPAddress.Any, listenPort); UdpClient udp = new UdpClient(listening); // 屏蔽UDP层面的ICMP端口不可达错误 const int SIO_UDP_CONNRESET = -1744830452; udp.Client.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); Console.WriteLine($"Listening on {listening}"); var parts = args[1].Split(':'); IPEndPoint targetEndpoint = new IPEndPoint(IPAddress.Parse(parts[0]), int.Parse(parts[1])); TcpClient tcp = new TcpClient(); bool stop = false; Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => { e.Cancel = true; stop = true; }; // 封装TCP自动重连逻辑 void EnsureTcpConnected() { if (tcp.Connected) return; tcp.Dispose(); tcp = new TcpClient(); while (!stop && !tcp.Connected) { try { tcp.Connect(targetEndpoint); Console.WriteLine($"Connected to target {targetEndpoint}"); } catch (Exception ex) { Console.WriteLine($"Connect to target failed, retry in 3s: {ex.Message}"); Thread.Sleep(3000); } } } try { EnsureTcpConnected(); var remoteUdpEp = new IPEndPoint(IPAddress.Any, 0); while (!stop) { try { var bytes = udp.Receive(ref remoteUdpEp); if (bytes.Length == 0 || bytes[0] == 0x02) continue; EnsureTcpConnected(); if (!tcp.Connected) continue; Console.WriteLine($"Received {bytes.Length} bytes, forwarding to target"); tcp.Client.Send(bytes); } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset || ex.SocketErrorCode == SocketError.Disconnecting) { Console.WriteLine($"TCP connection lost, will reconnect: {ex.Message}"); tcp.Close(); } catch (Exception ex) { if (!stop) Console.WriteLine($"Forward error: {ex.Message}"); } } } finally { // 循环退出后统一释放资源 udp.Dispose(); tcp.Dispose(); Console.WriteLine("Program exited."); } }
内容的提问来源于stack exchange,提问作者axelekenberg
相关产品推荐
相关产品推荐

