VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • C#中关于UDP协议深度解析(无连接、不可靠、广播与组播)

第二部分:C#网络编程核心技术
第3章 UDP编程实战
3.1 UDP协议深度解析(无连接、不可靠、广播与组播)
一、为什么要学UDP?
我刚做网络编程时,觉得UDP“不可靠”,没用处——直到做实时视频传输项目,用TCP卡成PPT,换成UDP后,流畅度直接拉满。后来才明白:TCP是“可靠的慢车”,UDP是“快速的跑车”,各有各的适vb.net教程C#教程python教程SQL教程access 2010教程用场景。这节就把UDP的底层原理、核心特性、实战代码讲透,让你知道什么时候用UDP,怎么用UDP。
二、UDP的核心特性:无连接、不可靠、快速
UDP(User Datagram Protocol)是无连接的传输层协议,和TCP的核心区别可以用一句话概括:
TCP:先敲门,再说话,说完再告别;UDP:直接喊,不管对方听不听得到。

  1. 无连接:不需要握手,直接发送
    TCP通信前必须三次握手建立连接,UDP不需要——你想发数据,直接把数据打包成“数据报”,发给目标IP和端口就行,不管对方有没有在听,也不管对方是否存在。
    类比:TCP是打电话,必须等对方接了才能说话;UDP是发微信,不管对方有没有看,你直接发就行。
  2. 不可靠:不保证数据到达,不保证顺序
    UDP不提供可靠传输机制:
    数据可能丢失:如果网络拥堵,UDP数据报会直接被丢弃,不会重传;
    数据可能乱序:如果两个UDP数据报走不同的路由,后发的可能先到;
    数据可能重复:如果网络中出现重复的UDP数据报,UDP不会去重。
    类比:UDP是寄明信片,可能丢了,可能寄错顺序,可能收到两张一样的,但寄的时候很快,不需要等对方确认。
  3. 快速:开销小,延迟低
    UDP的报文头部只有8字节(TCP是20字节),没有握手、挥手、重传、滑动窗口等开销,所以传输速度快,延迟低——适合实时性要求高的场景,比如视频通话、游戏、实时监控。
    三、UDP的通信流程:简单到极致
    UDP的通信流程比TCP简单太多,就三步:
    1.创建Socket:指定UDP协议;
    2.发送数据:把数据打包成数据报,发给目标IP和端口;
    3.接收数据:监听指定端口,接收来自任意IP的UDP数据报。
    四、C#实战:UDP客户端与服务器(基础版)
  4. UDP服务器:监听端口,接收数据
    csharp
	using System;
	using System.Net;
	using System.Net.Sockets;
	using System.Text;
	using System.Threading.Tasks;
	
	namespace UdpServerDemo;
	
	class Program
	{
	// 服务器监听的端口
	private const int ListenPort = 8888;
	
	static async Task Main(string[] args)
	{
	// 1. 创建UDP Socket:AddressFamily.InterNetwork(IPv4)、SocketType.Dgram(数据报)、ProtocolType.Udp(UDP协议)
	using var udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
	
	// 2. 绑定IP地址和端口:IPAddress.Any表示监听所有本地IP地址
	var localEndPoint = new IPEndPoint(IPAddress.Any, ListenPort);
	udpSocket.Bind(localEndPoint);
	Console.WriteLine($"UDP服务器已启动,监听端口:{ListenPort}");
	
	// 3. 创建接收缓冲区:UDP数据报最大65507字节(IPv4),所以缓冲区设为65536字节足够
	var buffer = new byte[65536];
	var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); // 用于存储发送方的IP地址和端口
	
	try
	{
	while (true)
	{
	// 4. 异步接收数据:ReceiveFromAsync会阻塞当前线程,直到收到数据
	var receiveResult = await udpSocket.ReceiveFromAsync(buffer, SocketFlags.None, remoteEndPoint);
	var senderEndPoint = receiveResult.RemoteEndPoint as IPEndPoint;
	int bytesRead = receiveResult.ReceivedBytes;
	
	// 5. 解析数据:把字节数组转换成字符串
	string receivedMsg = Encoding.UTF8.GetString(buffer, 0, bytesRead);
	Console.WriteLine($"收到来自 {senderEndPoint?.Address}:{senderEndPoint?.Port} 的消息:{receivedMsg}");
	
	// 6. 发送响应:把收到的消息原封不动发回去
	byte[] responseBytes = Encoding.UTF8.GetBytes($"服务器已收到:{receivedMsg}");
	await udpSocket.SendToAsync(responseBytes, SocketFlags.None, senderEndPoint);
	Console.WriteLine($"已向 {senderEndPoint?.Address}:{senderEndPoint?.Port} 发送响应");
	}
	}
	catch (Exception ex)
	{
	Console.WriteLine($"服务器异常:{ex.Message}");
	}
	// 7. using会自动释放Socket资源,不需要手动Close()
	}
	}

