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

C# Windows服务串口通信运行30分钟后异常停止求助

Windows服务串口处理30分钟后停止响应问题排查与解决

我开发了一个Windows服务,功能是从串口读取来电号码并返回数据库数据,运行初期完全符合预期,但精确运行30分钟后会彻底停止处理数据。服务进程仍保持运行状态,但不再执行串口的读写操作,且串口错误接收事件从未触发。相关代码如下:

protected override void OnStart(string[] args)
{
    string fullpath = "C:/debug/debug.txt";
    File.AppendAllText(fullpath, "Started: " + Convert.ToString(DateTime.Now) + Environment.NewLine);
    File.AppendAllText(fullpath, "Machine Name:" + Environment.MachineName + Environment.NewLine);

    var worker = new Thread(mainevent);
    worker.IsBackground = false;
    worker.Start();

}
private void mainevent()
{

    SerialPort MyCOMPort = new SerialPort();
    string COM_PortName = "COM2";
    int BaudRate = 9600;
    MyCOMPort.PortName = COM_PortName;
    MyCOMPort.BaudRate = BaudRate;
    MyCOMPort.Parity = Parity.None;
    MyCOMPort.DataBits = 8;
    MyCOMPort.StopBits = StopBits.One;
    MyCOMPort.Handshake = Handshake.None;
    MyCOMPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
    MyCOMPort.ErrorReceived += new SerialErrorReceivedEventHandler(ErrorReceivedHandler);
    MyCOMPort.RtsEnable = true;
    MyCOMPort.DtrEnable = true;
    MyCOMPort.Open();

}
private void ErrorReceivedHandler(object sender, SerialErrorReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;
    string fullpath = "C:/DEBUG/debut.txt";
    File.AppendAllText(fullpath, "WE HAVE RECEIEVED AN ERROR");
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
    try
    {
        SerialPort sp = (SerialPort)sender;
        string fullpath = "C:/debug/debug.txt";

        var ak = String.Format("{0}", ACK);
        var nk = String.Format("{0}", NAK);
        var sx = String.Format("{0}", STX);
        var ex = String.Format("{0}", ETX);
        var crt = String.Format("{0}", CR);
        var lfd = String.Format("{0}", LF);


        string indata = sp.ReadExisting();
        string prxData = indata.Normalize();
        string rxData = prxData.Trim();

        File.AppendAllText(fullpath, "rx:" + indata + "-" + Convert.ToString(DateTime.Now) + Environment.NewLine);

        if (rxData.Equals("H"))
        {

            sp.Write(Convert.ToString(ak));
        }

        if (char.IsDigit(indata[0]) && firstpart.Length < 1)
        {
            firstpart = indata;
        }
        else if (char.IsDigit(indata[0]))
        {
            firstpart = firstpart + indata;

            string nena = getNena(firstpart);

            byte[] nenaascii = System.Text.Encoding.ASCII.GetBytes(nena);

            File.AppendAllText(fullpath, "Phone Number:" + firstpart.Substring(0, 10) + Environment.NewLine);
            File.AppendAllText(fullpath, "Record Number: 2" + firstpart.Substring(10, 2) + Environment.NewLine);
            sp.Write(Convert.ToString(ak));
            sp.Write(Convert.ToString(sx));

            sp.Write(nenaascii, 0, nena.Length);
            sp.Write(Convert.ToString(crt));
            sp.Write(Convert.ToString(lfd));
            sp.Write(ex);

            firstpart = "";

            sp.DiscardInBuffer();
            sp.DiscardOutBuffer();
        }

    }
    catch (Exception ex)
    {
        string fullpath = "C:/debug/debug.txt";
        File.AppendAllText(fullpath, "ERROR:" + ex.ToString() + Environment.NewLine);
    }
}

问题原因分析

  • SerialPort对象被垃圾回收:mainevent方法中创建的MyCOMPort是局部变量,方法执行完毕后失去强引用,可能被GC回收,导致事件订阅失效,无法响应串口数据。
  • 工作线程过早退出:mainevent仅完成串口初始化和打开就执行完毕,线程随之退出。虽然SerialPort会在后台触发事件,但无持续强引用时对象易被回收。
  • 错误日志路径错误:ErrorReceivedHandler中日志路径为C:/DEBUG/debut.txt,与其他日志路径不一致,即使触发错误也无法正常记录。
  • 缺失超时与连接保持机制:未设置串口读写超时,部分设备/驱动在30分钟无操作后可能进入休眠状态,导致数据接收中断。

