简体   繁体   中英

How to switch a ConnectionHandler to UDP

My ASP.Net Core application provides a TCP listener, implemented with a custom ConnectionHandler , to receive binary data from another process (let's call it Datasource ) on another host. This data is then sent to the browser through a WebSocket (called DataSink in the code).

Since the process Datasource has changed from a single TCP connection to UDP datagrams, I need to adapt (its internals are out of my reach).

How can I switch the current implementation to an UDP listener? Is there a canonical way how this is done with ASP.Net Core?

public class MySpecialConnectionHandler : ConnectionHandler
{
    private readonly IMyDataSink DataSink;

    public MySpecialConnectionHandler(IMyDataSink dataSink)
    {
        DataSink = dataSink;
    }

    public override async Task OnConnectedAsync(ConnectionContext context)
    {
        TransportConnection connection = context as TransportConnection;

        Console.WriteLine("new connection: " + connection.RemoteAddress + ":" + connection.RemotePort);

        while (true)
        {
            var result = await connection.Transport.Input.ReadAsync().ConfigureAwait(false);
            var buffer = result.Buffer;

            foreach (var segment in buffer)
            {
                await DataSink.RelayData(segment.Span.ToArray()).ConfigureAwait(false);
            }

            if (result.IsCompleted)
            {
                break;
            }

            connection.Transport.Input.AdvanceTo(buffer.End);
        }

        Console.WriteLine(connection.ConnectionId + " disconnected");
    }
}

The UDP listener must be available while the ASP.Net Core application is running.

EDIT:

Order and reliability of the datagram transmission is not that important (perhaps not at all), since the transmitted data is a MPEG1-stream multiplexed into MPEG-TS. The data source is on the first host, the ASP.Net Core application is on a second host and the receiver / consumer is a third host. The host creating the stream and the receiving process on the third host are in separate networks. The ASP.Net Core application acts as a relay. The sender is sending all time, but does not care about whether the data is received or not.

EDIT 2:

The main problem right now is where to put the UdpClient. The previous implementation (back when we used TCP) configured the Kestrel server for additional TCP listening and used the already presented ConnectionHandler :

return WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
    .ConfigureKestrel((_, options) =>
    {
        // HTTP
        options.Listen(networkInterface, httpPort);

        // HTTPS
        options.Listen(networkInterface, httpsPort, builder => builder.UseHttps());

        // stream sink
        options.Listen(streamInterface, streamPort, builder => builder.UseConnectionHandler<MySpecialConnectionHandler >());
    }); 

The ConnectionHandler accepts the incoming TCP connection and then forwards the streaming data to a number of connected WebSockets. Since this is not usable with UDP datagrams, I need to place the UdpClient somewhere where it continuously (ie while(true) ) receives datagrams and forwards them to the WebSockets. My problem is that I don't know where to put it, run the background thread and have the communication span threads without having any problems with this inter-thread data flow (like race conditions).

So, to conclude this:

We used a combination of a BackgroundWorker with an UdpClient . The BackgroundWorker is only instantiated when there is at least one receiver:

StreamReceiver = new BackgroundWorker();
StreamReceiver.DoWork += ReceiveStream;
StreamReceiver.RunWorkerAsync();

ReceiveStream is a private method that establishes the UdpClient and then waits for incoming data that needs to be relayed.

private async void ReceiveStream(object sender, DoWorkEventArgs e)
{
    // DataSinkPort is a const int
    UdpClient datasource = new UdpClient(_DataSinkPort);

    while (true)
    {
        var rec = await datasource.ReceiveAsync();
        await RelayData(rec.Buffer);

        if (_CancellationToken.IsCancellationRequested)
        {
            return;
        }
    }
}

The method RelayData just uses the outgoing TCP connection of each subscribed receiver.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM