跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

上位机TCP与UDP通讯详解:原理、代码与实战

前言 在工业自动化、数据采集、设备监控等领域, 上位机 作为控制系统的核心,负责与下位机(如PLC、传感器、执行器)进行数据交换。

网络通讯是上位机开发的关键技术,其中 TCP(传输控制协议) 和 UDP(用户数据报协议) 是两种最基础、最常用的传输层协议。

理解两者的区别并掌握其编程实现,对于构建稳定、高效的工业通讯系统至关重要。

本文将深入浅出地讲解TCP与UDP的通讯原理、核心差异,并提供C#(基于.NET)和Python两种主流语言的完整代码示例,最后进行总结与选型建议。

1. TCP与UDP核心概念与区别 1.1 TCP(传输控制协议) 连接导向 :通讯前需通过“三次握手”建立稳定连接,通讯结束后“四次挥手”断开连接。

可靠传输 :提供数据确认、重传、排序、流量控制等机制,确保数据包按序、无误地到达。

流式数据 :数据被视为连续的字节流,无明确的消息边界。

适用场景 :对数据完整性要求高、允许一定延迟的场景,如文件传输、远程控制、数据库连接。

1.2 UDP(用户数据报协议) 无连接 :无需建立连接,直接发送数据包。

不可靠传输 :不保证数据包一定到达、按序到达或不重复。

数据报 :每个数据包(Datagram)独立,有明确边界。

适用场景 :对实时性要求高、可容忍少量数据丢失的场景,如视频流、语音通话、实时状态广播。

1.3 对比表格 特性TCPUDP 连接性面向连接无连接可靠性高(确认、重传)低(尽力而为)数据顺序保证顺序不保证顺序传输速度相对较慢(有握手、确认开销)非常快数据边界无(字节流)有(数据报)头部大小较大(20-60字节)较小(8字节)流量控制有(滑动窗口)无拥塞控制有无典型应用HTTP、FTP、SSH、数据库DNS、DHCP、视频流、广播 2. TCP通讯代码示例 2.1 C# (.NET) 示例 TCP 服务器端

using System;

using System.Net; using System.Net.Sockets; using System.Text;

class TcpServer { static void Main() { // 1. 创建监听Socket,绑定IP和端口 IPAddress ip = IPAddress.Parse("127.0.0.1"); TcpListener listener = new TcpListener(ip, 8080); listener.Start(); Console.WriteLine($"TCP服务器已启动,监听 {ip}:8080...");

// 2. 等待客户端连接 TcpClient client = listener.AcceptTcpClient(); Console.WriteLine("客户端已连接!

"); NetworkStream stream = client.GetStream();

// 3. 接收数据 byte[] buffer = new byte[1024]; int bytesRead = stream.Read(buffer, 0, buffer.Length); string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead); Console.WriteLine($"收到消息: {receivedData}");

// 4. 发送响应 string response = "Hello from TCP Server!"; byte[] responseData = Encoding.UTF8.GetBytes(response); stream.Write(responseData, 0, responseData.Length); Console.WriteLine("已发送响应。

");

// 5. 清理 stream.Close(); client.Close(); listener.Stop(); } }

TCP 客户端

using System;

using System.Net.Sockets; using System.Text;

class TcpClientExample { static void Main() { // 1. 创建TcpClient并连接服务器 TcpClient client = new TcpClient(); client.Connect("127.0.0.1", 8080); Console.WriteLine("已连接到服务器!

"); NetworkStream stream = client.GetStream();

// 2. 发送数据 string message = "Hello TCP Server!"; byte[] data = Encoding.UTF8.GetBytes(message); stream.Write(data, 0, data.Length); Console.WriteLine($"已发送: {message}");

// 3. 接收响应 byte[] buffer = new byte[1024]; int bytesRead = stream.Read(buffer, 0, buffer.Length); string response = Encoding.UTF8.GetString(buffer, 0, bytesRead); Console.WriteLine($"服务器响应: {response}");

// 4. 断开连接 stream.Close(); client.Close(); } }

2.2 Python 示例 TCP 服务器端

import socket

# 1. 创建socket对象 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 绑定IP和端口 server_socket.bind(('127.0.0.1', 8080)) # 3. 开始监听 server_socket.listen(1) print("TCP服务器已启动,监听 127.0.0.1:8080...")

# 4. 等待客户端连接 client_socket, client_addr = server_socket.accept() print(f"客户端 {client_addr} 已连接!

")

# 5. 接收数据 data = client_socket.recv(1024) print(f"收到消息: {data.decode('utf-8')}")

# 6. 发送响应 response = "Hello from TCP Server!" client_socket.send(response.encode('utf-8')) print("已发送响应。

")

# 7. 关闭连接 client_socket.close() server_socket.close()

TCP 客户端

import socket

# 1. 创建socket并连接服务器 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect(('127.0.0.1', 8080)) print("已连接到服务器!

")

# 2. 发送数据 message = "Hello TCP Server!" client_socket.send(message.encode('utf-8')) print(f"已发送: {message}")

# 3. 接收响应 data = client_socket.recv(1024) print(f"服务器响应: {data.decode('utf-8')}")

# 4. 关闭连接 client_socket.close()

3. UDP通讯代码示例 3.1 C# (.NET) 示例 UDP 接收端(服务器)

using System;

using System.Net; using System.Net.Sockets; using System.Text;

class UdpReceiver { static void Main() { // 1. 创建UdpClient,绑定端口 UdpClient udpServer = new UdpClient(8081); Console.WriteLine("UDP接收端已启动,监听端口 8081...");

// 2. 接收数据(无连接,直接接收) IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); byte[] receivedBytes = udpServer.Receive(ref remoteEP); string receivedData = Encoding.UTF8.GetString(receivedBytes); Console.WriteLine($"从 {remoteEP} 收到消息: {receivedData}");

// 3. 可选:发送回复 string reply = "UDP Message Received!"; byte[] replyData = Encoding.UTF8.GetBytes(reply); udpServer.Send(replyData, replyData.Length, remoteEP); Console.WriteLine("已发送回复。

");

// 4. 关闭 udpServer.Close(); } }

UDP 发送端(客户端)

using System;

using System.Net; using System.Net.Sockets; using System.Text;

class UdpSender { static void Main() { // 1. 创建UdpClient(无需绑定本地端口) UdpClient udpClient = new UdpClient();

// 2. 目标地址和端口 IPAddress serverIP = IPAddress.Parse("127.0.0.1"); int serverPort = 8081; IPEndPoint serverEP = new IPEndPoint(serverIP, serverPort);

// 3. 发送数据 string message = "Hello UDP Receiver!"; byte[] data = Encoding.UTF8.GetBytes(message); udpClient.Send(data, data.Length, serverEP); Console.WriteLine($"已发送: {message}");

// 4. 可选:接收回复(需设置超时,UDP可能无回复) udpClient.Client.ReceiveTimeout = 2000; // 2秒超时 try { IPEndPoint replyEP = new IPEndPoint(IPAddress.Any, 0); byte[] replyBytes = udpClient.Receive(ref replyEP); string reply = Encoding.UTF8.GetString(replyBytes); Console.WriteLine($"收到回复: {reply}"); } catch (SocketException ex) { Console.WriteLine($"未收到回复: {ex.Message}"); }

// 5. 关闭 udpClient.Close(); } }

3.2 Python 示例 UDP 接收端

import socket

# 1. 创建socket udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 2. 绑定端口 udp_socket.bind(('127.0.0.1', 8081)) print("UDP接收端已启动,监听 127.0.0.1:8081...")

# 3. 接收数据 data, addr = udp_socket.recvfrom(1024) print(f"从 {addr} 收到消息: {data.decode('utf-8')}")

# 4. 可选:发送回复 reply = "UDP Message Received!" udp_socket.sendto(reply.encode('utf-8'), addr) print("已发送回复。

")

# 5. 关闭 udp_socket.close()

UDP 发送端

import socket

# 1. 创建socket udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 2. 目标地址 server_addr = ('127.0.0.1', 8081)

# 3. 发送数据 message = "Hello UDP Receiver!" udp_socket.sendto(message.encode('utf-8'), server_addr) print(f"已发送: {message}")

# 4. 可选:接收回复(设置超时) udp_socket.settimeout(2.0) # 2秒超时 try: data, addr = udp_socket.recvfrom(1024) print(f"收到回复: {data.decode('utf-8')}") except socket.timeout: print("未收到回复(超时)")

# 5. 关闭 udp_socket.close()

4. 关键点解释与注意事项 4.1 TCP粘包与拆包问题 由于TCP是字节流协议,发送方多次发送的数据可能在接收方缓冲区合并(粘包),或一个数据包被拆成多次接收(拆包)。

解决方案 : 固定长度 :每个消息长度固定,不足补位。

分隔符 :使用特殊字符(如

\n

)作为消息边界。

长度前缀 :在消息头部添加固定字节表示后续数据长度。

4.2 UDP数据报大小限制 UDP数据报最大长度受网络MTU(通常1500字节)限制,IP层会分片。

建议应用层数据包控制在 1472字节 以内(1500 - 20 IP头 - 8 UDP头)。

4.3 上位机开发实践建议 TCP选型场景 : 需要可靠传输的控制指令、参数配置、文件上传。

与数据库、Web服务交互。

需要保持长连接、会话状态的场景。

UDP选型场景 : 实时数据采集(传感器数据流)。

视频/音频流传输。

广播/组播(如设备发现)。

对延迟敏感、允许少量丢包的场景。

混合使用 :可结合两者优势,例如用TCP传输控制命令,用UDP传输实时数据。

4.4 代码健壮性增强 异常处理 :所有网络操作都应包裹在

try-catch

中,处理

SocketException

、

TimeoutException

等。

资源释放 :使用

using

语句(C#)或

try-finally

(Python)确保

Socket

、

Stream

被正确关闭。

异步编程 :对于需要高并发或响应的上位机,使用异步API(如C#的

async/await

,Python的

asyncio

)避免界面卡顿。

5. 总结 本文系统介绍了上位机开发中TCP与UDP两种核心网络通讯协议。

TCP 以其可靠性成为需要保证数据完整性的场景的首选,但其连接建立和维护的开销会带来一定的延迟。

UDP 则以其无连接、低延迟的特性,在对实时性要求极高的场景中不可或缺,但需要应用层自行处理丢包、乱序等问题。

选型决策矩阵 : 选TCP :当数据 必须 完整、按序到达,且网络环境稳定或允许重传时。

选UDP :当数据 可以容忍 少量丢失,但对 延迟极其敏感 ,或需要广播/组播功能时。

在实际的上位机项目中,开发者应根据具体的业务需求、网络条件和性能指标,审慎选择协议,甚至可以采用TCP与UDP混合的架构,以兼顾可靠性与实时性。

希望本文提供的原理对比和可直接运行的代码示例,能帮助你快速上手并应用于实际开发中。

(注:以上代码为演示核心流程的简化版本,实际生产环境需考虑线程安全、连接池、心跳机制、断线重连等更多细节。

)

相关文章