You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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");
        }
    }
}

入库实现要点

  1. 创建两张关联表:
    • Clients表:包含Id(主键)、IpAddress(唯一约束)等字段
    • Messages表:包含Id(主键)、ClientId(外键关联Clients.Id)、Content、CreatedAt等字段
  2. 在Receiver的消息处理逻辑中,先查询或创建对应的Client记录,再插入Message记录
  3. 使用EF Core操作数据库时,注意异步方法的使用,避免阻塞监听逻辑

内容的提问来源于stack exchange,提问作者ddxkalin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 02:40:34