UDP(User Datagram Protocol)是一种无连接的传输层协议,以其低延迟、高效率的特点在实时通信、游戏、流媒体、物联网等领域得到广泛应用。本文介绍如何在 C#中实现 UDP 网络编程,涵盖从基础概念到实际应用。

UDP 协议基础

UDP vs TCP 对比

特性 UDP TCP
连接性 无连接 面向连接
可靠性 不保证送达 可靠传输
速度 快速 相对较慢
开销 低开销 高开销
数据完整性 不保证顺序 保证顺序
适用场景 实时通信、广播 文件传输、Web

UDP 应用场景

  • 在线游戏 - 位置同步、状态更新
  • 流媒体 - 音频、视频实时传输
  • 服务发现 - 局域网设备发现
  • 物联网 - 传感器数据采集
  • 即时通讯 - 语音通话、视频会议
  • 监控系统 - 实时数据收集

基础 UDP 实现

单播 UDP 通信

单播是最常见的 UDP 通信方式,一对一的数据传输。

UDP 服务器实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
public class UdpServer : IDisposable
{
private readonly UdpClient _udpServer;
private readonly IPEndPoint _localEndPoint;
private readonly CancellationTokenSource _cancellationTokenSource;
private bool _isRunning = false;
private bool _disposed = false;

public UdpServer(int port)
{
_localEndPoint = new IPEndPoint(IPAddress.Any, port);
_udpServer = new UdpClient();
_cancellationTokenSource = new CancellationTokenSource();

// 配置Socket选项
_udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_udpServer.Client.Bind(_localEndPoint);
}

/// <summary>
/// 启动UDP服务器
/// </summary>
public async Task StartAsync()
{
if (_isRunning) return;

_isRunning = true;
Console.WriteLine($" UDP服务器启动,监听端口: {_localEndPoint.Port}");

try
{
while (!_cancellationTokenSource.Token.IsCancellationRequested)
{
var result = await _udpServer.ReceiveAsync(_cancellationTokenSource.Token);

// 异步处理接收到的数据,避免阻塞主循环
_ = Task.Run(async () => await ProcessReceivedData(result), _cancellationTokenSource.Token);
}
}
catch (OperationCanceledException)
{
Console.WriteLine(" UDP服务器已停止");
}
catch (Exception ex)
{
Console.WriteLine($" UDP服务器异常: {ex.Message}");
}
finally
{
_isRunning = false;
}
}

/// <summary>
/// 处理接收到的数据
/// </summary>
private async Task ProcessReceivedData(UdpReceiveResult result)
{
try
{
var clientMessage = Encoding.UTF8.GetString(result.Buffer);
var clientEndPoint = result.RemoteEndPoint;
var receiveTime = DateTime.Now;

Console.WriteLine($"[{receiveTime:HH:mm:ss.fff}] 收到来自 {clientEndPoint} 的消息: {clientMessage}");

// 分析消息类型并处理
var response = await ProcessMessage(clientMessage, clientEndPoint);

if (!string.IsNullOrEmpty(response))
{
await SendResponseAsync(response, clientEndPoint);
}
}
catch (Exception ex)
{
Console.WriteLine($" 处理UDP消息时发生错误: {ex.Message}");
}
}

/// <summary>
/// 处理具体的消息内容
/// </summary>
private async Task<string> ProcessMessage(string message, IPEndPoint clientEndPoint)
{
await Task.Delay(50); // 模拟处理时间

// 获取服务器本地IP信息
var serverInfo = GetServerInfo();

// 根据消息类型返回不同的响应
return message.ToLower() switch
{
"ping" => "pong",
"time" => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
"info" => $"服务器信息: {serverInfo}",
"echo" => $"Echo: {message}",
_ => $"服务器已收到消息: {message} | 服务器IP: {serverInfo} | 时间: {DateTime.Now:HH:mm:ss.fff}"
};
}

/// <summary>
/// 发送响应消息
/// </summary>
private async Task SendResponseAsync(string response, IPEndPoint clientEndPoint)
{
try
{
var responseBytes = Encoding.UTF8.GetBytes(response);
await _udpServer.SendAsync(responseBytes, responseBytes.Length, clientEndPoint);

Console.WriteLine($" 向 {clientEndPoint} 发送响应: {response}");
}
catch (Exception ex)
{
Console.WriteLine($" 发送响应失败: {ex.Message}");
}
}

/// <summary>
/// 获取服务器信息
/// </summary>
private string GetServerInfo()
{
try
{
var localIp = IPProvider.GetRealIPv4(); // 自定义方法获取真实IP
var hostName = Environment.MachineName;
return $"{hostName}({localIp})";
}
catch
{
return "Unknown";
}
}

/// <summary>
/// 停止服务器
/// </summary>
public void Stop()
{
if (!_isRunning) return;

Console.WriteLine(" 正在停止UDP服务器...");
_cancellationTokenSource.Cancel();
}

public void Dispose()
{
if (!_disposed)
{
Stop();
_cancellationTokenSource?.Dispose();
_udpServer?.Dispose();
_disposed = true;
}
}
}

