当前位置: 首页 > 知识库问答 >
问题:

良好的UDP中继服务器设计

逑彬炳
2023-03-14

我正在创建一个UDP服务器,它需要从各种客户端接收UDP数据包,然后将它们转发给其他客户端。我使用C#,所以每个UDP套接字对我来说都是一个Udp客户端。我想我想要2个Udp客户端对象,一个用于接收,一个用于发送。接收套接字将绑定到已知端口,发送者根本不会绑定。

服务器将获取每个数据包,查找数据包html" target="_blank">数据中的用户名,然后根据服务器维护的路由列表,将数据包转发给1个或多个其他客户端。

我开始监听Udp客户端:

   UdpClient udpListener = new UdpClient(new IPEndPoint(ListenerIP, UdpListenerPort));
   udpListener.BeginReceive(new AsyncCallback(OnUDPRead), udpListener);

我的回调看起来像:

    void OnUDPRead(IAsyncResult ar)
    {
        UdpClient udpListener = (UdpClient)ar.AsyncState;

        try
        {
            IPEndPoint remoteEndPoint = null;
            byte[] packet = udpListener.EndReceive(ar, ref remoteEndPoint);

            // Get connection based on data in packet

            // Get connections routing table (from memory)

            // ***Send to each client in routing table***
        }
        catch (Exception ex)
        {
        }

        udpListener.BeginReceive(new AsyncCallback(OnUDPRead), udpListener);
    }

服务器将需要每秒处理数百个数据包(这是针对VOIP的)。我不确定的主要部分是在上面的“发送给每个客户”部分做什么。我应该使用< code>UdpClient。Send或< code>UdpClient。BeginSend?由于这是针对实时协议的,因此对于任何给定的客户端,让多个发送挂起/缓冲是没有意义的。

由于我在任何给定时间都只发布了一个 BeginReceive(),因此我认为我的 OnUDPRead 函数在已经执行时永远不会被调用。除非 UdpClient 在这方面的工作方式与 TcpClient 不同?由于我没有给它一个缓冲区(我没有给它任何缓冲区),我想它可以在只发布一个 BeginReceive 的情况下多次触发 OnUDPRead?

共有3个答案

白星海
2023-03-14

EJP说的话。

几乎所有关于使用异步IO扩展套接字服务器的文献都是针对TCP场景编写的。我去年做了很多关于如何扩展UDP套接字服务器的研究,发现IOCP/epoll不像TCP那样适合UDP。看到这个回答在这里。

因此,仅使用多个线程扩展UDP套接字服务器实际上非常简单,而不是尝试执行任何类型的异步I / O操作。很多时候,一个线程抽出 recvfrom/sendto 调用就足够了。

我建议服务器每个客户端都有一个同步套接字。然后让几个线程做一个套接字。选择调用不同的客户端套接字子集。我想你可以为所有客户端使用一个套接字,但是如果你试图处理来自多个线程的套接字上的音频数据包,你会遇到一些无序的问题。

还可以考虑使用较低级别的socket类而不是UdpClient。

但是如果你真的想横向扩展——你可能想用C/C做核心工作。使用C#,在winsock之上有更多的抽象层意味着更多的缓冲区副本和更高的延迟。有一些技术可以让您使用winsock将数据包从一个套接字转发到另一个套接字,而不会产生缓冲区副本。

缪英锐
2023-03-14

这在很大程度上取决于规模——即您想要中继多少个并发RTP流。我开发了这个解决方案,作为我们SIP交换机的媒体网关的一部分。尽管最初有所保留,但我使用C#让它非常高效地工作(在一台服务器上有1000个并发流,每秒50K个数据报,使用20毫秒PTime)。

通过反复试验,我确定了以下内容:

1) 使用套接字。ReceiveFromAsync比使用同步方法要好得多,因为每个流不需要单独的线程。当接收到数据包时,它们通过线程池进行调度。

2)创建/处置SocketAsyncEventArgs结构需要时间 - 最好在启动时分配一个结构池并从池中“借用”它们。

