十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C# Socket+串口Modbus设备通信 全套知识点

C# Socket+串口Modbus设备通信 全套知识点 一、项目整体架构与核心流程必考大题1.1 项目架构本项目为Socket-TCP服务端 串口SerialPort 双向转发网关实现 客户端 ↔ 服务器 ↔ 硬件设备 数据透传。服务器TCP多客户端服务端 串口通信核心转发中间层1.2 标准四层转发流程背诵满分Socket服务器接收客户端帧原样转发给串口硬件设备服务器将响应帧精准转发给对应请求客户端一对一应答非广播普通聊天室服务端广播给所有客户端融合TCP网络通信 串口硬件通信 Modbus协议 CRC16校验 INI配置持久化2.1 知识点实现参数持久化程序重启保留IP、端口、串口号、波特率。2.2 窗体加载读取配置代码private void Form1_Load(object sender, EventArgs e) { BindConfig(); // 加载本地ini配置文件 if (File.Exists(config.ini)) { txtIP.Text FileIni.Read(Config,ip); txtPort.Text FileIni.Read(Config, port); cbbPortNames.Text FileIni.Read(Config, com); cbbBaudRate.Text FileIni.Read(Config, botelv); } }2.3 保存配置代码private void btnSave_Click(object sender, EventArgs e) { FileIni.Write(Config,ip,txtIP.Text); FileIni.Write(Config, port, txtPort.Text); FileIni.Write(Config, com, cbbPortNames.SelectedItem.ToString()); FileIni.Write(Config, botelv, cbbBaudRate.SelectedItem.ToString()); }三、初始化绑定配置 BindConfig知识点代码自动获取本机IPv4、初始化默认端口、自动扫描串口、预置常用波特率/数据位/停止位/校验位。3.2 对应完整代码public void BindConfig() { // 1.自动获取本机IPv4 IPAddress[] address Dns.GetHostAddresses(Dns.GetHostName()); foreach (IPAddress ip in address) { if (ip.AddressFamily AddressFamily.InterNetwork) { txtIP.Text ip.ToString(); } } // 2.默认端口9999 txtPort.Text 9999.ToString(); // 3.扫描本机所有串口 cbbPortNames.DataSource SerialPort.GetPortNames(); cbbPortNames.SelectedIndex 0; // 4.预置波特率 int botelv 1200; cbbBaudRate.Items.Add(botelv); for (int i 1; i 5; i) { if (i ! 3) { cbbBaudRate.Items.Add(botelv * i * 2); } } cbbBaudRate.SelectedIndex 3; // 5.串口默认参数 cbbDataBits.Items.Add(8); cbbDataBits.SelectedIndex 0; cbbStopBits.Items.Add(One); cbbStopBits.SelectedIndex 0; cbbParity.Items.Add(None); cbbParity.SelectedIndex 0; }四、服务端启动核心Socket串口双启动4.1 知识点点击启动同时完成TCP服务端监听启动 串口设备连接启动双通道同时就绪。4.2 启动服务代码public void ConnectServer() { // 1.启动TCP Socket服务端 serverSocket new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); serverSocket.Bind(new IPEndPoint(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text))); serverSocket.Listen(100); userControl13.BackColor Color.Green; // 2.启动串口绑定参数 serialport new SerialPort(); serialport.BaudRate int.Parse(cbbBaudRate.SelectedItem.ToString()); serialport.PortName cbbPortNames.SelectedItem.ToString(); serialport.DataBits int.Parse(cbbDataBits.SelectedItem.ToString()); serialport.Parity (Parity)Enum.Parse(typeof(Parity), cbbParity.SelectedItem.ToString()); serialport.StopBits (StopBits)Enum.Parse(typeof(StopBits), cbbStopBits.SelectedItem.ToString()); if (!serialport.IsOpen) serialport.Open(); // 绑定串口数据接收事件 serialport.DataReceived Serialport_DataReceived; userControl11.BackColor Color.Green; // 3.锁定配置控件修改按钮状态 btnStart.Text 停止; isStart true; txtIP.Enabled txtPort.Enabled cbbBaudRate.Enabled cbbDataBits.Enabled cbbParity.Enabled cbbPortNames.Enabled cbbStopBits.Enabled false; }五、多客户端监听机制知识点代码使用ListClient存储所有在线客户端重复终端自动去重防止客户端重复连接残留5.2 客户端实体类public class Client { public EndPoint EndPoint { get; set; } // 客户端唯一终端标识 public Socket Socket { get; set; } // 客户端套接字对象 }5.3 监听客户端连接代码ListClient clients new ListClient(); CancellationTokenSource cts; public void AcceptQuestion() { cts new CancellationTokenSource(); Task task new Task(AcceptClient, cts.Token); task.Start(); } public async void AcceptClient() { while (!cts.IsCancellationRequested) { try { // 异步接收客户端连接 Socket 客户端 await serverSocket.AcceptAsync(); // 重复客户端去重 int index clients.FindIndex(c c.EndPoint 客户端.RemoteEndPoint); if (index ! -1) { clients.RemoveAt(index); } // 添加新客户端 clients.Add(new Client() { EndPoint 客户端.RemoteEndPoint, Socket 客户端 }); // 开启当前客户端消息监听 ReceiveMessage(客户端); } catch (Exception) { } } }六、核心队列转发机制考试最大难点使用ConcurrentQueue 线程安全队列存储请求客户端实现请求-响应一对一匹配。6.2 客户端消息接收入队代码ConcurrentQueueSocket cq new ConcurrentQueueSocket(); public void ReceiveMessage(Socket c1) { cts new CancellationTokenSource(); Task task new Task(async () { while (!cts.IsCancellationRequested) { try { byte[] buffer new byte[c1.Available]; int count await c1.ReceiveAsync(new ArraySegmentbyte(buffer), SocketFlags.None); if (count 0) { // 请求客户端入队 cq.Enqueue(c1); // 状态灯动画刷新 userControl13.BackColor Color.Yellow; await Task.Delay(50); userControl13.BackColor Color.Gray; await Task.Delay(50); userControl13.BackColor Color.Green; // 转发原始Modbus帧到串口设备 serialport.Write(buffer, 0, buffer.Length); userControl11.BackColor Color.Yellow; await Task.Delay(50); userControl11.BackColor Color.Gray; await Task.Delay(50); userControl11.BackColor Color.Green; // 统计接收字节数 Invoke(new Action(() { int oldCount int.Parse(txtReceiveByte.Text); txtReceiveByte.Text (oldCount count).ToString(); })); } } catch (Exception) { } } }, cts.Token); task.Start(); }6.3 串口数据返回、精准回发客户端代码private void Serialport_DataReceived(object sender, SerialDataReceivedEventArgs e) { byte[] buffer new byte[serialport.BytesToRead]; int count serialport.Read(buffer, 0, buffer.Length); if (count 0) return; // 队列取出请求客户端一对一响应 if (cq.TryDequeue(out Socket s1)) { // 校验客户端在线防止离线残留 if (clients.Any(v v.Socket s1)) { s1.Send(buffer); } } // 统计发送字节 Invoke(new Action(() { txtSendByte.Text (int.Parse(txtSendByte.Text) count).ToString(); })); }七、服务端关闭释放资源知识点代码关闭必须同时释放线程令牌、所有客户端、串口资源、服务端Socket、重置UI状态。7.2 关闭方法代码public void DisConnect() { // 终止所有后台线程 cts?.Cancel(); // 断开所有在线客户端 foreach (var item in clients) { Socket s1 item.Socket; if (s1 ! null s1.Connected) { s1.Disconnect(false); } } clients.Clear(); // 关闭串口 if (serialport ! null serialport.IsOpen) serialport.Close(); // 关闭服务端 serverSocket?.Close(); serverSocket null; // 重置UI btnStart.Text 启动; isStart false; txtIP.Enabled txtPort.Enabled cbbBaudRate.Enabled cbbDataBits.Enabled cbbParity.Enabled cbbPortNames.Enabled cbbStopBits.Enabled true; }八、客户端核心功能知识点代码8.1 客户端连接与断开Socket client; CancellationTokenSource cts null; public async void ConnectServer() { client new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await client.ConnectAsync(new IPEndPoint(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text))); btnConnOrClose.Text 断开; } public void DisConnect() { cts?.Cancel(); client?.Disconnect(false); client?.Close(); btnConnOrClose.Text 连接; }8.2 客户端接收解析Modbus响应帧知识点解析03功能码读寄存器响应帧拆分高低位拼接真实数值public void AcceptData() { cts new CancellationTokenSource(); Task.Run(() { while (!cts.IsCancellationRequested) { byte[] buffer new byte[client.Available]; int count client.Receive(buffer); if (count 0) continue; Invoke(new Action(() { // 解析03读寄存器响应 if (buffer[1] 0x03) { // 高低8位拼接16位寄存器数值 txtReadData1.Text (buffer[3] * 256 buffer[4]).ToString(); txtReadData2.Text (buffer[5] * 256 buffer[6]).ToString(); } })); } }, cts.Token); }8.3 定时自动轮询定时器知识点System.Timers.Timer t1; private void cbRealTimeRead_CheckedChanged(object sender, EventArgs e) { if (cbRealTimeRead.Checked) { if (client ! null client.Connected) { // 1秒定时下发读取指令 t1 new System.Timers.Timer(1000); t1.Start(); t1.Elapsed (s, e1) { // 定时发送Modbus读取帧 SendDataHelper.SendData(client, 1, 3, 0, 2); }; } } else { t1.Stop(); txtReadData1.Text ; txtReadData2.Text ; } }九、Modbus帧组装 CRC16校验超级重点标准Modbus RTU帧结构从站ID 功能码 起始地址(2字节) 数据长度(2字节) CRC16校验(2字节)固定8字节。9.2 帧组装工具类代码public class SendDataHelper { public static void SendData(Socket s, ushort slaveId, ushort functionCode, ushort startAddress, ushort datalength) { byte[] data new byte[8]; data[0] (byte)slaveId; data[1] (byte)functionCode; // 起始地址 高8位、低8位 data[2] (byte)(startAddress / 256); data[3] (byte)(startAddress % 256); // 数据长度 高8位、低8位 data[4] (byte)(datalength / 256); data[5] (byte)(datalength % 256); // CRC16校验 byte[] crc CRCHelper.CRC16(data); data[6] crc[0]; data[7] crc[1]; s.Send(data); } }9.3 标准Modbus CRC16算法考试必背完整代码public static class CRCHelper { // Modbus标准CRC16校验低字节在前、高字节在后 public static byte[] CRC16(byte[] data) { ushort crc 0xFFFF; foreach (byte b in data) { crc ^ b; for (int i 0; i 8; i) { if ((crc 0x0001) ! 0) { crc (ushort)((crc 1) ^ 0xA001); } else { crc 1; } } } // 返回低字节在前、高字节在后Modbus标准 return new byte[] { (byte)(crc 0xff), (byte)(crc 8) }; } }十、所有高频考点汇总背诵版网关作用TCP网络帧 ↔ 串口设备帧 双向透传转发INI配置实现参数持久化无需每次重启手动输入10.2 Modbus协议考点06功能码写入单个寄存器数据CRC16为Modbus必须校验防止数据传输错误子线程接收数据、串口事件更新UI必须InvokeCancellationTokenSource安全终止后台任务防止内存泄漏十一、终结本项目为TCP-Socket与串口结合的Modbus设备转发网关。服务端启动后同时监听TCP客户端连接与串口设备通过泛型集合管理多客户端利用线程安全队列缓存请求客户端实现客户端请求帧转发至硬件设备、设备响应帧精准回发对应客户端的一对一透传功能。客户端通过定时器定时自动下发带CRC16校验的标准Modbus读写指令解析设备返回的高低位寄存器数据。程序通过INI文件实现配置持久化全程异步线程通信、跨线程UI刷新、资源完整释放实现网络与硬件设备的稳定数据交互。
返回列表