【问题标题】:How to switch a ConnectionHandler to UDP如何将 ConnectionHandler 切换到 UDP
【发布时间】:2019-06-24 17:28:04
【问题描述】:

我的 ASP.Net Core 应用程序提供了一个 TCP 侦听器,使用自定义 ConnectionHandler 实现,以从另一台主机上的另一个进程(我们称之为 Datasource)接收二进制数据。然后通过 WebSocket(代码中称为DataSink)将这些数据发送到浏览器。

由于进程Datasource 已从单个 TCP 连接更改为 UDP 数据报,我需要适应(它的内部结构超出我的能力范围)。

如何将当前实现切换到 UDP 侦听器?有没有一种规范的方式是如何使用 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");
    }
}

UDP 侦听器必须在 ASP.Net Core 应用程序运行时可用。

编辑:

数据报传输的顺序和可靠性并不那么重要(也许根本不重要),因为传输的数据是多路复用到 MPEG-TS 中的 MPEG1 流。数据源位于第一台主机上,ASP.Net Core 应用程序位于第二台主机上,接收方/消费者位于第三台主机上。创建流的主机和第三台主机上的接收进程在不同的网络中。 ASP.Net Core 应用程序充当中继。发送者一直在发送,但并不关心数据是否收到。

编辑 2:

现在的主要问题是放置 UdpClient 的位置。之前的实现(当我们使用 TCP 时)配置了 Kestrel 服务器以进行额外的 TCP 侦听,并使用已经提供的 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 >());
    }); 

ConnectionHandler 接受传入的 TCP 连接,然后将流数据转发到多个连接的 WebSocket。由于这不适用于 UDP 数据报,我需要将 UdpClient 放置在它连续(即while(true))接收数据报并将它们转发到 WebSocket 的地方。我的问题是我不知道把它放在哪里,运行后台线程并让通信跨线程,而这个线程间数据流没有任何问题(如竞争条件)。

【问题讨论】:

  • 没有一种单一的方法可以做到这一点。这在很大程度上取决于基于 UDP 的协议。请注意,您需要自己注意顺序和完整性,因为 UDP 既不可靠也不保持顺序(数据包可能无序到达或根本没有到达)。这意味着您需要再次实现请求丢失的数据包,并且您需要实现数据包排序......您还需要弄清楚您的“数据源”是否在线,因为 UDP 也是无连接的。
  • 也就是说,我建议您与数据源团队(或至少其中一个)坐下来,让他们向您解释(应用程序)协议。
  • 应用协议是一个简单的视频流,所以丢包完全没有问题。数据报传输的可靠性并不那么重要。现在,我什至根本不知道在哪里放置UdpListener
  • 你支持多流吗?
  • @Fildor 我添加了一些有用的信息。

标签: c# asp.net-core


【解决方案1】:

所以,总结一下:

我们使用了BackgroundWorkerUdpClient 的组合。 BackgroundWorker 仅在至少有一个接收器时才被实例化:

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

ReceiveStream 是一个私有方法,它建立UdpClient,然后等待需要中继的传入数据。

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;
        }
    }
}

RelayData 方法只是使用每个订阅接收者的传出 TCP 连接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-22
    • 1970-01-01
    相关资源
    最近更新 更多