UDP 客户端实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class UdpClient : IDisposable
{
private readonly UdpClient _udpClient;
private readonly IPEndPoint _serverEndPoint;
private bool _disposed = false;

public UdpClient(string serverAddress, int serverPort)
{
_serverEndPoint = new IPEndPoint(IPAddress.Parse(serverAddress), serverPort);
_udpClient = new UdpClient();

// 设置超时时间
_udpClient.Client.ReceiveTimeout = 5000; // 5秒超时
}

/// <summary>
/// 发送消息并等待响应
/// </summary>
public async Task<string> SendMessageAsync(string message)
{
try
{
// 发送消息
var messageBytes = Encoding.UTF8.GetBytes(message);
var sentBytes = await _udpClient.SendAsync(messageBytes, messageBytes.Length, _serverEndPoint);

Console.WriteLine($" 向 {_serverEndPoint} 发送消息: {message} ({sentBytes} 字节)");

// 接收响应
var response = await _udpClient.ReceiveAsync();
var responseMessage = Encoding.UTF8.GetString(response.Buffer);

Console.WriteLine($" 收到响应: {responseMessage}");
return responseMessage;
}
catch (Exception ex)
{
Console.WriteLine($" 发送消息失败: {ex.Message}");
return null;
}
}

/// <summary>
/// 批量发送测试消息
/// </summary>
public async Task SendBatchMessagesAsync(int count = 10)
{
Console.WriteLine($" 开始批量发送 {count} 条消息...");

var tasks = new List<Task>();
var stopwatch = Stopwatch.StartNew();

for (int i = 1; i <= count; i++)
{
var message = $"Test message #{i} from {Environment.MachineName}";
tasks.Add(SendMessageAsync(message));

// 避免发送过快
if (i % 5 == 0)
{
await Task.Delay(100);
}
}

await Task.WhenAll(tasks);
stopwatch.Stop();

Console.WriteLine($" 批量发送完成,耗时: {stopwatch.ElapsedMilliseconds} ms");
}

public void Dispose()
{
if (!_disposed)
{
_udpClient?.Dispose();
_disposed = true;
}
}
}

UDP 广播通信