解决方案

  • 保持SerialPort强引用:将MyCOMPort声明为类成员变量,确保服务生命周期内持有引用,避免被GC回收。
  • 阻塞工作线程:在mainevent中添加Thread.Sleep(Timeout.Infinite),防止线程过早退出。
  • 修正错误日志路径:统一日志文件路径,确保错误信息能被记录。
  • 添加串口超时设置:设置ReadTimeout和WriteTimeout,避免长时间阻塞,若设备支持可定期发送心跳包保持连接。
  • 优化数据处理逻辑:避免在DataReceivedHandler中执行耗时操作(如数据库查询),可将数据放入队列由单独线程处理,防止阻塞事件线程。

修改后的示例代码

// 类成员变量,保持SerialPort强引用
private SerialPort _myCOMPort;
private string firstpart = string.Empty;

protected override void OnStart(string[] args)
{
    string fullpath = "C:/debug/debug.txt";
    File.AppendAllText(fullpath, "Started: " + DateTime.Now + Environment.NewLine);
    File.AppendAllText(fullpath, "Machine Name:" + Environment.MachineName + Environment.NewLine);

    var worker = new Thread(MainEvent);
    worker.IsBackground = false;
    worker.Start();
}

private void MainEvent()
{
    try
    {
        _myCOMPort = new SerialPort();
        string COM_PortName = "COM2";
        int BaudRate = 9600;
        _myCOMPort.PortName = COM_PortName;
        _myCOMPort.BaudRate = BaudRate;
        _myCOMPort.Parity = Parity.None;
        _myCOMPort.DataBits = 8;
        _myCOMPort.StopBits = StopBits.One;
        _myCOMPort.Handshake = Handshake.None;
        // 添加读写超时设置
        _myCOMPort.ReadTimeout = 5000;
        _myCOMPort.WriteTimeout = 5000;
        _myCOMPort.DataReceived += DataReceivedHandler;
        _myCOMPort.ErrorReceived += ErrorReceivedHandler;
        _myCOMPort.RtsEnable = true;
        _myCOMPort.DtrEnable = true;
        _myCOMPort.Open();

        string fullpath = "C:/debug/debug.txt";
        File.AppendAllText(fullpath, "Serial port opened successfully: " + DateTime.Now + Environment.NewLine);

        // 阻塞线程,防止退出
        Thread.Sleep(Timeout.Infinite);
    }
    catch (Exception ex)
    {
        string fullpath = "C:/debug/debug.txt";
        File.AppendAllText(fullpath, "Serial port initialization error: " + ex.ToString() + Environment.NewLine);
    }
}

private void ErrorReceivedHandler(object sender, SerialErrorReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;
    string fullpath = "C:/debug/debug.txt";
    File.AppendAllText(fullpath, "ERROR RECEIVED: " + e.EventType + " at " + DateTime.Now + Environment.NewLine);
}

private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
    try
    {
        SerialPort sp = (SerialPort)sender;
        string fullpath = "C:/debug/debug.txt";

        var ak = Convert.ToString(ACK);
        var sx = Convert.ToString(STX);
        var ex = Convert.ToString(ETX);
        var crt = Convert.ToString(CR);
        var lfd = Convert.ToString(LF);

        string indata = sp.ReadExisting();
        string prxData = indata.Normalize();
        string rxData = prxData.Trim();

        File.AppendAllText(fullpath, "rx:" + indata + "-" + DateTime.Now + Environment.NewLine);

        if (rxData.Equals("H"))
        {
            sp.Write(ak);
        }

        if (!string.IsNullOrEmpty(indata) && char.IsDigit(indata[0]))
        {
            if (string.IsNullOrEmpty(firstpart))
            {
                firstpart = indata;
            }
            else
            {
                firstpart += indata;

                // 验证长度,避免Substring越界
                if (firstpart.Length >= 12)
                {
                    string phoneNumber = firstpart.Substring(0, 10);
                    string recordNumber = "2" + firstpart.Substring(10, 2);
                    File.AppendAllText(fullpath, "Phone Number:" + phoneNumber + Environment.NewLine);
                    File.AppendAllText(fullpath, "Record Number: " + recordNumber + Environment.NewLine);

                    string nena = getNena(firstpart);
                    byte[] nenaascii = System.Text.Encoding.ASCII.GetBytes(nena);

                    sp.Write(ak);
                    sp.Write(sx);
                    sp.Write(nenaascii, 0, nena.Length);
                    sp.Write(crt);
                    sp.Write(lfd);
                    sp.Write(ex);

                    firstpart = "";
                    sp.DiscardInBuffer();
                    sp.DiscardOutBuffer();
                }
            }
        }
    }
    catch (Exception ex)
    {
        string fullpath = "C:/debug/debug.txt";
        File.AppendAllText(fullpath, "Data handling ERROR:" + ex.ToString() + " at " + DateTime.Now + Environment.NewLine);
    }
}

// 假设getNena是你的数据库查询方法
private string getNena(string phoneNumber)
{
    // 数据库查询逻辑
    return string.Empty;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 18:59:55