c#软件开发学习笔记--TcpListener\TcpClient\UdpClient
TCP / UDP 设备连接学习总结一、TcpListener —— 服务器端客户端 服务器 (TcpListener) │ │ │ ───① new TcpListener(ip, port) │ │ ───② .Start() 开始监听 ────────── │ │ │ │ ◄─── AcceptTcpClientAsync() 阻塞等待 │ │ │ │ ───③ TcpClient.ConnectAsync() ──► │ │ │ │ ◄── 返回 TcpClient 实例 ───────── │ │ │ │ ───④ GetStream() 获取 NetworkStream │ │ │ │ ═════⑤ ReadAsync() / WriteAsync() ═══数据交换═════ │ │ │ ───⑥ 停止: Stop() 停止监听 ─────── │ │ │1.1 关键方法与属性成员说明new TcpListener(IPAddress, int port)构造绑定本地 IP 与端口Start()开始监听Stop()停止监听Pending()是否有挂起的连接请求常用于循环里非阻塞判断AcceptTcpClient()/AcceptTcpClientAsync()同步/异步接受一个连接返回TcpClientAcceptTcpClientAsync(CancellationToken)支持取消令牌的异步接受.NET 9 项目中使用1.2 基础实现TCPListen业务TCP 服务器收到客户端数据 → 写入串口串口返回数据 → 回发给当前客户端。使用ConcurrentQueue把多个客户端请求串行化避免串口并发冲突。// 启动监听tcpListennewTcpListener(IPAddress.Parse(192.168.219.14),9999);tcpListen.Start();// 异步循环接受连接privatevoidAcceptRequest(){ctsnewCancellationTokenSource();Task.Run(async(){while(!cts.IsCancellationRequested){if(tcpListennull)break;if(tcpListen.Pending())continue;// 若挂起连接则跳过TcpClienttcpClientawaittcpListen.AcceptTcpClientAsync();AcceptData(tcpClient);// 转入数据接收循环}},cts.Token);}// 读取客户端数据并通过 NetworkStream 回写串口响应NetworkStreamstreamcurrentClient.GetStream();stream.Write(fullFrame,0,fullFrame.Length);1.3 完整设备服务器实现TCPListen连接设备服务器在基础版上增加了配置持久化FileIni、本机 IP 自动识别、收发字节计数、Modbus 帧按功能码解析0x03读 /0x06写、带CancellationToken的异步 API。// 自动获取本机 IPv4 地址用于监听IPAddress[]addressesDns.GetHostAddresses(Dns.GetHostName());foreach(IPAddressaddressinaddresses)if(address.AddressFamilyAddressFamily.InterNetwork)txtServerIP.Textaddress.ToString();// 启动tcpListenernewTcpListener(IPAddress.Parse(txtServerIP.Text),int.Parse(txtPort.Text));tcpListener.Start();// 带取消令牌的异步接受TcpClientclientawaittcpListener.AcceptTcpClientAsync(_cts.Token);// 带取消令牌的异步读取NetworkStreamstreamclient.GetStream();intlenawaitstream.ReadAsync(buffer,_cts.Token);if(len0)return;// len0 表示客户端已正常断开// 回写响应异步awaitstream.WriteAsync(fullFrame);停止服务器时释放资源privatevoidStopServer(){_cts?.Cancel();serialPort?.Close();tcpListener?.Stop();// 停止监听tcpListenernull;}二、TcpClient —— 客户端客户端 (TcpClient) 服务器 │ │ │ ───① new TcpClient() ───────────── │ │ │ │ ───② .ConnectAsync(host, port) ─► │ AcceptTcpClientAsync() │ │ │ ◄── 连接成功 (Connectedtrue) ──── │ │ │ │ ───③ GetStream() 获取 NetworkStream │ │ │ │ ───④ Write() / WriteAsync() 发送 ──► │ ReadAsync() 接收 │ │ │ ◄──⑤ ReadAsync() 接收响应 ───────── │ WriteAsync() 回写 │ │ │ ═════ 循环收发 ═══════════════════ │ │ │ │ ───⑥ 停止: Close() 关闭连接 ────── │ │ │2.1 关键方法与属性成员说明new TcpClient()构造一个未连接的客户端Connect(host, port)/ConnectAsync(host, port)同步/异步连接服务器Connected(bool)当前是否处于连接状态Available(int)网络缓冲区中可读字节数GetStream()返回NetworkStream用于读写Client(Socket)底层 Socket可取RemoteEndPoint等信息Close()关闭连接NetworkStream关键成员Read / ReadAsync返回 0 表示对端关闭、Write / WriteAsync。2.2 基础实现TCPClient业务连接服务器 → 后台循环接收 → 定时器周期性发送。// 连接privateasyncTaskConnectServer(){tcpClientnewTcpClient();awaittcpClient.ConnectAsync(192.168.219.14,9999);}// 接收循环Available 判断 ReadAsyncprivatevoidAcceptData(){ctsnewCancellationTokenSource();Task.Run(async(){byte[]buffernewbyte[1024];while(!cts.IsCancellationRequested){if(tcpClientnull||!tcpClient.Connected)continue;NetworkStreamstreamtcpClient.GetStream();intlenawaitstream.ReadAsync(buffer,0,buffer.Length,cts.Token);if(len0)break;// 处理 buffer[0..len)}},cts.Token);}// 发送NetworkStreamstreamtcpClient.GetStream();byte[]bsEncoding.UTF8.GetBytes(textBox1.Text);stream.Write(bs,0,bs.Length);2.3 设备客户端实现TCPClient连接设备客户端业务通过 TCP 承载 Modbus RTU 协议——组装请求帧功能码0x03读 /0x06写CRCHelper.CRC16校验发送后解析响应。发送使用lock(sendLock)保证线程安全。// 连接含异常提示awaitclient.ConnectAsync(txtServerIP.Text,int.Parse(txtPort.Text));// 发送 Modbus 读保持寄存器请求功能码 0x03privatevoidSendRequest(ushortslaveAddress,ushortstartAddress,ushortdataLength){lock(sendLock){if(clientnull)return;byte[]bsnewbyte[8];bs[0](byte)slaveAddress;bs[1]0X03;bs[2](byte)(startAddress8);bs[3](byte)(startAddress0XFF);bs[4](byte)(dataLength8);bs[5](byte)(dataLength0XFF);byte[]crcCRCHelper.CRC16(bs);bs[6]crc[0];bs[7]crc[1];client.GetStream().Write(bs);}}// 接收并按功能码解析响应intfuncCodebuffer[1];if(funcCode0X03){/* 解析寄存器值 */}elseif(funcCode0X06){/* 解析写入回显 */}断开连接privatevoidDisConnectServer(){_cts?.Cancel();client?.Close();clientnull;}三、UdpClient —— 无连接收发客户端 (UdpClient) 服务器 (UdpClient) │ │ │ ───① new UdpClient(任意端口) │ │ │ │ ───② 构造 IPEndPoint(server, port) │ │ │ │ ───③ Send(data, len, endPoint) ─► │ ReceiveAsync() 阻塞等待 │ │ │ │ 获得 RemoteEndPoint │ │ │ ◄──④ ReceiveAsync() 收到响应 ───── │ Send(data, len, remoteEndPoint) │ │ │ │ │ ═════ 无需连接直接数据报收发 ════ │ │ │ │ ───⑤ 停止: Close() / Dispose() ─── │ │ │UDP 不需要建立连接服务器与客户端的区别仅在于服务器绑定固定端口并ReceiveAsync等待数据客户端绑定任意端口IPAddress.Any, 0后用Send指定目标端点。3.1 关键方法与属性成员说明new UdpClient(IPEndPoint)构造并绑定本地端点Send(byte[], int, IPEndPoint)向指定远程端点发送数据报ReceiveAsync()异步接收返回UdpReceiveResult含Buffer与RemoteEndPointClose()/Dispose()释放资源3.2 UDP 客户端业务向设备发送 Modbus 请求帧异步接收并解析响应寄存器值。// 绑定本地任意端口目标端点由 endPoint 指定clientnewUdpClient(newIPEndPoint(IPAddress.Any,0));endPointnewIPEndPoint(IPAddress.Parse(txtIp.Text),int.Parse(txtPort.Text));// 发送client.Send(bytes,bytes.Length,endPoint);// 异步接收varresawaitclient.ReceiveAsync();if(res.Buffer[1]!0X03)continue;intbyteCountres.Buffer[2];// 解析寄存器值...3.3 UDP 服务器业务UDP 服务器收到客户端请求 → 写串口 → 串口返回数据 → 用Send回发给该客户端RemoteEndPoint。同样用ConcurrentQueue串行化串口访问。// 绑定本机 IP 9999 端口servernewUdpClient(newIPEndPoint(address,9999));// 接收循环varresawaitserver.ReceiveAsync();requestQueue.Enqueue(newRequestItem{clientEndPointres.RemoteEndPoint,datares.Buffer});// 串口响应回发给对应客户端server.Send(fullFrame,fullFrame.Length,currentRequest.clientEndPoint);四、通用设计模式总结4.1 异步接收循环统一范式所有子项目都采用相同范式CancellationTokenSourceTask.Runwhile(!cts.IsCancellationRequested)循环 await XxxAsync(cts.Token)。这是 WinForms 下避免阻塞 UI 线程的标准做法。4.2 请求队列 串行化处理串口是独占资源不能并发写入。服务器端统一使用ConcurrentQueueRequestItemlockisProcessing标志位把并发的网络请求排成队列逐个转发给串口串口响应完成后再出队下一个privatevoidTryProcessQueue(){lock(processLock){if(isProcessing)return;if(requestQueue.IsEmpty)return;if(requestQueue.TryDequeue(outvaritem)){currentClientitem;isProcessingtrue;serialPort.Write(item.Data,0,item.Data.Length);}}}4.3 网口 ↔ 串口协议转换以上这三个服务器项目的本质都是协议网关把以太网(TCP/UDP)上收到的 Modbus RTU 帧透传给串口设备再把串口返回的帧回发给发起请求的网络客户端。4.4 Modbus RTU 帧解析按功能码确定帧长度从Listbyte缓冲区中切片取完整帧功能码0x03读帧长 3 字节数 2(CRC)功能码0x06写固定 8 字节帧尾 2 字节 CRC16CRCHelper.CRC16完整帧解析实现以串口数据接收为例privatevoidSerialPort_DataReceived(objectsender,SerialDataReceivedEventArgse){if(serialPortnull||!serialPort.IsOpen)return;// 读取串口缓冲区全部数据byte[]buffernewbyte[serialPort.BytesToRead];serialPort.Read(buffer,0,buffer.Length);list.AddRange(buffer);// 循环处理缓冲区中可能存在的多个完整帧while(list.Count5){// 根据功能码计算帧长度intfuncCodelist[1];intfullFrameLength0;if(funcCode0X03)// 读寄存器响应{intlenlist[2];// 字节数fullFrameLength3len2;// 地址 功能码 字节数 数据 CRC}elseif(funcCode0X06)// 写单寄存器响应{fullFrameLength8;// 固定 8 字节}// 缓冲区长度足够一个完整帧if(list.CountfullFrameLength){byte[]fullFramelist.Take(fullFrameLength).ToArray();list.RemoveRange(0,fullFrameLength);// 将完整帧回发给发起请求的客户端if(currentClient!nullcurrentClient.TcpClient!nullcurrentClient.TcpClient.Connected){NetworkStreamstreamcurrentClient.TcpClient.GetStream();awaitstream.WriteAsync(fullFrame);}}elsebreak;// 数据不足等待更多字节}}4.5 连接状态与生命周期管理接受端AcceptTcpClientAsync返回的TcpClient用Connected判断活性ReadAsync返回0视为断开。停止统一先cts.Cancel()取消后台任务再Close()/Stop()/Dispose()释放网络与串口资源并把对象置null。客户端发送用lock保护NetworkStream.Write避免定时器与按钮并发发送互相踩踏。五、关键方法/属性速查表类方法/属性用途出现子项目TcpListenerStart()/Stop()启停监听TCPListen、TCPListen连接设备服务器TcpListenerPending()是否有挂起连接TCPListen、TCPListen连接设备服务器TcpListenerAcceptTcpClientAsync(CancellationToken)异步接受连接同上TcpClientConnectAsync(host, port)异步连接服务器TCPClient、TCPClientTest、TCPClient连接设备客户端TcpClientConnected/Available连接状态 / 可读字节全部 TCP 客户端TcpClientGetStream()获取 NetworkStream全部 TCPTcpClientClose()关闭连接全部 TCPNetworkStreamReadAsync(buf, token)异步读返回 0断开全部 TCPNetworkStreamWrite(buf)/WriteAsync(buf)同步/异步写全部 TCPUdpClientnew UdpClient(IPEndPoint)绑定本地端点WindowsFormsApp5/6UdpClientSend(buf, len, IPEndPoint)发送到指定端点WindowsFormsApp5/6UdpClientReceiveAsync()异步接收含来源端点WindowsFormsApp5/6UdpClientClose()/Dispose()释放资源WindowsFormsApp5/6六、对比与要点6.1 TCP vs UDP维度TCPTcpListener / TcpClientUDPUdpClient连接面向连接需Accept/Connect无连接直接Send/Receive可靠性可靠传输、保序不可靠、可能丢包/乱序服务器区分TcpListener监听 TcpClient表示每个连接仅UdpClient靠RemoteEndPoint区分客户端数据流NetworkStream读写流数据报Send/ReceiveAsync适用需要稳定可靠的设备控制Modbus TCP简单查询/广播/低开销场景6.2 同步 vs 异步项目统一采用异步 APIXxxAsyncawait配合CancellationToken实现可控停止避免阻塞 WinForms UI 线程。Pending()continue是一种轻量的非阻塞轮询写法但更推荐直接await AcceptTcpClientAsync(cts.Token)。6.3 .NET 9 vs .NET Framework 4.8 差异.NET 9项目date87 下 TCP 系列可使用AcceptTcpClientAsync(CancellationToken)、ReadAsync(buffer, token)等带取消令牌的重载。.NET Framework 4.8项目date84 下 UDP 系列使用ReceiveAsync()无取消令牌重载停止时靠cts循环判断 Close()触发异常退出循环。七、学习要点清单服务器三步走new TcpListener(ip, port)→Start()→AcceptTcpClientAsync()。客户端两步走new TcpClient()→ConnectAsync(host, port)再用GetStream()读写。ReadAsync返回0是判断对端正常关闭的关键信号。Available属性可用于接收循环中避免空转但异步ReadAsync本身会阻塞至有数据二者择一即可。UDP 服务器与客户端本质相同区别仅在绑定端点是否固定、是否主动Send。串口 网口混合场景下务必用队列 锁串行化串口访问。停止流程要有序Cancel()取消任务 →Close()/Stop()关闭资源 → 置null。Modbus RTU 帧需按功能码计算帧长并校验 CRC16TCP 承载时帧边界由ReadAsync返回长度保证UDP 则由数据报天然保界。

