上位机实践1
1编写Tcp客户端工具要求实现可同步/异步接收Tcp服务端的数据Tcp服务端可使用现有第三方功能2拓展工具功能支持Modbus通信支持基本的参数配置Modbus服务端可使用Modbus Slave;3自行编写Tcp服务端代替之前的第三方Tcp服务端工具要求可支持主动/被动 发送模式4自行编写Modbus服务端代替之前使用的Modbus Slave要求可实现自定义站号/方法/地址等常用服务参数5自定义数据协议且上面的客户端和服务可根据自定义数据协议实现各自完整的通信流程1、tcp客户端/服务端界面Tcp服务端开启监听等待客户端连接这时候需要相同的ipport才能够完成通信和客户端发起连接。客户端主动发送数据后服务端会接收到发送的数据。服务端会主动下发数据到客户端。服务端的被动发送是客户端发送请求后服务端再返回特定的返回数据。1、TcpClientManager.csusingSystem;usingSystem.Net.Sockets;usingSystem.Text;usingSystem.Threading.Tasks;namespaceTcpClientApp{publicclassTcpClientManager{privateSystem.Net.Sockets.TcpClientclient;privateNetworkStreamstream;// 连接客户端publicboolConnect(stringip,intport){try{Disconnect();clientnewSystem.Net.Sockets.TcpClient();client.Connect(ip,port);streamclient.GetStream();returntrue;}catch(Exceptionex){Console.WriteLine(ex.Message);returnfalse;}}//异步发送数据publicasyncTaskSendAsync(stringmsg){if(clientnull||!client.Connected||streamnull)return;byte[]dataEncoding.UTF8.GetBytes(msg);awaitstream.WriteAsync(data,0,data.Length);}//同步发送数据未使用publicstringReceiveSync(){if(streamnull)returnnull;byte[]buffernewbyte[1024];intlengthstream.Read(buffer,0,buffer.Length);returnEncoding.UTF8.GetString(buffer,0,length);}// 异步接收数据publicasyncTaskReceiveAsync(Actionstringcallback){if(streamnull)return;byte[]buffernewbyte[1024];try{while(client!nullclient.Connected){intlenawaitstream.ReadAsync(buffer,0,buffer.Length);if(len0){stringmsgEncoding.UTF8.GetString(buffer,0,len);callback(msg);}else{break;}}}catch{// 忽略断开异常}}// 断开连接publicvoidDisconnect(){try{stream?.Close();client?.Close();streamnull;clientnull;}catch{}}}}知识点1、System.Net.Sockets什么是 SocketSocket套接字 是操作系统提供的一种网络通信抽象机制。实际作用它封装了底层的 TCP/IP、UDP 等协议细节。不管是浏览器请求网页还是游戏连接服务器底层都是通过 Socket建立连接并传送数据的。代码中的 TcpClientSystem.Net.Sockets.TcpClient 是 C# 在低级 Socket类之上封装的高级类专门用来简化 TCP 协议客户端的开发。2、GetStream() 的作用GetStream() 是 TcpClient 的一个方法用于获取网络字节流NetworkStream。TCP 是基于字节流Byte Stream传输的。获得 NetworkStream 后你就可以像对文件流FileStream一样使用 Write / WriteAsync 向服务端发送字节或者用 Read / ReadAsync 从服务端读取字节。3、async Task异步编程用法async 和 Task 是 C# 中实现异步编程Async/Await 模式的核心关键字用来避免阻塞 UI 界面或主线程。单线程阻塞问题如果在主线程调用同步的 stream.Read(…)如果服务器一直没发数据你的软件界面如 WinForms / WPF就会卡死。Task代表一个正在进行或将要完成的操作相当于一个“承诺”或异步任务。async / awaitasync 修饰方法表明该方法内部有异步操作。await 用于暂停当前方法的执行把线程释放回系统处理其他事比如刷新 UI直到异步操作如网络传输完成后再自动接着往下走。4、Action callback回调函数/委托用法Action 是 C# 内置的一种委托Delegate用来把一个方法作为参数传递给另一个方法。5、stream?.Close()空条件运算符与资源释放if (stream ! null){stream.Close();}2、TcpServerManager.csusingSystem;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.Text;usingSystem.Threading.Tasks;namespaceTcpServerApp{publicclassTcpServerManager{privateTcpListenerlistener;privateSystem.Net.Sockets.TcpClientclient;publicboolIsAutoReply{get;set;}false;publicstringAutoReplyContent{get;set;};// 开启监听publicasyncTaskStartAsync(stringip,intport){Stop();listenernewTcpListener(IPAddress.Parse(ip),port);listener.Start();clientawaitlistener.AcceptTcpClientAsync();}// 接收异步传输publicasyncTaskReceiveAsync(ActionstringonMessageReceived){if(clientnull||!client.Connected)return;NetworkStreamstreamclient.GetStream();byte[]buffernewbyte[1024];try{while(client!nullclient.Connected){intlenawaitstream.ReadAsync(buffer,0,buffer.Length);if(len0){stringmsgEncoding.UTF8.GetString(buffer,0,len);onMessageReceived?.Invoke(msg);// 被动自动应答逻辑if(IsAutoReply){stringreplyMsgstring.IsNullOrEmpty(AutoReplyContent)?$[服务端被动回复]: 已成功收到数据 [{msg}]:AutoReplyContent;awaitSendAsync(replyMsg);onMessageReceived?.Invoke($[服务端被动自动回复] ➔{replyMsg});}}else{onMessageReceived?.Invoke([提示] 客户端已断开连接);break;}}}catch(Exceptionex){onMessageReceived?.Invoke($[提示] 连接中断:{ex.Message});}}// 异步发送数据publicasyncTaskSendAsync(stringmsg){if(clientnull||!client.Connected)return;NetworkStreamstreamclient.GetStream();byte[]dataEncoding.UTF8.GetBytes(msg);awaitstream.WriteAsync(data,0,data.Length);}// 断开连接publicvoidStop(){try{client?.Close();listener?.Stop();clientnull;listenernull;}catch{}}}}2、modbus主站从站从站开始监听主站发起来连接也是对应相同的ipport。主站实现了01 到 06功能码只提供了读取寄存器和写入寄存器这两个按钮。可以从起始地址、读取长度、写入值设置寄存器的内容。从站动态改变值。1、ModbusTcpServerusingSystem;usingNModbus;usingTcpClientApp.Config;namespaceTcpClientApp{publicclassModbusTcpClient{privateSystem.Net.Sockets.TcpClienttcpClient;privateIModbusMastermaster;// 主站自动开启publicboolConnect(ModBusConfigconfig){try{tcpClientnewSystem.Net.Sockets.TcpClient(config.Ip,config.Port);varfactorynewModbusFactory();masterfactory.CreateMaster(tcpClient);returntrue;}catch{returnfalse;}}// 读线圈publicbool[]ReadCoils(byteslaveId,ushortstartAddress,ushortcount){returnmaster?.ReadCoils(slaveId,startAddress,count);}// 读保持寄存器publicushort[]ReadHoldingRegisters(byteslaveId,ushortaddress,ushortcount){returnmaster?.ReadHoldingRegisters(slaveId,address,count);}// 读输入寄存器publicushort[]ReadInputRegisters(byteslaveId,ushortaddress,ushortcount){returnmaster?.ReadInputRegisters(slaveId,address,count);}// 写入线圈publicvoidWriteCoil(byteslaveId,ushortaddress,boolvalue){master?.WriteSingleCoil(slaveId,address,value);}// 写入寄存器publicvoidWriteRegister(byteslaveId,ushortaddress,ushortvalue){master?.WriteSingleRegister(slaveId,address,value);}}}2、ModbusTcpClientusingSystem;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.Threading.Tasks;usingNModbus;namespaceTcpServerApp{publicclassModbusTcpServer{privateTcpListener_listener;privateIModbusSlaveNetwork_slaveNetwork;privateIModbusSlave_slave;publicbyteSlaveId{get;privateset;}1;publicboolIsRunning{get;privateset;}false;publiceventActionushort[]OnRegistersUpdated;// 开启从站publicasyncTaskStartAsync(stringip127.0.0.1,intport502,byteslaveId1,ushortstartAddress0,ushort[]initialRegistersnull){if(IsRunning)return;SlaveIdslaveId;IPAddressiPAddressIPAddress.Parse(ip);_listenernewTcpListener(iPAddress,port);_listener.Start();varfactorynewModbusFactory();_slaveNetworkfactory.CreateSlaveNetwork(_listener);_slavefactory.CreateSlave(slaveId);if(initialRegisters!nullinitialRegisters.Length0){_slave.DataStore.HoldingRegisters.WritePoints(startAddress,initialRegisters);}else{ushort[]defaultRegistersnewushort[]{0,0,0,0,0,0,0,0,0,0};_slave.DataStore.HoldingRegisters.WritePoints(0,defaultRegisters);}_slaveNetwork.AddSlave(_slave);IsRunningtrue;// ✨ 开启后台 200ms 实时监听一旦客户端写入修改内存立即自动通知 UI 刷新看板与日志_Task.Run(async(){ushort[]lastValuesnewushort[10];while(IsRunning){awaitTask.Delay(200);if(_slave!null){ushort[]currentGetHoldingRegisters(0,10);boolchangedfalse;for(inti0;icurrent.Length;i){if(current[i]!lastValues[i]){changedtrue;break;}}if(changed){Array.Copy(current,lastValues,current.Length);OnRegistersUpdated?.Invoke(current);}}}});await_slaveNetwork.ListenAsync();}//写入保持寄存器publicvoidSetHoldingRegisters(ushortstartAddress,ushort[]values){if(_slavenull)return;_slave.DataStore.HoldingRegisters.WritePoints(startAddress,values);}//读取保持寄存器publicushort[]GetHoldingRegisters(ushortstartAddress,ushortcount){if(_slavenull)returnnewushort[0];return_slave.DataStore.HoldingRegisters.ReadPoints(startAddress,count);}// 断开连接publicvoidStop(){if(!IsRunning)return;_slaveNetwork?.Dispose();_listener?.Stop();IsRunningfalse;}}}1、为什么变量名开头有一个下划线 _变量名开头的下划线 _ 是一种 C# 代码命名规范Coding Convention用来标识这个变量是类的私有成员变量Private Field。快速区分变量作用域在代码中看到 _listener就能立马知道它是定义在整个类层级的私有字段而如果看到没有下划线的 listener通常代表它是一个局部变量或函数参数。避免与方法参数同名冲突在构造函数或属性赋值时省去了写 this. 的麻烦。publicclassServer{privateTcpListener_listener;// 私有字段// 没有下划线时需要写 this.listener listener;// 有下划线时可以直接写publicServer(TcpListenerlistener){_listenerlistener;}}3、自定义通信协议一、为什么需要自定义通信协议在实际工程中普通 TCP 协议只管发裸数据容易出现**“粘包/断句乱码”**比如发Hello和World接收端可能收到HelloWor和ld且没有数据校验。Modbus TCP 协议功能固定只适合读写 16 位整数寄存器无法高效传输复杂字符串、结构化浮点传感器或大块数据。自定义通信协议兼具防粘包断句、CRC16 防乱码校验、消息自增流水号、结构化浮点传感器等能力是商业设备和硬件通信的标准选择。二、 协议数据帧全景结构图我们协议的数据包在网线上传输时由8 大核心字段紧密排列组成字节位置: [ 0..1 ] [ 2 ] [ 3..4 ] [ 5 ] [ 6..7 ] [ 8 .. 8N ] [ 8N1..8N2 ] [ 8N3..8N4 ] 字段名称: [ 帧头 ] [版本] [消息流水号] [命令字] [数据长度] [ 结构化 Payload ] [ CRC-16 校验码 ] [ 帧尾 ] 对应大小: 2字节 1字节 2字节 1字节 2字节 N 字节 (变长) 2 字节 2 字节 示例数据: AA 55 0x10 0x00 0x01 0x03 0x00 0x0E [ 14字节传感器结构 ] 0x4A 0x2B 0x0D 0x0A帧头 (0xAA 0x55, 2 字节)协议暗号。接收端检测到0xAA 0x55才开始拆包过滤无效杂波。版本号 (0x10, 1 字节)代表 V1.0 协议方便未来软件版本升级兼容。消息流水号SeqID(2 字节)每发送一个数据包序号自动加 11, 2, 3...用于防止丢包、重放攻击与并发匹配。命令字Cmd(1 字节)代表本包数据的功能类型。数据长度 (2 字节)大端序记录后方数据内容 Payload 的实际字节数。数据内容Payload(N 字节)变长数据区域文本、心跳或结构化传感器。CRC-16 校验码 (2 字节)标准的CRC16-Modbus查表法冗余校验拦截网络干扰造成的乱码。帧尾 (0x0D 0x0A, 2 字节)固定换行符\r\n保证整帧数据闭环。三、 支持的 4 大命令字指令集命令字 (Hex)命令名称功能描述传输载荷 (Payload) 示例0x01心跳包 (PING)客户端定期检测服务端是否在线PING0x02文本消息包发送任意长度的 UTF-8 变长文本Hello Protocol0x03高精度传感器包传输结构化多传感器浮点数据设备ID (4B) 温度 Float (4B) 湿度 Float (4B) 电压 UInt16 (2B)0x80ACK 应答包服务端处理成功后的被动确认回复包含原请求SeqID的回复确认字

相关新闻

Android各版本适配总结

Android各版本适配总结

Android 各版本适配总结(targetSdk) 整理日期:2026-07-30 范围:Android 8(API 26)→ Android 17(API 37) 说明:以下为 升 targetSdkVersion 后 的关键行为变更与适配要点…

2026/7/31 22:16:00 阅读更多 →
云原生架构下的政务 AI 服务建设

云原生架构下的政务 AI 服务建设

随着数字政府建设进入深度数智化转型阶段,传统政务信息化的痛点愈发凸显:系统烟囱林立、资源利用率低、迭代更新缓慢、AI应用落地成本高、突发业务峰值承载能力不足。以往政务AI多是“单点试点、零散落地”,存在模型复用率低、数据安全风险高…

2026/7/31 22:14:59 阅读更多 →
房车跑马:改装经验与赛事经济全解析

房车跑马:改装经验与赛事经济全解析

1. 房车跑马:一种新兴的生活方式老季的故事让我想起去年在兰州马拉松遇到的一位跑友。当时他开着一辆改装过的二手房车停在起跑点附近,车身上贴满了各种马拉松完赛贴纸。这种"开着房车去跑马"的生活方式,正在国内跑圈悄然兴起。房车…

2026/7/31 22:14:59 阅读更多 →

最新新闻

Playnite终极指南:如何免费统一管理你的跨平台游戏库

Playnite终极指南:如何免费统一管理你的跨平台游戏库

Playnite终极指南:如何免费统一管理你的跨平台游戏库 【免费下载链接】Playnite Video game library manager with support for wide range of 3rd party libraries and game emulation support, providing one unified interface for your games. 项目地址: http…

2026/7/31 22:52:11 阅读更多 →
如何永久保存微信聊天记录?这款开源工具让你的数字记忆不再消失

如何永久保存微信聊天记录?这款开源工具让你的数字记忆不再消失

如何永久保存微信聊天记录?这款开源工具让你的数字记忆不再消失 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending…

2026/7/31 22:52:11 阅读更多 →
Rust与Python混合编程:AI开发的高效组合

Rust与Python混合编程:AI开发的高效组合

1. 为什么AI时代需要RustPython组合拳?三年前我在处理一个千万级数据的机器学习项目时,Python在特征工程阶段突然内存溢出崩溃,那次事故让我开始认真寻找能弥补Python弱点的语言。Rust以其独特的内存安全性和并发性能进入了我的视野——它没有…

2026/7/31 22:52:11 阅读更多 →
GSE270583单细胞实战palantir拟时序分析

GSE270583单细胞实战palantir拟时序分析

写在前面 本教程可视化集锦: 单细胞发育轨迹分析(Pseudotime Analysis)是揭开细胞分化、状态转换及发育命运谜团的核心技术。传统的分析往往通过聚类识别离散的细胞群,但难以捕捉从祖细胞到成熟谱系之间连续的转录组流形演变。 …

2026/7/31 22:52:11 阅读更多 →
鸣潮自动化工具ok-ww终极指南:零门槛解放双手,让游戏回归乐趣

鸣潮自动化工具ok-ww终极指南:零门槛解放双手,让游戏回归乐趣

鸣潮自动化工具ok-ww终极指南:零门槛解放双手,让游戏回归乐趣 【免费下载链接】ok-wuthering-waves 鸣潮 后台自动战斗 自动刷声骸 一键日常 Automation for Wuthering Waves 项目地址: https://gitcode.com/GitHub_Trending/ok/ok-wuthering-waves …

2026/7/31 22:52:11 阅读更多 →
3个隐藏功能让MacBook刘海变身智能控制中心:Boring Notch完整指南

3个隐藏功能让MacBook刘海变身智能控制中心:Boring Notch完整指南

3个隐藏功能让MacBook刘海变身智能控制中心:Boring Notch完整指南 【免费下载链接】boring.notch TheBoringNotch: Not so boring notch That Rocks 🎸🎶 项目地址: https://gitcode.com/gh_mirrors/bor/boring.notch 厌倦了MacBook上…

2026/7/31 22:51:11 阅读更多 →

日新闻

物理复制比逻辑复制好在哪?数据库复制原理详解

物理复制比逻辑复制好在哪?数据库复制原理详解

数据库复制是把主库数据同步到备库的机制,分为逻辑复制和物理复制两种。逻辑复制传输的是 SQL 语句或行变更事件,物理复制传输的是存储引擎底层的物理日志。阿里云 PolarDB(云原生数据库)采用物理复制,在同步延迟、数据…

2026/7/31 0:00:34 阅读更多 →
BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/Bilib…

2026/7/31 0:00:34 阅读更多 →
有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

当前,游戏行业的“DataAI融合”已从概念验证进入价值落地阶段。根据IDC 2025年数据,中国AI游戏云市场规模已达18.6亿元;同时,游戏研发环节AI渗透率高达86%,生成式AI内容普及率超过50%。面对庞大的市场,游戏…

2026/7/31 0:00:34 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/31 1:03:03 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/31 4:19:39 阅读更多 →

月新闻