Search code examples
c#websocketudpclient.net-6.0

receive messages over UDP protocol from multiple clients and stores them in a database with C#


I want to create a self-hosted application that is able to receive messages over UDP protocol from multiple clients and stores them in a database. The sender's IP address and message text are stored in two related tables in a relational database. I'm using .Net 6, but I have problems with the handshake between the multiple clients over the UDP protocol. My idea is to have two Background Services in two separated projects, Sender and Receiver, which contains the logic for sending and receiving data between the clients. The issue is that I can't make the handshake between the clients and after I read all microsoft documentation and mostly all questions here, I'm pretty much stuck over this problem.

This is the documentation about implementing the UDP protocol inside your project. UDP Documentation

This is my Receiver.cs in the Receiver Project.

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;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Receiver running at: {time}", DateTimeOffset.Now);

        while (true)
        {
            await Receiver();
            await Task.Delay(4000, stoppingToken);
        }
    }

    private async Task Receiver()
    {
        using (UdpClient udpClient = new UdpClient(receivePort))
        {
            try
            {
                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);

                Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
                string returnData = Encoding.ASCII.GetString(receiveBytes);

                Console.WriteLine("This is the message you received " +
                                                returnData.ToString());
                Console.WriteLine("This message was sent from " +
                                            RemoteIpEndPoint.Address.ToString() +
                                            " on their port number " +
                                            RemoteIpEndPoint.Port.ToString());

                //udpClient.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            finally
            {
                udpClient.Dispose();
            }
        }
    }
}

This is the Messager.cs which is the Sender

using System.Net.Sockets;
using System.Text;
using System.Net;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Messager;

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    private const int listenPort = 11000;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Sender running at: {time}", DateTimeOffset.Now);
        while(true)
        {
            await Listener();
            await Task.Delay(4000, stoppingToken);
        }
    }

    private async Task Listener()
    {
        using (UdpClient udpClient = new UdpClient(listenPort))
        {
            try
            {
                // or IPAddress.Any
                udpClient.Connect("127.1.0.0", listenPort);

                Byte[] sendBytes = Encoding.ASCII.GetBytes("Is anybody there?");
                await udpClient.SendAsync(sendBytes, sendBytes.Length);

                Console.WriteLine("Sended the message" + sendBytes.ToString());

                udpClient.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            finally
            {
                udpClient.Dispose();
            }
        }
    }
}

When I execute them both via the Terminal or with VS22, at Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint); something is happening, that the debugger cannot catch and everything is just stuck there. The whole idea is to store the sender's IP address and text, but I can't figure it out, how to make the handshake properly, so I can get the messages.

Thanks in advance to all of us! Cheers!!!


Solution

  • It's ok when your receiver hangs on at udpClient.Receive, it means than no one sends data to it. Other story is - what does 127.1.0.0 address mean? Localhost as far as i know has 127.0.0.1 address. Try to change address.

    As a side note, you can use OS tools to get info about listening sockets, just to be sure.

    UPDATE
    Sender code:

    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    
    namespace Sender;
    
    public class SenderWorker : BackgroundService
    {
    private readonly ILogger<SenderWorker> _logger;
    
    private const int listenPort = 11000;
    
    private UdpClient _udpSender;
    
    public SenderWorker(ILogger<SenderWorker> logger)
    {
        _logger = logger;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Sender running at: {time}", DateTimeOffset.Now);
        _udpSender = new UdpClient();
        _udpSender.Connect(IPAddress.Loopback, listenPort);
    
        using (_udpSender)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    Byte[] sendBytes = Encoding.ASCII.GetBytes("Is anybody there?");
                    await _udpSender.SendAsync(sendBytes, sendBytes.Length);
                    Console.WriteLine($"Sent the message: {sendBytes.Length} bytes");
                    await Task.Delay(4000, stoppingToken);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }
    }
    }
    

    Its entry:

    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using Sender;
    
    var services = new ServiceCollection();
    services.AddLogging();
    var logger = services.BuildServiceProvider().GetRequiredService<ILogger<SenderWorker>>();
    var cts = new CancellationTokenSource();
    SenderWorker? worker = null;
    
    try
    {
        worker = new SenderWorker(logger);
        await worker.StartAsync(cts.Token);
        Console.ReadLine();
    }
    catch (Exception e)
    {
      Console.WriteLine(e);
    }
    

    Receiver code:

    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    
    namespace ConsoleApp2.UdpTests;
    
    public class ReceiverWorker : BackgroundService
    {
        private readonly ILogger<ReceiverWorker> _logger;
        private const int receivePort = 11000;
        private UdpClient _udpReceiver;
    
        public ReceiverWorker(ILogger<ReceiverWorker> logger)
        {
            _logger = logger;
        }
    
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("Receiver running at: {time}", DateTimeOffset.Now);
            _udpReceiver = new UdpClient(receivePort);
            var localEndpoint = (IPEndPoint)_udpReceiver.Client.LocalEndPoint;
            Console.WriteLine($"UdpReceiver is bound to: {localEndpoint.Address}:{localEndpoint.Port}");
    
            using (_udpReceiver)
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    await Receive(_udpReceiver);
                }
            }
        }
    
        private async Task Receive(UdpClient receiver)
        {
            try
            {
                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
    
                Byte[] receiveBytes = receiver.Receive(ref RemoteIpEndPoint);
                string returnData = Encoding.ASCII.GetString(receiveBytes);
                Console.WriteLine($"This is the message you received: {returnData} (sent from {RemoteIpEndPoint.Address}:{RemoteIpEndPoint.Port})");
                var ep = (IPEndPoint)_udpReceiver.Client.LocalEndPoint!;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
    

    Receiver's entry:

    using ConsoleApp2.UdpTests;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    
    var services = new ServiceCollection();
    services.AddLogging();
    var logger = services.BuildServiceProvider().GetRequiredService<ILogger<ReceiverWorker>>();
    var cts = new CancellationTokenSource();
    ReceiverWorker? worker = null;
    
    try
    {
        worker = new ReceiverWorker(logger);
        await worker.StartAsync(cts.Token);
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
    }
    

    As a side note, no need to call Task.Delay on receiver's side - it will work with the same cadence as sender, when sender is the only one.

    That works, check it out. Hope it will help.