代码逐行讲:
1.new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);:创建UDP Socket,SocketType.Dgram表示数据报套接字,对应UDP协议;
2.udpSocket.Bind(localEndPoint);:绑定IP地址和端口,UDP服务器必须绑定端口,否则无法接收数据;
3.new IPEndPoint(IPAddress.Any, 0);:用于存储发送方的IP地址和端口,ReceiveFromAsync会自动填充这个对象;
4.await udpSocket.ReceiveFromAsync(buffer, SocketFlags.None, remoteEndPoint);:异步接收UDP数据报,返回SocketReceiveFromResult,包含收到的字节数和发送方的IP地址;
5.Encoding.UTF8.GetString(buffer, 0, bytesRead);:把字节数组转换成字符串,注意UDP数据报是完整的,不需要担心粘包(每个数据报是独立的);
6.await udpSocket.SendToAsync(responseBytes, SocketFlags.None, senderEndPoint);:异步发送响应给发送方,不需要建立连接,直接指定目标IP和端口。
2. UDP客户端:发送数据,接收响应
csharp

	using System;
	using System.Net;
	using System.Net.Sockets;
	using System.Text;
	using System.Threading.Tasks;
	
	namespace UdpClientDemo;
	
	class Program
	{
	// 服务器的IP地址和端口
	private const string ServerIp = "127.0.0.1";
	private const int ServerPort = 8888;
	
	static async Task Main(string[] args)
	{
	// 1. 创建UDP Socket
	using var udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
	
	// 2. 目标服务器的IP地址和端口
	var serverEndPoint = new IPEndPoint(IPAddress.Parse(ServerIp), ServerPort);
	
	try
	{
	while (true)
	{
	// 3. 输入要发送的消息
	Console.Write("请输入要发送的消息(输入exit退出):");
	string input = Console.ReadLine() ?? string.Empty;
	if (input.Equals("exit", StringComparison.OrdinalIgnoreCase))
	{
	break;
	}
	
	// 4. 把字符串转换成字节数组
	byte[] sendBytes = Encoding.UTF8.GetBytes(input);
	
	// 5. 异步发送数据到服务器
	await udpSocket.SendToAsync(sendBytes, SocketFlags.None, serverEndPoint);
	Console.WriteLine($"已向 {ServerIp}:{ServerPort} 发送消息:{input}");
	
	// 6. 异步接收服务器的响应
	var buffer = new byte[65536];
	var receiveResult = await udpSocket.ReceiveFromAsync(buffer, SocketFlags.None, new IPEndPoint(IPAddress.Any, 0));
	string responseMsg = Encoding.UTF8.GetString(buffer, 0, receiveResult.ReceivedBytes);
	Console.WriteLine($"收到服务器响应:{responseMsg}");
	}
	}
	catch (Exception ex)
	{
	Console.WriteLine($"客户端异常:{ex.Message}");
	}
	// 7. using会自动释放Socket资源
	}
	}