广播允许向网络中的所有设备发送消息,常用于服务发现和网络公告。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
public class UdpBroadcastServer : IDisposable
{
private readonly UdpClient _udpServer;
private readonly int _serverPort;
private readonly CancellationTokenSource _cancellationTokenSource;
private bool _isRunning = false;

public UdpBroadcastServer(int port)
{
_serverPort = port;
_udpServer = new UdpClient();
_cancellationTokenSource = new CancellationTokenSource();

// 配置广播接收
_udpServer.Client.Bind(new IPEndPoint(IPAddress.Any, _serverPort));
_udpServer.EnableBroadcast = true;

// 允许端口重用,支持多个应用监听同一端口
_udpServer.Client.SetSocketOption(
SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress,
true
);
}

/// <summary>
/// 启动广播服务器
/// </summary>
public async Task StartAsync()
{
if (_isRunning) return;

_isRunning = true;
Console.WriteLine($" UDP广播服务器启动,监听端口: {_serverPort}");
Console.WriteLine("可以接收单播和广播消息");

try
{
while (!_cancellationTokenSource.Token.IsCancellationRequested)
{
var result = await _udpServer.ReceiveAsync(_cancellationTokenSource.Token);

_ = Task.Run(async () => await ProcessBroadcastMessage(result),
_cancellationTokenSource.Token);
}
}
catch (OperationCanceledException)
{
Console.WriteLine(" UDP广播服务器已停止");
}
catch (Exception ex)
{
Console.WriteLine($" UDP广播服务器异常: {ex.Message}");
}
finally
{
_isRunning = false;
}
}

/// <summary>
/// 处理广播消息
/// </summary>
private async Task ProcessBroadcastMessage(UdpReceiveResult result)
{
try
{
var clientMessage = Encoding.UTF8.GetString(result.Buffer);
var clientEndPoint = result.RemoteEndPoint;
var receiveTime = DateTime.Now;

// 判断是否为广播消息
bool isBroadcast = IsBroadcastAddress(clientEndPoint.Address);
string messageType = isBroadcast ? "广播" : "单播";

Console.WriteLine($"[{receiveTime:HH:mm:ss.fff}] 收到{messageType}消息");
Console.WriteLine($" 来源: {clientEndPoint}");
Console.WriteLine($" 内容: {clientMessage}");

// 处理特定的广播请求
await HandleBroadcastRequest(clientMessage, clientEndPoint, isBroadcast);
}
catch (Exception ex)
{
Console.WriteLine($" 处理广播消息时发生错误: {ex.Message}");
}
}

/// <summary>
/// 处理广播请求
/// </summary>
private async Task HandleBroadcastRequest(string message, IPEndPoint clientEndPoint, bool isBroadcast)
{
// 对于广播消息,通常不回复,或者选择性回复
if (isBroadcast)
{
if (message.ToLower().Contains("discover") || message.ToLower().Contains("who"))
{
// 服务发现请求,回复设备信息
var deviceInfo = GetDeviceInfo();
await SendResponse(deviceInfo, clientEndPoint);
}
else
{
Console.WriteLine(" 广播消息,不发送响应");
}
}
else
{
// 单播消息,正常响应
var localIp = IPProvider.GetRealIPv4();
var response = $"服务器已收到消息: {message};服务器IP: {localIp}";
await SendResponse(response, clientEndPoint);
}
}

/// <summary>
/// 发送响应
/// </summary>
private async Task SendResponse(string response, IPEndPoint clientEndPoint)
{
try
{
var responseBytes = Encoding.UTF8.GetBytes(response);
await _udpServer.SendAsync(responseBytes, responseBytes.Length, clientEndPoint);
Console.WriteLine($" 已向 {clientEndPoint} 发送响应");
}
catch (Exception ex)
{
Console.WriteLine($" 发送响应失败: {ex.Message}");
}
}

/// <summary>
/// 获取设备信息
/// </summary>
private string GetDeviceInfo()
{
var deviceInfo = new
{
DeviceName = Environment.MachineName,
IPAddress = IPProvider.GetRealIPv4(),
Port = _serverPort,
Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
ServiceType = "UDP Broadcast Server",
Version = "1.0.0"
};

return JsonConvert.SerializeObject(deviceInfo, Formatting.Indented);
}

/// <summary>
/// 检查是否为广播地址
/// </summary>
private bool IsBroadcastAddress(IPAddress address)
{
// 检查是否为255.255.255.255或局域网广播地址
if (address.Equals(IPAddress.Broadcast))
return true;

// 检查是否为局域网广播地址(简单检查)
var bytes = address.GetAddressBytes();
return bytes[3] == 255; // 简单判断,实际应该根据子网掩码计算
}

public void Stop()
{
if (!_isRunning) return;

Console.WriteLine(" 正在停止UDP广播服务器...");
_cancellationTokenSource.Cancel();
}

public void Dispose()
{
Stop();
_cancellationTokenSource?.Dispose();
_udpServer?.Dispose();
}
}