相关新闻

2026服装行业ERP选型指南与实施策略

2026服装行业ERP选型指南与实施策略

1. 2026年服装行业ERP选型全景图当我在2023年参与某女装品牌的ERP系统迁移时,CIO曾说过:"选错ERP相当于给企业装上不合脚的鞋,不仅走不快,还会磨得血肉模糊。"这句话在服装行业尤为贴切——季节性波动大、SKU数量庞大、…

2026/9/29 8:13:02 阅读更多 →
Halcon与C#联合开发工业视觉检测框架实践

Halcon与C#联合开发工业视觉检测框架实践

1. Halcon与C#联合开发框架概述在工业视觉检测领域,Halcon作为老牌机器视觉库一直占据重要地位。我团队基于Halcon 12和C#开发了一套生产级视觉检测框架,经过三年迭代现已升级至2.0改进版。这个框架最大的特点是封装了Halcon核心算子与C#交互逻辑&#x…

2026/9/29 16:02:36 阅读更多 →
JavaScript模块化:从CommonJS到ES Module的演进与实践

JavaScript模块化:从CommonJS到ES Module的演进与实践

1. JavaScript模块化发展背景与核心痛点2009年Node.js的出现让JavaScript首次具备了服务端开发能力,随之而来的大规模代码管理需求催生了CommonJS规范。我在早期Node项目中最直观的感受是:当代码量超过3000行时,全局作用域污染和依赖管理混乱…