代码逐行讲:
1.new IPEndPoint(IPAddress.Parse(ServerIp), ServerPort);:指定服务器的IP地址和端口,UDP客户端不需要绑定端口,系统会自动分配一个临时端口;
2.await udpSocket.SendToAsync(sendBytes, SocketFlags.None, serverEndPoint);:异步发送UDP数据报到服务器,不需要建立连接;
3.await udpSocket.ReceiveFromAsync(buffer, SocketFlags.None, new IPEndPoint(IPAddress.Any, 0));:异步接收服务器的响应,注意UDP客户端可能收到来自任意IP的响应,所以需要验证发送方是否是服务器;
4.注意:如果服务器没有启动,客户端发送数据不会报错,因为UDP是无连接的,不知道对方是否存在。
五、UDP的高级特性:广播与组播
UDP除了一对一通信,还支持一对多通信:广播和组播——这是TCP没有的特性,适合需要向多个设备发送数据的场景,比如局域网设备发现、实时数据推送。

  1. 广播:向局域网内所有设备发送数据
    广播是向同一局域网内的所有设备发送数据,目标IP是局域网的广播地址(比如192.168.1.255)。
    C#实战:UDP广播客户端
    csharp
	using System;
	using System.Net;
	using System.Net.Sockets;
	using System.Text;
	using System.Threading.Tasks;
	
	namespace UdpBroadcastClient;
	
	class Program
	{
	// 局域网广播地址(根据你的局域网网段调整)
	private const string BroadcastIp = "192.168.1.255";
	private const int Port = 8888;
	
	static async Task Main(string[] args)
	{
	using var udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
	
	// 必须开启广播权限,否则无法发送广播数据
	udpSocket.EnableBroadcast = true;
	
	var broadcastEndPoint = new IPEndPoint(IPAddress.Parse(BroadcastIp), Port);
	string message = "我是广播消息,局域网内的设备都能收到!";
	byte[] sendBytes = Encoding.UTF8.GetBytes(message);
	
	await udpSocket.SendToAsync(sendBytes, SocketFlags.None, broadcastEndPoint);
	Console.WriteLine($"已发送广播消息:{message}");
	}
	}

代码关键:udpSocket.EnableBroadcast = true;——必须开启广播权限,否则操作系统会拒绝发送广播数据。
2. 组播:向指定组的设备发送数据
组播是向同一组播组内的设备发送数据,目标IP是组播地址(224.0.0.0~239.255.255.255),设备需要加入该组播组才能收到数据。
C#实战:UDP组播服务器(发送组播数据)
csharp

	using System;
	using System.Net;
	using System.Net.Sockets;
	using System.Text;
	using System.Threading.Tasks;
	
	namespace UdpMulticastServer;
	
	class Program
	{
	// 组播地址(224.0.0.0~239.255.255.255)
	private const string MulticastIp = "224.0.0.1";
	private const int Port = 8888;
	
	static async Task Main(string[] args)
	{
	using var udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
	
	var multicastEndPoint = new IPEndPoint(IPAddress.Parse(MulticastIp), Port);
	string message = "我是组播消息,只有加入组播组的设备才能收到!";
	byte[] sendBytes = Encoding.UTF8.GetBytes(message);
	
	// 循环发送组播数据,每秒一次
	while (true)
	{
	await udpSocket.SendToAsync(sendBytes, SocketFlags.None, multicastEndPoint);
	Console.WriteLine($"已发送组播消息:{message}");
	await Task.Delay(1000);
	}
	}
	}

C#实战:UDP组播客户端(加入组播组,接收数据)
csharp

	using System;
	using System.Net;
	using System.Net.Sockets;
	using System.Text;
	using System.Threading.Tasks;
	
	namespace UdpMulticastClient;
	
	class Program
	{
	private const string MulticastIp = "224.0.0.1";
	private const int Port = 8888;
	
	static async Task Main(string[] args)
	{
	using var udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
	
	// 绑定端口,必须和组播服务器的端口一致
	var localEndPoint = new IPEndPoint(IPAddress.Any, Port);
	udpSocket.Bind(localEndPoint);
	
	// 加入组播组
	var multicastAddress = IPAddress.Parse(MulticastIp);
	udpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress));
	
	Console.WriteLine($"已加入组播组 {MulticastIp}:{Port},等待接收消息...");
	
	var buffer = new byte[65536];
	var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
	
	while (true)
	{
	var receiveResult = await udpSocket.ReceiveFromAsync(buffer, SocketFlags.None, remoteEndPoint);
	string message = Encoding.UTF8.GetString(buffer, 0, receiveResult.ReceivedBytes);
	Console.WriteLine($"收到组播消息:{message}");
	}
	}
	}