📢 UDP 广播客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
public class UdpBroadcastClient : IDisposable
{
private readonly UdpClient _udpClient;
private readonly int _targetPort;
private bool _disposed = false;

public UdpBroadcastClient(int targetPort)
{
_targetPort = targetPort;
_udpClient = new UdpClient();
_udpClient.EnableBroadcast = true; // 启用广播
}

/// <summary>
/// 发送广播消息
/// </summary>
public async Task SendBroadcastAsync(string message)
{
try
{
var messageBytes = Encoding.UTF8.GetBytes(message);
var broadcastEndPoint = new IPEndPoint(IPAddress.Broadcast, _targetPort);

Console.WriteLine($" 发送广播消息: {message}");
Console.WriteLine($" 目标: {broadcastEndPoint}");

var sentBytes = await _udpClient.SendAsync(messageBytes, messageBytes.Length, broadcastEndPoint);
Console.WriteLine($" 已发送 {sentBytes} 字节");

// 等待并收集响应
await CollectBroadcastResponses();
}
catch (Exception ex)
{
Console.WriteLine($" 发送广播消息失败: {ex.Message}");
}
}

/// <summary>
/// 收集广播响应
/// </summary>
private async Task CollectBroadcastResponses()
{
Console.WriteLine(" 正在收集响应...");
var responses = new List<(IPEndPoint, string)>();
var timeoutTask = Task.Delay(3000); // 3秒超时

try
{
while (!timeoutTask.IsCompleted)
{
var receiveTask = _udpClient.ReceiveAsync();
var completedTask = await Task.WhenAny(receiveTask, timeoutTask);

if (completedTask == receiveTask)
{
var result = await receiveTask;
var responseMessage = Encoding.UTF8.GetString(result.Buffer);
responses.Add((result.RemoteEndPoint, responseMessage));

Console.WriteLine($" 响应来自 {result.RemoteEndPoint}:");
Console.WriteLine($" {responseMessage}");
}
else
{
break; // 超时
}
}
}
catch (Exception ex)
{
Console.WriteLine($" 接收响应时发生错误: {ex.Message}");
}

Console.WriteLine($" 收集完成,共收到 {responses.Count} 个响应");
}

/// <summary>
/// 网络设备发现
/// </summary>
public async Task DiscoverDevicesAsync()
{
Console.WriteLine(" 开始发现网络设备...");

var discoveryMessage = new
{
Type = "Discovery",
From = Environment.MachineName,
Timestamp = DateTime.Now,
Request = "Who is available?"
};

var jsonMessage = JsonConvert.SerializeObject(discoveryMessage);
await SendBroadcastAsync(jsonMessage);
}

/// <summary>
/// 局域网广播测试
/// </summary>
public async Task LocalNetworkBroadcastTest()
{
// 获取本机网络信息
var networkInfo = GetLocalNetworkInfo();

foreach (var networkInterface in networkInfo)
{
Console.WriteLine($" 在网络 {networkInterface.Network} 上广播");

var localBroadcast = new IPEndPoint(networkInterface.BroadcastAddress, _targetPort);
var message = $"Hello from {Environment.MachineName} on {networkInterface.Network}";
var messageBytes = Encoding.UTF8.GetBytes(message);

try
{
await _udpClient.SendAsync(messageBytes, messageBytes.Length, localBroadcast);
Console.WriteLine($" 已发送到 已发送到 {localBroadcast}");
}
catch (Exception ex)
{
Console.WriteLine($" 发送失败: 发送失败: {ex.Message}");
}
}
}

/// <summary>
/// 获取本地网络信息
/// </summary>
private List<NetworkInterfaceInfo> GetLocalNetworkInfo()
{
var networkInterfaces = new List<NetworkInterfaceInfo>();

foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
{
if (ni.OperationalStatus == OperationalStatus.Up &&
ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
{
foreach (var ip in ni.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
// 计算广播地址
var broadcastAddress = CalculateBroadcastAddress(ip.Address, ip.IPv4Mask);

networkInterfaces.Add(new NetworkInterfaceInfo
{
Name = ni.Name,
IPAddress = ip.Address,
SubnetMask = ip.IPv4Mask,
BroadcastAddress = broadcastAddress,
Network = $"{ip.Address}/{ip.PrefixLength}"
});
}
}
}
}

return networkInterfaces;
}

/// <summary>
/// 计算广播地址
/// </summary>
private IPAddress CalculateBroadcastAddress(IPAddress ipAddress, IPAddress subnetMask)
{
var ipBytes = ipAddress.GetAddressBytes();
var maskBytes = subnetMask.GetAddressBytes();
var broadcastBytes = new byte[4];

for (int i = 0; i < 4; i++)
{
broadcastBytes[i] = (byte)(ipBytes[i] | (~maskBytes[i]));
}

return new IPAddress(broadcastBytes);
}

public void Dispose()
{
if (!_disposed)
{
_udpClient?.Dispose();
_disposed = true;
}
}
}

