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
相关产品推荐
相关产品推荐

