.Net套接字(UDP)发送、接收和调度

本文关键字:调度 发送 套接字 UDP Net | 更新日期: 2023-09-27 18:28:54

我目前正在进行一个用于学习目的的个人项目。我想通过UDP建立连接,用于游戏等应用程序。发送的每个数据报都有一个特定的报头,指示它属于哪个"逻辑"通道——例如,通道0就像UDP一样,有额外的报头开销,而通道1使用更多的报头来带来一些额外的可靠性。通道的目标是"自动"将消息分为逻辑组,最高可达特定数量。

在我当前的代码中,在处理发送和接收的单独线程中有一个简单的循环:

// This is pseudo code
public void Tick() {
    if(Socket.Poll) {
        do {
            ReadMessage();
        } while(Socket.Available > 0)
    }
    SendQueuedOutgoingMessages();
}

尽管这在理想世界中有效,但我有一种感觉,当传入或传出的消息太多时,这种逻辑就会失败。是否可以使用相同的套接字同时发送和接收消息(即发送和接收是异步的或在不同的线程中)?即使有可能,如果我只使用两个或多个UDP套接字(如果我需要可靠性,可以混合使用TCP和UDP套接字),并考虑到特别的可维护性,会更好吗?

我能想到的最直接的替代方案是使用调度算法,通过队列大小或其他因素来控制读取和发送多少消息,但在这种情况下,这感觉很糟糕,也很不灵活。

编辑:添加有关代码的更多信息。

如果Tick()方法立即返回,则设置为每秒调用特定次数。例如,如果不存在新的输入或输出消息,则每秒30次,如果需要一些时间发送或接收数据,则每秒更少。我使用了阻止ReceiveFrom和SendTo方法来避免繁忙的等待或诸如睡眠(0)之类的呼叫。

虽然我会立即处理传入消息,但我会使用传出消息队列来帮助实现通道的想法——每个通道都有自己的优先级,降至"无优先级",在平稳和繁忙的时刻影响其带宽共享。

.Net套接字(UDP)发送、接收和调度

是否使用两个套接字分别进行接收和发送,或者仅使用一个套接字取决于具体情况。如果要发送大量消息,即使在专用线程上,如果套接字使用的传出队列已满,套接字也可能会阻塞。

这个问题有几种解决办法。使用2个套接字和2个不同的线程是其中之一,将select与异步套接字结合使用是另一种。关键是,你不想仅仅因为发送可能会被阻止就停止接收。

每种可能的解决方案都有其自身的复杂性。

select api用于检查某个套接字是否有可接收的内容,但您也可以使用它来检测套接字是否再次可写。您需要一个套接字选项来将套接字置于非阻塞状态,并且每次发送都需要检查E_WOULDBLOCK返回代码。如果是这样,则发送失败,您必须自己对消息进行排队。

你并不是真的同时发送和接收,而是按顺序发送。通过使用fd_setAPI操作的2个位掩码,您可以使用select来检查套接字是否可写和可读。您甚至可以同时在多个套接字上使用select。然后,如果select(这是一个阻塞调用)返回,您可以检查每个单独的位,以检查需要执行的操作。

如果套接字没有处于非阻塞状态,则发送可能阻塞的原因是套接字的输出队列可能已满。如果套接字被阻塞,它只需等待队列再次准备就绪。但在等待期间,您无法在同一个套接字上接收到任何内容。这就是为什么您需要非阻塞套接字和select api,并结合自己的某种排队机制的原因。

为什么不简单地设置标准的读取循环?

while (true)
    ReadMessage();

不需要调度或节流。不需要知道数据包是否准备好。

您可以在同一个UDP套接字上同时读取和写入。

也不需要传出队列。发送即可。