3)类似地,对于处理期间需要的任何其他对象(例如RTPPacket类)。以如此高的速度动态分配这些内存会导致任何类型的实际负载都很快出现GC问题。如果您避免所有的动态分配,而是使用您自己管理的对象池,这个问题就会消失。

4)如果您需要进行任何计时来协调输入和输出流、代码转换、混音或文件回放,请不要尝试使用标准。网络计时器。我包装了winmm.dll的Win32多媒体定时器——这些要精确得多。

我最终得到了一个架构,其中组件可以公开IRTPSource和IRTPSink接口,然后可以连接这些接口来创建所需的图形。我创建了RTP输入/输出组件,混音器,播放器,录音机,(简单的)代码转换器,播放缓冲区等。然后用这两个接口把它们连接起来。然后,使用MM定时器通过图表记录RTP数据包。

事实证明,C#对于通常使用C或C的方法来说是一个不错的选择。

迈克

蓬琦
2023-03-14

我看不出为什么你不能用一个套接字,简单的方法。在这种情况下,异步I/O似乎没有带来任何惊人的好处。只需使用非阻塞模式。接收数据包,然后发送。如果send()导致EAGAIN或EWOULDBLOCK或其他C#表现形式,请将其删除。

在您的计划中,发送套接字将在第一次调用send()时自动绑定。客户端将回复该端口。因此,它需要是您正在读取的端口。并且您已经拥有其中一个端口。

 类似资料:
  • 程序代码 udp_server.php //创建Server对象,监听 127.0.0.1:9502端口,类型为SWOOLE_SOCK_UDP $serv = new Swoole\Server("127.0.0.1", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); //监听数据接收事件 $serv->on('Packet', function ($serv,

  • 问题内容: 我只是想知道哪种语言是开发支持大量(数千)用户的游戏服务器的理想选择?我涉足python,但意识到这不会给所有内核带来线程(这意味着8核心服务器= 1核心服务器)会带来太多麻烦。我也不太喜欢这种语言(那种“自我”的东西使我很生气)。 我知道C ++是性能方面的工作语言,但我讨厌它。我不想处理它的草率语法,我喜欢我的手被托管语言握住。这使我接触到C#和Java,但是我对其他语言持开放态度

  • 我有一个基于UDP网络的服务器。它有一个流水线,我在其中重写方法。 我需要时不时地写些信息。我只能通过使用中的套接字信息和中的通道来实现。为了能够重用这些信息,我保留在一个静态映射中。 我做错什么了吗? 有没有比将通道和远程地址保留在某个成员中更好的回写方法?

  • 提示 视频 PPT 下载 背景介绍 UDP 协议是用于客户端-服务器模式的一种传输协议,如今的很多通信软件都是利用这个协议实现的,如腾讯 QQ 发送消息用的就是 UDP 协议。 本教程介绍了如何利用 socket 编程来实现一个 UDP 服务器,与客户端进行通信。 UDP 服务器的 socket 编程流程 1. 创建 socket 2. 将创建的 socket 绑定到一个 IP 地址和端口号上 3

  • 我目前正在尝试为我的udp服务器在Android上实现udp打孔。事情应该是这样的: 客户端(在NAT之后;可能是3G,..)向服务器发送DatagramPacket(服务器有一个公共IP;端口也是45555)。客户端重复发送具有给定延迟的数据报 一旦服务器接收到数据报,它每500ms发送一次数据报(“信号”)。 如果穿孔成功,客户端应接收到这些信号 下面是我当前的客户端实现(Android):

  • 我是个新手,如果你能给我建议的话,请告诉我。我有一个向客户端广播消息的服务器。然后客户端将回复发送回服务器。我想用单独的线程处理每个回复。每个回复都有mesage id和thread id。我如何用来自所有线程的信息填充一些结构,然后读取它 也从我的代码,它是正确地创建线程,而还是它存在某种方式来创建线程,只是如果我得到客户端的回复? 我是从正确的理解开始的吗? 非常感谢。