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

C# TcpListener监听TCP/IP端口仅能接收单台设备XML数据问题

问题背景

客户所用透析机通过以太网TCP/IP协议通信,数据传输协议为XML。设备可通过预先配置的IP地址与端口号自动连接我方计算机系统,每60秒主动向系统发送非请求式数据消息,数据以XML数据流形式传输。当前需求为将接收的数据存储为XML文件,现有实现代码如下:

namespace NiproMI
{
    class NiproListener
    {
        public static void GetXMLStream()
        {
            TcpListener server = null;
            try
            {
                Int32 port = Int32.Parse(AppConfigValues.GetAppConfigValue("Port"));
                IPAddress localAddr = IPAddress.Parse(AppConfigValues.GetAppConfigValue("IPAddress"));
                server = new TcpListener(localAddr, port);
                server.Start();
                Byte[] bytes = new Byte[256];
                String data = null;
                while (true)
                {
                    TcpClient client = server.AcceptTcpClient();
                    NetworkStream stream = client.GetStream();
                    string fileName = "NiproMI_" + DateTime.Now.ToString("dd-MM-yyyy--HH-mm-ss-ffff") + ".xml";
                    int i;
                    while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                    {

                        data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                        WriteToFile(data, fileName);
                        if (data.Contains("</Rec>"))
                            {
                            MoveInputFile(fileName);
                            fileName = "NiproMI_" + DateTime.Now.ToString("dd-MM-yyyy--HH-mm-ss-ffff") + ".xml";
                            }
                    }
                }
            }
            catch (SocketException e)
            {
                MIEventLogs.WriteLog("SocketException: {0}" + e);
            }
            finally
            {
                server.Stop();
            }

        }
        public static void WriteToFile(string sMessage, string fileName)
        {
            try {
                DirectoryInfo logdirFile = new DirectoryInfo(System.Configuration.ConfigurationManager.AppSettings["XmlFilePath"].ToString());
                string filePath = logdirFile.FullName;
                if (File.Exists(Path.Combine(filePath, fileName)))
                {
                    StreamWriter sw = null;
                    //Open File
                    FileStream fs = File.Open(Path.Combine(filePath, fileName), FileMode.Append, FileAccess.Write);
                    // generate a file stream with UTF8 characters          
                    sw = new StreamWriter(fs, System.Text.Encoding.UTF8);
                    sw.WriteLine(sMessage);
                    sw.Close();
                    sw = null;
                }
                else
                {
                    StreamWriter sw = null;
                    //Open File
                    FileStream fs = new FileStream(Path.Combine(filePath, fileName), FileMode.Create, FileAccess.Write);
                    // generate a file stream with UTF8 characters          
                    sw = new StreamWriter(fs, System.Text.Encoding.UTF8);
                    sw.WriteLine(sMessage);
                    sw.Close();
                    sw = null;
                }
            }
            catch (Exception e)
            {
                MIEventLogs.WriteLog( e.ToString());
            }
        }
        private static void MoveInputFile(string sFileName)
        {
            string strSourceFullFileName = "";
            string strDestFullFilename = "";
            string movedFromFolder= AppConfigValues.GetAppConfigValue("XmlFilePath");
            string movedToFolder = AppConfigValues.GetAppConfigValue("InputPath");
            System.IO.DirectoryInfo objdestfolder = new System.IO.DirectoryInfo(movedToFolder);
            System.IO.DirectoryInfo getsourceFile = new System.IO.DirectoryInfo(movedFromFolder);
            string sourcePath = "";
            string DestPath = "";
            try
            {
                sourcePath = getsourceFile.FullName;

                if (sourcePath[sourcePath.Length - 1] != '\\')
                    sourcePath += "\\";

                if (File.Exists(sourcePath + sFileName))
                    strSourceFullFileName = sourcePath + sFileName;

                DestPath = objdestfolder.FullName;
                if (DestPath[DestPath.Length - 1] != '\\')
                    DestPath += "\\";

                strDestFullFilename = DestPath + sFileName;

                if ((File.Exists(strSourceFullFileName) == true) && (objdestfolder.Exists == true))
                {
                    if (File.Exists(strDestFullFilename))
                    {
                        File.Delete(strDestFullFilename);
                    }
                    File.Move(strSourceFullFileName, strDestFullFilename);
                    
                }
                else
                {
                    throw new System.ApplicationException("Invalid destination folder to move retry file.");
                }
            }
            catch (System.Exception ex)
            {
               
            }
            finally
            {

            }
        }
    }
}
现存问题

客户侧共有4台透析机发送数据消息,但现有代码仅能捕获单台设备传输的数据。

代码核心遗漏点
  • 单线程阻塞导致无法接收多客户端连接:这是最核心的问题。AcceptTcpClient() 接收到第一台透析机的连接后,代码会直接进入内层while循环持续读取该连接的数据流,主线程被完全阻塞,永远无法回到外层循环再次调用AcceptTcpClient()接收其余3台设备的连接请求,自然只能拿到单台设备的数据。
  • 无连接资源管理逻辑:代码获取TcpClient和NetworkStream后没有做断开检测、资源释放处理,只要第一台设备保持连接,对应资源就会一直被占用,即使设备断开连接,也可能因为残留的连接状态导致新连接无法被正常处理。
  • 未处理TCP粘包半包问题:TCP是面向流的协议,数据传输时可能出现拆分、拼接的情况,现有代码仅判断单次读取到的缓冲区内容是否包含</Rec>结束标签,没有做数据拼接校验,多设备并发传输时很容易出现XML内容截断、多段数据混杂的问题。
  • 多并发下文件写入冲突风险:现有逻辑生成文件名仅用时间戳,没有绑定设备唯一标识(如客户端IP、设备编号),如果改造为多线程处理多连接,很容易出现不同设备的数据写入同一个文件、文件移动时锁冲突的问题。
  • 异常无日志导致问题无法排查:MoveInputFile方法的catch块完全为空,文件移动、删除失败时没有任何日志输出,多设备并发时出现的文件操作异常完全无法定位。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 18:24:41