.NET 6下UDP多客户端消息接收入库的握手问题求助
解决UDP收发阻塞及消息接收问题(.NET 6 Background Service)
问题根源分析
你的代码里不存在UDP握手问题(UDP本身是无连接协议,不需要握手),阻塞和接收失败是由以下几个代码错误导致的:
- Sender端地址错误:连接的
127.1.0.0是无效回环IP,正确本地测试地址应为127.0.0.1 - 端口冲突:Sender端的UdpClient绑定了和Receiver相同的
11000端口,同一机器无法同时有两个程序绑定同一端口 - Receiver端监听逻辑错误:每次创建UdpClient完成一次接收就销毁,且使用同步
Receive方法,搭配Task.Delay会导致监听不连续,容易错过消息 - Sender端输出错误:
sendBytes.ToString()输出的是字节数组的类型名,而非实际发送的文本
修正后的Receiver代码
using System.Net; using System.Net.Sockets; using System.Text; namespace Receiver; public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; private const int receivePort = 11000; private UdpClient? _udpClient; public Worker(ILogger<Worker> logger) { _logger = logger; } public override Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("Receiver running at: {time}", DateTimeOffset.Now); // 初始化一次UdpClient,持续监听 _udpClient = new UdpClient(receivePort); return base.StartAsync(cancellationToken); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { // 使用异步接收方法,避免阻塞主线程 var result = await _udpClient.ReceiveAsync(stoppingToken); string message = Encoding.ASCII.GetString(result.Buffer); IPAddress senderIp = result.RemoteEndPoint.Address; int senderPort = result.RemoteEndPoint.Port; _logger.LogInformation("Received message: {message} from {ip}:{port}", message, senderIp, senderPort); // TODO: 这里添加消息入库逻辑,将senderIp和message存入关联表 // 示例伪代码: // var client = await _dbContext.Clients.FirstOrDefaultAsync(c => c.IpAddress == senderIp.ToString()); // if (client == null) { // client = new Client { IpAddress = senderIp.ToString() }; // _dbContext.Clients.Add(client); // await _dbContext.SaveChangesAsync(); // } // _dbContext.Messages.Add(new Message { ClientId = client.Id, Content = message, CreatedAt = DateTime.UtcNow }); // await _dbContext.SaveChangesAsync(); } catch (OperationCanceledException) { // 服务停止时的预期取消异常,无需处理 } catch (Exception ex) { _logger.LogError(ex, "Error receiving UDP message"); } } } public override async Task StopAsync(CancellationToken cancellationToken) { _udpClient?.Dispose(); _logger.LogInformation("Receiver stopped at: {time}", DateTimeOffset.Now); await base.StopAsync(cancellationToken); } }
修正后的Sender代码
using System.Net; using System.Net.Sockets; using System.Text; namespace Messager; public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; private const int targetPort = 11000; private readonly IPAddress _targetIp = IPAddress.Loopback; // 等价于127.0.0.1 public Worker(ILogger<Worker> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("Sender running at: {time}", DateTimeOffset.Now); while (!stoppingToken.IsCancellationRequested) { await SendMessageAsync(stoppingToken); await Task.Delay(4000, stoppingToken); } } private async Task SendMessageAsync(CancellationToken stoppingToken) { // 不绑定固定端口,让系统自动分配客户端端口,避免和Receiver冲突 using var udpClient = new UdpClient(); try { string message = $"Is anybody there? [{DateTime.Now:HH:mm:ss}]"; byte[] sendBytes = Encoding.ASCII.GetBytes(message); await udpClient.SendAsync(sendBytes, sendBytes.Length, new IPEndPoint(_targetIp, targetPort)); _logger.LogInformation("Sent message: {message}", message); } catch (Exception ex) { _logger.LogError(ex, "Error sending UDP message"); } } }
入库实现要点
- 创建两张关联表:
Clients表:包含Id(主键)、IpAddress(唯一约束)等字段Messages表:包含Id(主键)、ClientId(外键关联Clients.Id)、Content、CreatedAt等字段
- 在Receiver的消息处理逻辑中,先查询或创建对应的Client记录,再插入Message记录
- 使用EF Core操作数据库时,注意异步方法的使用,避免阻塞监听逻辑
内容的提问来源于stack exchange,提问作者ddxkalin
相关产品推荐
相关产品推荐

