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

C# TCP/IP Socket连接打印系统运行1小时后崩溃问题排查

C# Socket通信客户端连接打印系统

问题概述

通过TCP/IP Socket与打印系统通信,采用每次循环新建连接→发送数据→关闭连接的流程,运行约1小时后程序直接崩溃,添加try-catch块后问题依旧。关闭连接时执行以下代码:

sender.Shutdown(SocketShutdown.Both);
sender.Close();

崩溃日志

本地测试(随机报错DLL)

Faulting application name: PrintServerClient.exe, version: 1.0.0.0, time stamp: 0x8782029c
Faulting module name: clr.dll, version: 4.8.4614.0, time stamp: 0x63995bc5
Exception code: 0xc0000005
Fault offset: 0x0000000000682e80
Faulting process id: 0x73c8
Faulting application start time: 0x01d98d81d24fc9a1
Faulting application path: C:\Users\username\source\repos\PrintServerClient\bin\Debug\net45\PrintServerClient.exe
Faulting module path: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
Report Id: dfec97dc-0bbc-41ea-a9c4-b861460033bf
Faulting package full name: 
Faulting package-relative application ID: 

Windows 10 1909虚拟机测试(固定报错DNSAPI.dll)

Faulting application name: PrintServerClient.exe, version: 1.0.0.0, time stamp: 0x8bd9b096
Faulting module name: DNSAPI.dll, version: 10.0.18362.267, time stamp: 0x997f6419
Exception code: 0xc00000fd
Fault offset: 0x0000000000009104
Faulting process id: 0x1f40
Faulting application start time: 0x01d98d85b37e0c32
Faulting application path: C:\BELLMARK\PrintServerClient.exe
Faulting module path: C:\Windows\SYSTEM32\DNSAPI.dll
Report Id: 9063cd93-208a-4bea-90b6-a635fd439ed2
Faulting package full name: 
Faulting package-relative application ID: 

客户端代码片段

private void StartClient(byte[] PrintMsg)
{
    byte[] bytes = new byte[32];
    DateTime currentDateTime = DateTime.Now;

    if (ButtonStart.Visibility == Visibility.Visible)
    {
        try
        {
            // Connect to a Remote server
            // Get Host IP Address that is used to establish a connection
            // In this case, we get one IP address of localhost that is IP : 127.0.0.1
            // If a host has multiple addresses, you will get a list of addresses
            IPHostEntry host = Dns.GetHostEntry("localhost");
            IPAddress ipAddress = host.AddressList[1];
            IPEndPoint remoteEP = new IPEndPoint(ipAddress, 4500);
            Socket sender = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);


            // Connect the socket to the remote endpoint. Catch any errors.
            try
            {
                // Connect to Remote EndPoint
                sender.Connect(remoteEP);

                //Console.WriteLine("Socket connected to {0}", sender.RemoteEndPoint.ToString());

                // Encode the data string into a byte array.
                //byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>");
                //byte[] msg = Encoding.ASCII.GetBytes("<STX>#<ETX>");
                byte[] STOP_PRINT = { 02, 17, 03 };


                // Send the data through the socket.
                int bytesSent = sender.Send(STOP_PRINT);
            

                // Receive the response from the remote device.
                int bytesRec = sender.Receive(bytes);
                //Console.WriteLine("Echoed test = {0}", Encoding.ASCII.GetString(bytes, 0, bytesRec));

                // Release the socket.
                try
                {
                    sender.Shutdown(SocketShutdown.Both);
                    sender.Close();
                }
                catch
                {

                }
            }
            catch (ArgumentNullException ane)
            {
                //Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
            }
            catch (SocketException se)
            {
                //Console.WriteLine("SocketException : {0}", se.ToString());
            }
            catch (Exception e)
            {
                //Console.WriteLine("Unexpected exception : {0}", e.ToString());
            }
        }
        catch (Exception e)
        {
            //Console.WriteLine(e.ToString());
        }
    }
    else if (ButtonStart.Visibility == Visibility.Hidden)
    {
        try
        {
            // Connect to a Remote server
            // Get Host IP Address that is used to establish a connection
            // In this case, we get one IP address of localhost that is IP : 127.0.0.1
            // If a host has multiple addresses, you will get a list of addresses
            IPHostEntry host = Dns.GetHostEntry("localhost");
            IPAddress ipAddress = host.AddressList[1];
            IPEndPoint remoteEP = new IPEndPoint(ipAddress, 4500);


            // Create a TCP/IP  socket.
            Socket sender = new Socket(ipAddress.AddressFamily,
                SocketType.Stream, ProtocolType.Tcp);


            // Connect the socket to the remote endpoint. Catch any errors.
            try
            {
                // Connect to Remote EndPoint
                sender.Connect(remoteEP);

                //Console.WriteLine("Socket connected to {0}", sender.RemoteEndPoint.ToString());

                // Send the data through the socket.
                int bytesSent = sender.Send(PrintMsg);

                // Receive the response from the remote device.
                int bytesRec = sender.Receive(bytes);
                //Console.WriteLine("Echoed test = {0}",
                //    Encoding.ASCII.GetString(bytes, 0, bytesRec));
                

                string hex = BitConverter.ToString(bytes);
                string hexString = hex.Replace("-", "");

                string byte1 = hexString.Substring(0, 2);
                string byte2 = hexString.Substring(2, 2);
                string byte3 = hexString.Substring(4, 2);
                string byte4 = hexString.Substring(6, 2);

                try
                {
                    sender.Shutdown(SocketShutdown.Both);
                    sender.Close();

                    if (byte3 == "06")
                    {
                        InternalPrintCounter++;
                        string ReturnMessage = byte1 + " " + byte2 + " " + byte3 + " " + byte4 + "--> ACK - " + currentDateTime;
                        if (InternalPrintCounter > 5)
                        {
                            InternalPrintCounter = 0;
                            DynamicDataQueueCount(ReturnMessage);
                        }
                        else
                        {
                            SerialNumberGenerator(ReturnMessage);
                        }
                    }
                    else if (byte3 == "15")
                    {

                    }
                }
                catch
                {

                }
                
            }
            catch (ArgumentNullException ane)
            {
                //Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
            }
            catch (SocketException se)
            {
                //Console.WriteLine("SocketException : {0}", se.ToString());
            }
            catch (Exception e)
            {
                //Console.WriteLine("Unexpected exception : {0}", e.ToString());
            }
        }
        catch (Exception e)
        {
            //Console.WriteLine(e.ToString());
        }
    }

}