// 网络接口信息辅助类
public class NetworkInterfaceInfo
{
public string Name { get; set; }
public IPAddress IPAddress { get; set; }
public IPAddress SubnetMask { get; set; }
public IPAddress BroadcastAddress { get; set; }
public string Network { get; set; }
}

高级 UDP 应用场景

游戏状态同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
public class GameServer : IDisposable
{
private readonly UdpClient _server;
private readonly Dictionary<IPEndPoint, PlayerInfo> _players = new();
private readonly Timer _gameUpdateTimer;
private readonly object _playersLock = new object();

public GameServer(int port)
{
_server = new UdpClient(port);

// 游戏状态更新定时器(60 FPS)
_gameUpdateTimer = new Timer(BroadcastGameState, null, 0, 16); // ~60 FPS
}

public async Task StartAsync()
{
Console.WriteLine(" 游戏服务器启动...");

while (true)
{
try
{
var result = await _server.ReceiveAsync();
_ = Task.Run(() => ProcessPlayerMessage(result));
}
catch (Exception ex)
{
Console.WriteLine($" 游戏服务器错误: {ex.Message}");
}
}
}

private async Task ProcessPlayerMessage(UdpReceiveResult result)
{
try
{
var message = Encoding.UTF8.GetString(result.Buffer);
var playerData = JsonConvert.DeserializeObject<PlayerMessage>(message);

lock (_playersLock)
{
if (_players.TryGetValue(result.RemoteEndPoint, out var player))
{
// 更新现有玩家
player.Position = playerData.Position;
player.Rotation = playerData.Rotation;
player.LastUpdate = DateTime.Now;
}
else
{
// 新玩家加入
_players[result.RemoteEndPoint] = new PlayerInfo
{
Id = Guid.NewGuid().ToString(),
Name = playerData.PlayerName ?? $"Player_{_players.Count + 1}",
Position = playerData.Position,
Rotation = playerData.Rotation,
EndPoint = result.RemoteEndPoint,
LastUpdate = DateTime.Now
};

Console.WriteLine($" 新玩家加入: {_players[result.RemoteEndPoint].Name}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($" 处理玩家消息错误: {ex.Message}");
}
}

private void BroadcastGameState(object state)
{
try
{
List<PlayerInfo> currentPlayers;
lock (_playersLock)
{
// 移除超时的玩家
var timeout = DateTime.Now.AddSeconds(-5);
var expiredPlayers = _players.Where(p => p.Value.LastUpdate < timeout).ToList();
foreach (var expired in expiredPlayers)
{
_players.Remove(expired.Key);
Console.WriteLine($" 玩家离线: {expired.Value.Name}");
}

currentPlayers = _players.Values.ToList();
}

if (currentPlayers.Count == 0) return;

// 构建游戏状态
var gameState = new GameState
{
Timestamp = DateTime.Now,
Players = currentPlayers.Select(p => new PlayerState
{
Id = p.Id,
Name = p.Name,
Position = p.Position,
Rotation = p.Rotation
}).ToList()
};

var stateJson = JsonConvert.SerializeObject(gameState);
var stateBytes = Encoding.UTF8.GetBytes(stateJson);

// 广播给所有玩家
foreach (var player in currentPlayers)
{
_ = _server.SendAsync(stateBytes, stateBytes.Length, player.EndPoint);
}
}
catch (Exception ex)
{
Console.WriteLine($" 广播游戏状态错误: {ex.Message}");
}
}

public void Dispose()
{
_gameUpdateTimer?.Dispose();
_server?.Dispose();
}
}

// 数据模型
public class PlayerMessage
{
public string PlayerName { get; set; }
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
public string Action { get; set; }
}

public class PlayerInfo
{
public string Id { get; set; }
public string Name { get; set; }
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
public IPEndPoint EndPoint { get; set; }
public DateTime LastUpdate { get; set; }
}

public class GameState
{
public DateTime Timestamp { get; set; }
public List<PlayerState> Players { get; set; }
}

public class PlayerState
{
public string Id { get; set; }
public string Name { get; set; }
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
}

public struct Vector3
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
}

实时数据监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
public class DataCollector : IDisposable
{
private readonly UdpClient _collector;
private readonly ConcurrentDictionary<string, SensorData> _latestData = new();
private readonly Timer _reportTimer;
private readonly List<DataPoint> _dataHistory = new();
private readonly object _historyLock = new object();

public DataCollector(int port)
{
_collector = new UdpClient(port);

// 定期报告定时器
_reportTimer = new Timer(GenerateReport, null, TimeSpan.Zero, TimeSpan.FromSeconds(10));
}

public async Task StartAsync()
{
Console.WriteLine(" 数据收集器启动...");

while (true)
{
try
{
var result = await _collector.ReceiveAsync();
_ = Task.Run(() => ProcessSensorData(result));
}
catch (Exception ex)
{
Console.WriteLine($" 数据收集器错误: {ex.Message}");
}
}
}

private void ProcessSensorData(UdpReceiveResult result)
{
try
{
var jsonData = Encoding.UTF8.GetString(result.Buffer);
var sensorData = JsonConvert.DeserializeObject<SensorData>(jsonData);

sensorData.ReceiveTime = DateTime.Now;
sensorData.SourceEndPoint = result.RemoteEndPoint.ToString();

// 更新最新数据
_latestData.AddOrUpdate(sensorData.SensorId, sensorData, (key, oldValue) => sensorData);

// 添加到历史记录
lock (_historyLock)
{
_dataHistory.Add(new DataPoint
{
SensorId = sensorData.SensorId,
Value = sensorData.Value,
Timestamp = sensorData.ReceiveTime,
Unit = sensorData.Unit
});

// 保持最近1000条记录
if (_dataHistory.Count > 1000)
{
_dataHistory.RemoveAt(0);
}
}

// 实时数据处理
ProcessRealtimeData(sensorData);
}
catch (Exception ex)
{
Console.WriteLine($" 处理传感器数据错误: {ex.Message}");
}
}

private void ProcessRealtimeData(SensorData data)
{
// 异常值检测
if (data.SensorType == "temperature" && data.Value > 50)
{
Console.WriteLine($" 温度异常警告: {data.SensorId} = {data.Value}°C");
}
else if (data.SensorType == "humidity" && (data.Value < 20 || data.Value > 80))
{
Console.WriteLine($" 湿度异常警告: {data.SensorId} = {data.Value}%");
}

// 实时显示
Console.WriteLine($"[{data.ReceiveTime:HH:mm:ss.fff}] {data.SensorId}: {data.Value} {data.Unit}");
}

private void GenerateReport(object state)
{
try
{
Console.WriteLine("\n ========== 数据报告 ==========");
Console.WriteLine($"报告时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"活跃传感器数量: {_latestData.Count}");

foreach (var sensor in _latestData.Values.OrderBy(s => s.SensorId))
{
var timeDiff = DateTime.Now - sensor.ReceiveTime;
var status = timeDiff.TotalSeconds < 30 ? "在线" : "离线";

Console.WriteLine($" {sensor.SensorId}: {sensor.Value} {sensor.Unit} " +
$"({status}, {timeDiff.TotalSeconds:F0}秒前)");
}

// 统计信息
lock (_historyLock)
{
if (_dataHistory.Count > 0)
{
var groupedData = _dataHistory.GroupBy(d => d.SensorId);
Console.WriteLine("\n 统计信息:");

foreach (var group in groupedData)
{
var values = group.Select(d => d.Value).ToList();
var avg = values.Average();
var min = values.Min();
var max = values.Max();

Console.WriteLine($" {group.Key}: 平均={avg:F2}, 最小={min:F2}, 最大={max:F2}");
}
}
}

Console.WriteLine("=====================================\n");
}
catch (Exception ex)
{
Console.WriteLine($" 生成报告错误: {ex.Message}");
}
}

public void Dispose()
{
_reportTimer?.Dispose();
_collector?.Dispose();
}
}

// 传感器数据模型
public class SensorData
{
public string SensorId { get; set; }
public string SensorType { get; set; }
public double Value { get; set; }
public string Unit { get; set; }
public DateTime Timestamp { get; set; }
public DateTime ReceiveTime { get; set; }
public string SourceEndPoint { get; set; }
public Dictionary<string, object> Metadata { get; set; } = new();
}

public class DataPoint
{
public string SensorId { get; set; }
public double Value { get; set; }
public DateTime Timestamp { get; set; }
public string Unit { get; set; }
}

综合应用示例

主程序集成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
public static async Task UdpTest()
{
const int serverPort = 14756;

Console.WriteLine(" UDP网络编程演示");
Console.WriteLine("=".PadRight(50, '='));

while (true)
{
Console.WriteLine("\n选择UDP应用模式:");
Console.WriteLine("1. - 单播服务器");
Console.WriteLine("2. - 单播客户端");
Console.WriteLine("3. - 广播服务器");
Console.WriteLine("4. - 广播客户端");
Console.WriteLine("5. - 设备发现");
Console.WriteLine("6. - 游戏服务器");
Console.WriteLine("7. - 数据收集器");
Console.WriteLine("8. - 性能测试");
Console.WriteLine("9. - 退出");
Console.WriteLine("-".PadRight(30, '-'));
Console.Write("请选择: ");

var choice = Console.ReadLine();

switch (choice)
{
case "1":
await RunUdpServer(serverPort);
break;
case "2":
await RunUdpClient("127.0.0.1", serverPort);
break;
case "3":
await RunBroadcastServer(serverPort);
break;
case "4":
await RunBroadcastClient(serverPort);
break;
case "5":
await RunDeviceDiscovery(serverPort);
break;
case "6":
await RunGameServer(serverPort + 1);
break;
case "7":
await RunDataCollector(serverPort + 2);
break;
case "8":
await RunPerformanceTest(serverPort);
break;
case "9":
return;
default:
Console.WriteLine(" 无效选择");
break;
}
}
}

private static async Task RunUdpServer(int port)
{
using var server = new UdpServer(port);
await server.StartAsync();
}

private static async Task RunUdpClient(string serverIp, int port)
{
using var client = new UdpClient(serverIp, port);

Console.WriteLine(" UDP客户端模式");
Console.WriteLine("输入消息发送到服务器 (输入 'quit' 退出):");

string input;
while ((input = Console.ReadLine()) != "quit")
{
if (!string.IsNullOrEmpty(input))
{
await client.SendMessageAsync(input);
}
}
}

private static async Task RunBroadcastServer(int port)
{
using var server = new UdpBroadcastServer(port);
await server.StartAsync();
}

private static async Task RunBroadcastClient(int port)
{
using var client = new UdpBroadcastClient(port);

Console.WriteLine(" 广播客户端模式");
Console.WriteLine("1. 发送简单广播");
Console.WriteLine("2. 设备发现");
Console.WriteLine("3. 本地网络广播");
Console.Write("选择: ");

var choice = Console.ReadLine();
switch (choice)
{
case "1":
await client.SendBroadcastAsync("Hello, this is a broadcast message!");
break;
case "2":
await client.DiscoverDevicesAsync();
break;
case "3":
await client.LocalNetworkBroadcastTest();
break;
}
}

private static async Task RunPerformanceTest(int port)
{
Console.WriteLine(" UDP性能测试");

using var server = new UdpServer(port);
_ = Task.Run(() => server.StartAsync());

await Task.Delay(1000); // 等待服务器启动

using var client = new UdpClient("127.0.0.1", port);

Console.WriteLine("开始性能测试...");
var stopwatch = Stopwatch.StartNew();

await client.SendBatchMessagesAsync(1000);

stopwatch.Stop();
Console.WriteLine($" 性能测试完成,总耗时: {stopwatch.ElapsedMilliseconds} ms");
Console.WriteLine($" 平均每条消息: {stopwatch.ElapsedMilliseconds / 1000.0:F2} ms");
}

性能优化与经验分享

性能优化技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class OptimizedUdpServer
{
private readonly Socket _socket;
private readonly byte[] _buffer = new byte[8192]; // 预分配缓冲区
private readonly ArrayPool<byte> _arrayPool = ArrayPool<byte>.Shared;

public OptimizedUdpServer(int port)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_socket.Bind(new IPEndPoint(IPAddress.Any, port));

// 优化Socket选项
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 1024 * 1024); // 1MB接收缓冲区
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, 1024 * 1024); // 1MB发送缓冲区
}

public async Task StartHighPerformanceAsync()
{
while (true)
{
try
{
// 使用ValueTask减少内存分配
var result = await ReceiveFromAsync();
_ = ProcessMessageFast(result.buffer, result.length, result.remoteEndPoint);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}

private async ValueTask<(byte[] buffer, int length, EndPoint remoteEndPoint)> ReceiveFromAsync()
{
var buffer = _arrayPool.Rent(8192);
var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);

try
{
var result = await _socket.ReceiveFromAsync(buffer, SocketFlags.None, remoteEndPoint);
return (buffer, result.ReceivedBytes, result.RemoteEndPoint);
}
catch
{
_arrayPool.Return(buffer);
throw;
}
}

private Task ProcessMessageFast(byte[] buffer, int length, EndPoint remoteEndPoint)
{
try
{
// 快速处理消息
var message = Encoding.UTF8.GetString(buffer, 0, length);

// 处理逻辑...

return Task.CompletedTask;
}
finally
{
_arrayPool.Return(buffer);
}
}
}

错误处理与重试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public class ReliableUdpClient
{
private readonly UdpClient _client;
private readonly Dictionary<uint, PendingMessage> _pendingMessages = new();
private uint _messageId = 0;

public async Task<bool> SendReliableAsync(string message, int maxRetries = 3)
{
var id = ++_messageId;
var reliableMessage = new ReliableMessage
{
Id = id,
Content = message,
Timestamp = DateTime.Now
};

for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
await SendMessageWithId(reliableMessage);

// 等待确认
var ackReceived = await WaitForAck(id, TimeSpan.FromSeconds(2));
if (ackReceived)
{
return true;
}

Console.WriteLine($"重试 {attempt}/{maxRetries}...");
}
catch (Exception ex)
{
Console.WriteLine($"发送失败 (尝试 {attempt}): {ex.Message}");
}

if (attempt < maxRetries)
{
await Task.Delay(TimeSpan.FromMilliseconds(Math.Pow(2, attempt) * 100)); // 指数退避
}
}

return false;
}

