UDP广播是局域网内设备快速通信的重要手段,它不需要建立稳定的连接,就能将消息同时发送给网段内的所有设备,非常适合设备发现、状态通知这类场景。在C#中可以通过System.Net.Sockets命名空间下的Socket类实现UDP广播的发送和接收,完成局域网内设备的寻址与发现功能。

UDP广播核心原理
UDP广播的地址范围是255.255.255.255,发送到这个地址的数据包会被局域网内所有设备接收。设备发现的基本流程是:待发现的设备定时向广播地址发送包含自身信息的广播包,需要发现设备的客户端监听指定端口,收到广播包后解析出设备信息,完成寻址。
广播发送端实现(设备端)
设备端需要定时向广播地址发送自身信息,比如设备ID、IP地址、服务端口等,代码如下:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace UdpBroadcaster
{
class DeviceBroadcaster
{
// 广播端口,发送和接收端需要保持一致
private const int BROADCAST_PORT = 11000;
// 广播间隔,单位毫秒
private const int BROADCAST_INTERVAL = 2000;
// 设备唯一标识
private string deviceId;
// 设备提供的服务端口
private int servicePort;
public DeviceBroadcaster(string deviceId, int servicePort)
{
this.deviceId = deviceId;
this.servicePort = servicePort;
}
public void StartBroadcast()
{
// 创建UDP Socket
using (Socket broadcastSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
// 设置广播权限
broadcastSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
// 广播地址
IPEndPoint broadcastEndPoint = new IPEndPoint(IPAddress.Broadcast, BROADCAST_PORT);
while (true)
{
try
{
// 构造广播消息,格式:设备ID|设备IP|服务端口
string localIp = GetLocalIPAddress();
string message = $"{deviceId}|{localIp}|{servicePort}";
byte[] sendData = Encoding.UTF8.GetBytes(message);
// 发送广播
broadcastSocket.SendTo(sendData, broadcastEndPoint);
Console.WriteLine($"已发送广播消息:{message}");
}
catch (Exception ex)
{
Console.WriteLine($"广播发送失败:{ex.Message}");
}
// 等待下一个广播周期
Thread.Sleep(BROADCAST_INTERVAL);
}
}
}
// 获取本地局域网IP地址
private string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
return "127.0.0.1";
}
}
}
广播接收端实现(客户端)
客户端需要监听指定的广播端口,接收设备发送的广播消息并解析出设备信息,代码如下:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Collections.Generic;
namespace UdpReceiver
{
class DeviceDiscoverer
{
// 监听的广播端口,需要和发送端一致
private const int LISTEN_PORT = 11000;
// 存储发现的设备信息
private Dictionary<string, DeviceInfo> discoveredDevices = new Dictionary<string, DeviceInfo>();
public void StartDiscovery()
{
// 创建UDP Socket
using (Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
// 绑定监听端口
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, LISTEN_PORT);
listenSocket.Bind(localEndPoint);
// 远程端点,用于接收数据时获取发送方信息
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
Console.WriteLine($"开始监听广播端口 {LISTEN_PORT}...");
while (true)
{
try
{
// 接收数据缓冲区
byte[] receiveData = new byte[1024];
int receiveLength = listenSocket.ReceiveFrom(receiveData, ref remoteEndPoint);
string message = Encoding.UTF8.GetString(receiveData, 0, receiveLength);
// 解析设备信息
ParseDeviceMessage(message, remoteEndPoint as IPEndPoint);
}
catch (Exception ex)
{
Console.WriteLine($"接收广播失败:{ex.Message}");
}
}
}
}
// 解析接收到的设备消息
private void ParseDeviceMessage(string message, IPEndPoint remoteEndPoint)
{
if (string.IsNullOrEmpty(message))
{
return;
}
string[] parts = message.Split('|');
if (parts.Length != 3)
{
return;
}
string deviceId = parts[0];
string deviceIp = parts[1];
int servicePort;
if (!int.TryParse(parts[2], out servicePort))
{
return;
}
// 更新设备信息,避免重复添加
if (!discoveredDevices.ContainsKey(deviceId))
{
discoveredDevices.Add(deviceId, new DeviceInfo
{
DeviceId = deviceId,
DeviceIp = deviceIp,
ServicePort = servicePort,
LastActiveTime = DateTime.Now
});
Console.WriteLine($"发现新设备:ID={deviceId}, IP={deviceIp}, 服务端口={servicePort}");
}
else
{
discoveredDevices[deviceId].LastActiveTime = DateTime.Now;
}
}
// 获取当前发现的设备列表
public List<DeviceInfo> GetDiscoveredDevices()
{
return new List<DeviceInfo>(discoveredDevices.Values);
}
}
// 设备信息实体类
class DeviceInfo
{
public string DeviceId { get; set; }
public string DeviceIp { get; set; }
public int ServicePort { get; set; }
public DateTime LastActiveTime { get; set; }
}
}
注意事项
- 广播端口需要发送端和接收端保持一致,且不能被其他程序占用,建议选择1024以上的端口。
- 局域网内的防火墙可能会拦截UDP广播包,测试时需要关闭对应防火墙规则或者添加端口例外。
- 如果需要区分不同网段的设备,需要调整广播地址,避免广播包被路由器转发到公网。
- 设备信息可以扩展更多字段,比如设备名称、设备类型、支持的服务列表等,根据实际需求调整消息格式即可。
使用示例
设备端启动代码:
class Program
{
static void Main(string[] args)
{
// 初始化设备广播器,设备ID为DEV001,服务端口为8080
DeviceBroadcaster broadcaster = new DeviceBroadcaster("DEV001", 8080);
broadcaster.StartBroadcast();
}
}
客户端启动代码:
class Program
{
static void Main(string[] args)
{
DeviceDiscoverer discoverer = new DeviceDiscoverer();
discoverer.StartDiscovery();
}
}