代码关键:udpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress));——加入组播组,这样才能收到该组播组的数据。
六、UDP的可靠传输:自己造轮子?
UDP本身不可靠,但如果需要可靠的UDP传输,怎么办?可以自己实现可靠传输机制,或者用现成的库(比如KCP、QUIC)。

  1. 手动实现可靠UDP的核心机制
    序列号与确认号:每个UDP数据报加序列号,接收方收到后回复确认号,发送方如果没收到确认号,就重传;
    超时重传:发送方设置超时时间,超过时间没收到确认号,就重传数据报;
    滑动窗口:控制发送速率,避免接收方处理不过来;
    去重:接收方记录已收到的序列号,避免重复处理同一数据报。
    C#简化版示例:
    csharp
	// 发送方:给数据报加序列号
	int sequenceNumber = 0;
	string message = "Hello UDP";
	byte[] messageBytes = Encoding.UTF8.GetBytes(message);
	byte[] sequenceBytes = BitConverter.GetBytes(sequenceNumber);
	byte[] sendBytes = new byte[sequenceBytes.Length + messageBytes.Length];
	Buffer.BlockCopy(sequenceBytes, 0, sendBytes, 0, sequenceBytes.Length);
	Buffer.BlockCopy(messageBytes, 0, sendBytes, sequenceBytes.Length, messageBytes.Length);
	await udpSocket.SendToAsync(sendBytes, SocketFlags.None, serverEndPoint);
	
	// 接收方:验证序列号,回复确认号
	byte[] receiveBuffer = new byte[65536];
	var receiveResult = await udpSocket.ReceiveFromAsync(receiveBuffer, SocketFlags.None, remoteEndPoint);
	int sequenceNumber = BitConverter.ToInt32(receiveBuffer, 0);
	string message = Encoding.UTF8.GetString(receiveBuffer, 4, receiveResult.ReceivedBytes - 4);
	// 回复确认号
	byte[] ackBytes = BitConverter.GetBytes(sequenceNumber + 1);
	await udpSocket.SendToAsync(ackBytes, SocketFlags.None, receiveResult.RemoteEndPoint);
  1. 现成的可靠UDP库
    KCP:轻量级可靠UDP协议,比TCP快,适合游戏、实时通信;
    QUIC:Google开发的基于UDP的可靠传输协议,HTTP/3就是基于QUIC;
    ENet:游戏开发常用的可靠UDP库,支持多通道、流量控制。
    七、TCP vs UDP:怎么选?
场景 选TCP还是UDP? 原因
文件传输、电商支付 TCP 需要可靠传输,不能丢数据
视频通话、游戏 UDP 实时性要求高,丢几个包不影响
局域网设备发现 UDP广播 不需要连接,能快速发现所有设备
实时数据推送 UDP组播 一对多传输,比TCP高效
物联网设备通信 UDP 设备资源有限,UDP开销小

八、总结:UDP不是“垃圾协议”,是“特种工具”
UDP的优势:无连接、开销小、延迟低、支持广播和组播;
UDP的劣势:不可靠、不保证顺序;
适用场景:实时性要求高的场景,比如视频通话、游戏、实时监控;一对多通信的场景,比如局域网设备发现、实时数据推送;资源有限的设备,比如物联网设备。
下一节我们会学习UDP的实战进阶:用KCP实现可靠UDP,用QUIC实现HTTP/3通信,让你既能享受UDP的速度,又能保证数据的可靠。

 本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49514.html


相关教程