private async Task<bool> WaitForAck(uint messageId, TimeSpan timeout)
{
var timeoutTask = Task.Delay(timeout);

while (!timeoutTask.IsCompleted)
{
// 检查是否收到确认
if (!_pendingMessages.ContainsKey(messageId))
{
return true; // 已确认
}

await Task.Delay(50);
}

return false; // 超时
}
}

public class ReliableMessage
{
public uint Id { get; set; }
public string Content { get; set; }
public DateTime Timestamp { get; set; }
}

public class PendingMessage
{
public ReliableMessage Message { get; set; }
public DateTime SendTime { get; set; }
public int RetryCount { get; set; }
}

小结

UDP 网络编程在 C#中提供了高效的实时通信能力,适用于多种应用场景:

技术要点总结

  • 高性能 - 低延迟、高吞吐量的数据传输
  • 灵活性 - 支持单播、广播、组播等多种通信模式
  • 实时性 - 适合游戏、监控、流媒体等实时应用
  • 网络发现 - 便于实现服务发现和设备通信
  • 数据收集 - 高效的传感器数据采集和分发

开发经验分享

  1. 合理设置缓冲区大小 - 根据数据量调整接收/发送缓冲区
  2. 实现可靠性机制 - 在应用层添加确认、重传、去重机制
  3. 控制发送频率 - 避免网络拥塞和丢包
  4. 错误处理 - 妥善处理网络异常和超时情况
  5. 性能监控 - 监控延迟、丢包率等关键指标

通过合理使用 UDP 协议,我们可以构建出高效、实时的网络应用程序,满足现代应用对快速数据传输的需求。


选择建议:

  • 实时性要求高: 选择 UDP(游戏、流媒体)
  • 可靠性要求高: 选择 TCP(文件传输、Web)
  • 广播需求: UDP 天然支持广播和组播
  • 简单实现: UDP 协议简单,开发效率高

相关资源: