C# .NET 网络 & 串口通信
C# .NET 网络 & 串口通信(上位机全套完整版思维导图笔记)
中心主题:C# .NET 网络通信 + 串口通信(上位机开发全套) 四大主干:原生 Socket | TcpClient/TcpListener | UdpClient | 第三方 SDK 新增补充模块:串口 SerialPort、获取本机 IP、获取串口列表、端口操作、开发做题 / 项目标准顺序、全套可运行示例、踩坑清单
📋 前置命名空间汇总(所有代码通用)
csharp
运行
//网络通信必引
using System;
using System.Net;
using System.Net.Sockets;
//串口必引
using System.IO.Ports;
//其他辅助
using System.Linq;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
✅ 项目开发标准做题顺序(重点!面试 / 做毕设必按这个流程)
不管做 TCP 服务端、UDP 网关、串口 Modbus、海康 SDK,固定开发顺序
- 需求分析:确定通信类型(TCP?UDP?串口?第三方 SDK)、协议(Modbus-RTU / 自定义帧)、交互时序
- 获取硬件 / 网络资源
- 串口:枚举本机所有 COM 口
- 网络:获取本机 IP、选择监听端口
- 日志模块优先实现(先写 LogHelper,SDK 类优先开启底层日志,日志放在最前面!)
- 资源对象定义(全局:SerialPort / UdpClient / TcpListener / Socket / SDK 句柄、CancellationTokenSource 取消令牌)
- UI 绑定、参数校验(IP、端口、波特率非空判断)
- 通信初始化 + 开启监听 / 连接
- 异步接收循环(后台线程接收,严禁阻塞 UI)
- 协议解析(粘包处理 / CRC 校验 / 帧解析)
- 业务逻辑(云台、读写寄存器、截图录像等)
- 资源释放!(关闭串口、停止监听、Logout、Cleanup、Dispose)
- 异常捕获 + 写入日志
- 测试边界:断网、串口拔出、重复点击连接
口诀:日志先行 → 枚举资源 → 初始化 → 异步收 → 解析协议 → 业务 → 释放资源
分支 0【新增必备工具模块】上位机高频工具:串口枚举、本机 IP、端口
0.1 获取本机所有串口 COM 列表(SerialPort)
API
SerialPort.GetPortNames() → 返回string[],直接拿到本机 COM1、COM2...
完整示例(WinForm,加载到下拉框 cbbCom)
csharp
运行
/// <summary>获取本机所有串口,绑定到下拉框</summary>
private void LoadComList()
{
//拿到所有COM名称数组
string[] comArr = SerialPort.GetPortNames();
cbbCOMPort.Items.Clear();
cbbCOMPort.Items.AddRange(comArr);
if(comArr.Length>0) cbbCOMPort.SelectedIndex = 0;
}
//窗体加载时调用
private void Form_Load(object sender,EventArgs e)
{
LoadComList();
}
✅ 知识点:
- 依赖
System.IO.Ports - 插拔串口不会自动刷新,可加【刷新串口】按钮重新执行
GetPortNames() - 不存在的 COM 直接 Open 会抛异常,必须 try-catch
0.2 SerialPort 串口全套 API(Modbus-RTU 串口核心)
表格
| API | 功能 |
|---|---|
SerialPort(端口,波特率,校验位,数据位,停止位) |
构造串口对象 |
Open() |
打开串口 |
Close() / Dispose() |
关闭释放 |
Write(byte[],offset,count) |
发送二进制(Modbus-RTU 字节) |
Read(byte[],offset,count) |
同步读取 |
DataReceived |
串口接收事件(后台 IO 线程,禁止直接操作 UI!要用 Invoke) |
BytesToRead |
当前缓冲区字节数 |
最简 Modbus 串口示例
csharp
运行
SerialPort serial = new SerialPort("COM3",9600,Parity.None,8,StopBits.One);
serial.Open();
byte[] modbusFrame = {0x01,0x03,0x00,0x00,0x00,0x04,0x44,0x09};
serial.Write(modbusFrame,0,modbusFrame.Length);
//DataReceived事件接收设备应答
⚠️坑:DataReceived不在 UI 线程,更新控件必须Invoke
0.3 获取本机 IP 地址(上位机网关、服务端绑定 IP 用)
方式 1:获取本机第一个 IPv4(你之前 IPHelper 版本)
csharp
运行
public static IPAddress GetLocalIPv4()
{
IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName());
foreach(var ip in hostEntry.AddressList)
{
//只取IPv4,排除IPv6
if(ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip;
}
}
return null;
}
调用:IPAddress localIp = GetLocalIPv4();
方式 2:枚举所有网卡全部 IPv4(多网卡电脑必备,推荐上位机使用)
csharp
运行
/// <summary>获取本机全部IPv4,返回列表</summary>
public static List<IPAddress> GetAllLocalIPv4()
{
List<IPAddress> ipList = new List<IPAddress>();
foreach(var netInterface in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces())
{
//跳过回环、禁用、虚拟网卡
if(netInterface.OperationalStatus != System.Net.NetworkInformation.OperationalStatus.Up) continue;
foreach(var unicast in netInterface.GetIPProperties().UnicastAddresses)
{
if(unicast.Address.AddressFamily == AddressFamily.InterNetwork)
{
ipList.Add(unicast.Address);
}
}
}
return ipList;
}
✅适用:多网卡(有线 + 无线),下拉框给用户手动选择本机 IP
0.4 端口相关知识点
- 端口范围:1~65535;1-1023 知名端口,尽量不用,上位机建议用 8000、8080、9000 以上
- 端口占用判断(可选,启动服务前检测端口是否被占用)
csharp
运行
/// <summary>检测端口是否被占用</summary>
public static bool IsPortInUse(int port)
{
try
{
//尝试绑定该端口,失败=占用
using(Socket testSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp))
{
testSocket.Bind(new IPEndPoint(IPAddress.Any,port));
return false;
}
}
catch
{
return true;
}
}
分支 1:原生 Socket(底层,TCP/UDP 两套)
命名空间:
System.Net.Sockets本质:操作系统原生套接字,TcpClient/UdpClient 内部都是封装 Socket,自由度最高
1.1 Socket TCP
核心 API
表格
| API | 说明 |
|---|---|
Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp) |
TCP 构造 |
| Bind() | 服务端绑定 IP + 端口 |
| Listen(int backlog) | 开始监听,backlog = 等待连接队列长度 |
| Accept() / AcceptAsync() | 阻塞 / 异步接受客户端连接,返回新 Socket |
| Connect() / ConnectAsync() | 客户端发起连接(三次握手) |
| Send() / SendAsync() | 发送字节流 |
| Receive() / ReceiveAsync() | 接收字节流 |
| Shutdown(SocketShutdown.Both) | 优雅关闭 |
| Close() / Dispose() | 释放 |
✅特点:面向连接、可靠、有序、存在粘包 ✅TCP 服务端最简示例
csharp
运行
//1.获取本机IP、定义端口
IPAddress localIp = GetLocalIPv4();
int port = 8080;
IPEndPoint serverEp = new IPEndPoint(localIp,port);
//2.创建TCP Socket
Socket serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
serverSocket.Bind(serverEp);
serverSocket.Listen(10);
//3.等待客户端
Socket clientSocket = await serverSocket.AcceptAsync();
//收发
byte[] buffer = new byte[1024];
int recvLen = await clientSocket.ReceiveAsync(new ArraySegment<byte>(buffer),SocketFlags.None);
1.2 Socket UDP
核心 API
表格
| API | 说明 |
|---|---|
Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp) |
UDP 构造 |
| Bind() | UDP 服务端绑定本地端口 |
| SendTo(byte[],SocketFlags,EndPoint) | 指定远端端点发送 |
| ReceiveFrom(byte[],SocketFlags,ref EndPoint) | 接收,拿到发送方 RemoteEp(网关原路回包核心) |
✅特点:无连接、不可靠、无粘包、支持广播 ✅UDP Socket 网关最简示例(Modbus UDP 转串口)
csharp
运行
Socket udpSocket = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
IPEndPoint localEp = new IPEndPoint(IPAddress.Any,8080);
udpSocket.Bind(localEp);
IPEndPoint remoteEp = new IPEndPoint(IPAddress.Any,0);
byte[] buf = new byte[1024];
int len = udpSocket.ReceiveFrom(buf,SocketFlags.None,ref remoteEp);
//下发串口 → 拿到应答resp → SendTo原路返回
udpSocket.SendTo(resp,SocketFlags.None,remoteEp);
分支 2:TcpClient / TcpListener(TCP 封装,上位机 TCP 首选)
仅支持 TCP!封装 Socket,代码简洁,适合 ModbusTCP、TCP 设备
2.1 TcpListener(TCP 服务端)API
表格
| API | 功能 |
|---|---|
TcpListener(IPAddress localIp,int port) |
构造监听对象 |
| Start() | 启动监听(内部 Bind+Listen) |
| AcceptTcpClient() / AcceptTcpClientAsync() | 等待客户端,返回 TcpClient |
| Stop() | 停止监听 |
2.2 TcpClient(TCP 客户端)API
表格
| API | 功能 |
|---|---|
| TcpClient() | 构造 |
| Connect(ip,port) / ConnectAsync | 建立 TCP 连接 |
| NetworkStream GetStream() | 获取流,Write 写、Read 读 |
| Close() / Dispose() | 释放 |
✅示例 TCP 客户端
csharp
运行
TcpClient tcpClient = new TcpClient();
await tcpClient.ConnectAsync("127.0.0.1",8080);
NetworkStream stream = tcpClient.GetStream();
byte[] sendData = Encoding.UTF8.GetBytes("test");
await stream.WriteAsync(sendData,0,sendData.Length);
⚠️重点坑:TCP一定会粘包!自定义协议必须加【帧头 + 长度】做分包解析
分支 3:UdpClient(UDP 封装,你的 UDP-Modbus 网关项目)
封装 Socket Dgram,专门简化 UDP,无连接
3.1 核心 API
表格
| API | 功能 |
|---|---|
UdpClient(int port) |
服务端:绑定本地端口 |
UdpClient() |
UDP 客户端 |
Receive(ref IPEndPoint) / ReceiveAsync() |
接收,自动拿到远端 RemoteEp |
| Send(byte[],int,IPEndPoint) / SendAsync | 指定远端发送 |
| Connect(ip,port) | 可选:锁定默认远端,后续 Send 不用传 ep |
| Close() / Dispose() | 释放 |
✅示例 UDP 网关(和你项目一模一样)
csharp
运行
UdpClient udpServer = new UdpClient(8080);
IPEndPoint remoteEp = null;
//异步接收
UdpReceiveResult result = await udpServer.ReceiveAsync();
byte[] recvFrame = result.Buffer;
remoteEp = result.RemoteEndPoint;
//串口下发 → 获取应答respBuf
udpServer.Send(respBuf, respBuf.Length, remoteEp);
✅关键点:UDP 应答必须使用收到包时拿到的 RemoteEp 原路返回
分支 4:第三方 SDK(海康摄像头、工业相机、NModbus 等)
不直接操作 Socket,厂商封装底层网络、码流、私有协议,C# 通过
DllImport调用 C 库
4.1 海康 CHCNetSDK(你的项目全套 API)
表格
| API | 调用顺序说明 |
|---|---|
| NET_DVR_SetLogToFile | ✅最先调用,必须在 Init 之前,开启 SDK 底层日志 |
| NET_DVR_Init | SDK 全局初始化 |
| NET_DVR_Login_V40 | 登录摄像头设备,返回 UserID |
| NET_DVR_RealPlay_V40 | 预览取流,返回 RealHandle |
| NET_DVR_PTZControlWithSpeed | 云台控制 |
| NET_DVR_CaptureJPEGPicture | JPEG 抓图 |
| NET_DVR_SaveRealData | 开始录像 |
| NET_DVR_StopSaveRealData | 停止录像 |
| NET_DVR_StopRealPlay | 停止预览 |
| NET_DVR_Logout | 退出登录 |
| NET_DVR_Cleanup | 程序退出最后执行,释放 SDK |
| NET_DVR_GetLastError | 获取错误码,配合日志 |
✅标准调用顺序:SetLogToFile → Init → Login → 预览/云台/抓图 → Stop → Logout → Cleanup
4.2 第三方 SDK 通用知识点
- 大多是非托管 C++dll,需要
[DllImport]导入 - 句柄 / UserId 必须妥善保存,用完必须释放,极易内存泄漏
- 优先开启 SDK 自带底层日志,再叠加自己业务 LogHelper
📌 全模块横向对比总表(背诵)
表格
| 类型 | 底层 | 连接 | 粘包 | 典型场景 |
|---|---|---|---|---|
| Socket(TCP) | 原生套接字 | 有 | ✅会粘包 | 高性能自定义 TCP 协议 |
| Socket(UDP) | 原生套接字 | 无 | ❌无 | UDP 广播、极简网关 |
| TcpClient/TcpListener | 封装 Socket (TCP) | 有 | ✅会粘包 | ModbusTCP、普通 TCP 上位机 |
| UdpClient | 封装 Socket (UDP) | 无 | ❌无 | UDP-ModbusRTU 网关 |
| SerialPort 串口 | 系统串口驱动 | 无 | ❌按字节流 | Modbus-RTU485/232 |
| 第三方 SDK | 厂商封装 | SDK 内部处理 | SDK 内部处理 | 海康摄像头、工业相机 |
📌 通用高频坑汇总(上位机必避)
- TCP:粘包!必须设计帧结构(例:0xAA 0xBB + 长度 + 数据 + CRC)
- UDP:记住 RemoteEp,应答原路回包;不保证送达
- SerialPort:DataReceived 事件不在 UI 线程 → Invoke 更新控件;用完 Close
- 本机 IP:多网卡不要写死 127.0.0.1,优先枚举网卡选择
- 端口:启动前判断占用;防火墙放行
- 海康 SDK:SetLogToFile必须在 Init 前面;窗体关闭必须逐级释放句柄
- 所有异步循环接收:配合
CancellationTokenSource取消,窗体关闭停止循环,防止后台死循环
📌 配套工具类整合(一次性复制,直接放到项目)
csharp
运行
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.IO.Ports;
namespace _1
{
public static class NetSerialHelper
{
#region IP工具
/// <summary>获取本机第一个IPv4</summary>
public static IPAddress GetLocalIPv4()
{
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
return host.AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
}
/// <summary>获取本机全部网卡IPv4</summary>
public static List<IPAddress> GetAllLocalIPv4()
{
List<IPAddress> list = new List<IPAddress>();
foreach(var ni in NetworkInterface.GetAllNetworkInterfaces())
{
if(ni.OperationalStatus != OperationalStatus.Up) continue;
foreach(var addr in ni.GetIPProperties().UnicastAddresses)
{
if(addr.Address.AddressFamily == AddressFamily.InterNetwork)
list.Add(addr.Address);
}
}
return list;
}
/// <summary>检测TCP端口是否占用</summary>
public static bool IsTcpPortUsed(int port)
{
try
{
using(Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp))
{
s.Bind(new IPEndPoint(IPAddress.Any,port));
return false;
}
}
catch { return true; }
}
#endregion
#region 串口工具
/// <summary>获取本机所有COM口</summary>
public static string[] GetAllComPort()
{
return SerialPort.GetPortNames();
}
#endregion
}
//日志帮助类(业务日志,你海康项目在用)
public static class LogHelper
{
public static void WriteInfo(string msg)=>Write(msg,false);
public static void WriteError(string msg)=>Write(msg,true);
public static void WriteHkError(string op,uint errCode)=>Write($"【{op}】海康错误码:{errCode}",true);
private static void Write(string msg,bool isError)
{
try
{
string dir = Path.Combine(Application.StartupPath,"AppLog");
if(!Directory.Exists(dir)) Directory.CreateDirectory(dir);
string file = Path.Combine(dir,DateTime.Now.ToString("yyyy-MM-dd")+".txt");
string level = isError ? "[ERROR]" : "[INFO]";
string line = $"{DateTime.Now:HH:mm:ss} {level} {msg}{Environment.NewLine}";
File.AppendAllText(file,line,Encoding.UTF8);
}catch{}
}
}
}
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)