问题原因分析

  1. 资源泄漏:Socket未被正确释放。手动调用Close()和Shutdown()可能因异常被跳过,且未实现IDisposable的正确释放逻辑,导致系统句柄或内存泄漏,长时间运行后触发崩溃。
  2. DNS查询开销与错误:每次循环调用Dns.GetHostEntry("localhost"),频繁的DNS查询会消耗系统资源;硬编码host.AddressList[1]存在风险,若主机地址列表长度不足2会引发索引越界,空catch块吞掉了这类错误,导致问题隐藏。
  3. 空catch块吞噬异常:多处空catch块会掩盖连接、关闭过程中的错误,无法及时排查潜在问题,异常积累后引发崩溃。
  4. Socket接收逻辑不完整:sender.Receive(bytes)仅接收一次数据,TCP是流协议,可能存在单次接收不完整的情况,残留数据或错误状态未处理,影响后续连接。
  5. 频繁创建连接:每次循环新建Socket连接,会频繁占用和释放网络资源,增加系统负载,长时间运行后触发资源耗尽类崩溃。

修复建议

1. 使用using语句管理Socket资源

Socket实现了IDisposable接口,使用using可自动释放资源,替代手动Close()/Shutdown(),确保资源无论是否异常都能被正确回收:

using (Socket sender = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
{
    sender.Connect(remoteEP);
    // 发送、接收逻辑
    sender.Shutdown(SocketShutdown.Both); // 可选,确保数据传输完成
} // 自动调用Dispose(),释放Socket资源

2. 优化IP地址获取逻辑

避免频繁DNS查询,直接使用IPAddress.Loopback(对应127.0.0.1)代替Dns.GetHostEntry("localhost"),减少开销并避免索引错误:

IPAddress ipAddress = IPAddress.Loopback;
IPEndPoint remoteEP = new IPEndPoint(ipAddress, 4500);

若需解析远程主机,需判断AddressList长度后再选择,而非硬编码索引。

3. 移除空catch块,添加异常日志

空catch块会隐藏错误,至少记录异常信息,便于排查问题:

catch (Exception ex)
{
    // 写入日志文件或输出到调试窗口
    System.Diagnostics.Debug.WriteLine($"Socket操作异常: {ex.ToString()}");
}

4. 实现完整的Socket接收逻辑

TCP是流协议,需循环接收直到获取完整响应数据,示例如下:

int totalBytesReceived = 0;
int expectedBytes = 4; // 根据协议预期的响应长度
while (totalBytesReceived < expectedBytes)
{
    int bytesRec = sender.Receive(bytes, totalBytesReceived, expectedBytes - totalBytesReceived, SocketFlags.None);
    if (bytesRec == 0)
    {
        throw new SocketException((int)SocketError.ConnectionReset);
    }
    totalBytesReceived += bytesRec;
}
// 处理完整的响应数据

5. 复用Socket连接(可选)

若打印系统支持长连接,可复用同一Socket连接,避免频繁创建/销毁连接,减少资源消耗:

  • 初始化时创建一次Socket连接,保持连接状态
  • 循环发送数据时直接使用该连接,仅在连接断开时重新建立

6. 排查资源泄漏

使用Visual Studio诊断工具(诊断工具→内存/CPU)监控程序运行时的句柄、内存使用情况,确认是否存在资源泄漏;也可使用Windows的Resource Monitor查看进程的句柄计数变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 11:07:22