2026/9/26 0:51:06 阅读更多 →

最新新闻

全流程AI科研平台与单点工具对比:沁言学术等五款评测

全流程AI科研平台与单点工具对比:沁言学术等五款评测

一、AI科研软件为什么越来越多,不同人群到底卡在哪 1、行业背景。 大模型技术成熟之后,科研工具像雨后春笋一样往外冒。写论文的、改句子的、画图的、管文献的,几乎每个环节都有专门的AI软件。信息过载带来的新问题也随之出现:工具…

2026/10/1 15:34:21 阅读更多 →
辽宁北斗国产化本安防爆物联网终端,面向石化易燃易爆高危工况,覆盖安全帽、智能安全带、定位工牌、车载终端、执法记录仪,依托单北斗RTK定位、统一调度平台,实现人车一体化监管,解决高空作业、人员车辆定位等

辽宁北斗国产化本安防爆物联网终端,面向石化易燃易爆高危工况,覆盖安全帽、智能安全带、定位工牌、车载终端、执法记录仪,依托单北斗RTK定位、统一调度平台,实现人车一体化监管,解决高空作业、人员车辆定位等

辽宁北斗系列物联网终端|石化高危场景一体化安全管控方案#石化安全生产#本安防爆#单北斗#人员定位#车载监管#单兵执法#智慧化工一、石化企业业务痛点分析石化化工厂区属于爆炸性气体危险环境,设备必须满足本安防爆要求;同时存在塔罐高空作业、…

2026/10/1 15:34:21 阅读更多 →
HTTP 迁 HTTPS 掉收录的排查清单:301 链条断点与站点迁移的完整复盘

HTTP 迁 HTTPS 掉收录的排查清单:301 链条断点与站点迁移的完整复盘

HTTP 迁 HTTPS 掉收录的排查清单:301 链条断点与站点迁移的完整复盘 适用读者:负责制造业或 B2B 企业官网运维的开发者;正在 Google Search Console(谷歌站长平台,下称 GSC)覆盖率报告里看到收录量腰斩、需…

2026/10/1 15:34:21 阅读更多 →
上海点山十几年展陈经验如何让每个展厅做到独特且不重复?揭秘其可复用的设计方法论

上海点山十几年展陈经验如何让每个展厅做到独特且不重复?揭秘其可复用的设计方法论

上海点山展示如何通过十几年展陈经验让每个展厅做到独特且不重复?揭秘其可复用的设计方法论引子:为什么“独特且不重复”是展陈设计的核心挑战?在当前的企业展示空间建设中,“千馆一面”已成为普遍痛点。许多展厅虽投入不菲&#…

2026/10/1 15:34:21 阅读更多 →
NLP基础到高级01:文本处理 — 分词、词干提取、词形还原

NLP基础到高级01:文本处理 — 分词、词干提取、词形还原

文本处理 — 分词、词干提取、词形还原语言是连续的,模型是离散的。预处理是连接两者的桥梁。类型: 构建 语言: Python 前置条件: Phase 2 14 (朴素贝叶斯) 用时: ~45 分钟 问题所在 模型无法直接读取 “The cats wer…

2026/10/1 15:34:21 阅读更多 →
AI工程从零到落地:模型部署、Prompt与Agent全链路实践指南

AI工程从零到落地:模型部署、Prompt与Agent全链路实践指南

一年前我把仓库名定为ai-engineering-from-scratch的时候,心里其实没底。做后端出身,模型只是调过 API,所谓的“AI 工程”在我脑子里只是一个模糊的拼图:有训练、有部署、有提示词、有 Agent,但不知道它们怎么串成一条…

2026/10/1 15:33:21 阅读更多 →

日新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/30 13:14:22 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 18:13:06 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →