.Net Socket (UDP) 发送、接收和调度
.Net Socket (UDP) Sending, Receiving and Scheduling
我目前正在进行一个以学习为目的的个人项目。我想通过 UDP 建立连接,用于游戏等应用程序。发送的每个数据报都有一个特定的 header 指示它属于哪个 "logic" 通道 - 例如,通道 0 就像 UDP 一样具有额外的 header 开销,而通道 1 使用更多 headers 带来一些额外的可靠性。通道 objective 用于 "automatically" 将消息分成逻辑组,最多达到特定数量。
在我当前的代码中,在处理发送和接收的单独线程中有一个简单循环:
// This is pseudo code
public void Tick() {
if(Socket.Poll) {
do {
ReadMessage();
} while(Socket.Available > 0)
}
SendQueuedOutgoingMessages();
}
虽然这适用于理想世界,但我感觉当传入或传出消息过多时,此逻辑会失败。是否可以使用同一个套接字同时发送和接收消息(即发送和接收是异步的或在不同的线程中)?即使可能,如果我简单地使用两个或多个 UDP 套接字(或者混合使用 TCP 和 UDP 套接字,如果我需要可靠性),并特别考虑可维护性,会更好吗?
我能想到的最直接的替代方案是使用调度算法通过 queue 大小或其他因素来控制要读取和发送的消息数量,但这感觉很差且不灵活在这种情况下。
编辑:添加有关代码的更多信息。
如果立即 returns,Tick() 方法被设置为每秒调用特定次数。例如,如果不存在新的进出消息,则每秒 30 次,如果需要一些时间来发送或接收数据,则每秒 30 次。我已经使用阻塞 ReceiveFrom 和 SendTo 方法来避免繁忙的等待或诸如 Sleep(0) 之类的调用。
虽然我会立即处理传入消息,但我会使用传出消息 queue 来帮助实现频道想法 - 每个频道都有自己的优先级,低至 'no priority',会随着时间的推移影响其带宽份额顺利而忙碌的时刻。
为什么不简单地使用标准读取循环设置?
while (true)
ReadMessage();
无需安排或限制。没有必要知道数据包是否就绪。
您可以在同一个 UDP 套接字上同时读取和写入。
也不需要传出队列。发送即可。
是否使用2个socket分别接收和发送,或者只使用一个,视情况而定。如果您要发送大量消息,即使您在专用线程上,如果套接字使用的传出队列已满,套接字也可能会阻塞。
这个问题有几种解决方法。使用 2 个套接字和 2 个不同的线程是其中之一,将 select 与异步套接字结合使用是另一种方法。关键是您不想因为发送可能会阻塞而停止接收。
这些可能的解决方案中的每一个都有其自身的复杂性。
select api 用于检查某个套接字是否有要接收的内容,但您也可以使用它来检测套接字是否再次可写。您需要一个套接字选项来将套接字置于非阻塞状态,并且您需要在每次发送时检查 E_WOULDBLOCK return 代码。如果是这样,发送失败,您必须自己将消息排队。
你并不是真的同时发送和接收,它是顺序的。您使用 select 通过使用 fd_set api 操作的 2 个位掩码来检查套接字是否可写和可读。您甚至可以同时在多个套接字上使用 select。然后,如果 select,这是一个阻塞调用,returns,您可以检查每个单独的位以检查需要执行哪些操作。
如果套接字未处于非阻塞状态,发送可以阻塞的原因是套接字的输出队列可能已满。如果套接字阻塞,它只会等待队列再次准备就绪。但是在那段等待期间,您无法在同一个套接字上收到任何东西。这就是为什么您需要非阻塞套接字和 select api 以及您自己结合某种排队机制的原因。
我目前正在进行一个以学习为目的的个人项目。我想通过 UDP 建立连接,用于游戏等应用程序。发送的每个数据报都有一个特定的 header 指示它属于哪个 "logic" 通道 - 例如,通道 0 就像 UDP 一样具有额外的 header 开销,而通道 1 使用更多 headers 带来一些额外的可靠性。通道 objective 用于 "automatically" 将消息分成逻辑组,最多达到特定数量。
在我当前的代码中,在处理发送和接收的单独线程中有一个简单循环:
// This is pseudo code
public void Tick() {
if(Socket.Poll) {
do {
ReadMessage();
} while(Socket.Available > 0)
}
SendQueuedOutgoingMessages();
}
虽然这适用于理想世界,但我感觉当传入或传出消息过多时,此逻辑会失败。是否可以使用同一个套接字同时发送和接收消息(即发送和接收是异步的或在不同的线程中)?即使可能,如果我简单地使用两个或多个 UDP 套接字(或者混合使用 TCP 和 UDP 套接字,如果我需要可靠性),并特别考虑可维护性,会更好吗?
我能想到的最直接的替代方案是使用调度算法通过 queue 大小或其他因素来控制要读取和发送的消息数量,但这感觉很差且不灵活在这种情况下。
编辑:添加有关代码的更多信息。
如果立即 returns,Tick() 方法被设置为每秒调用特定次数。例如,如果不存在新的进出消息,则每秒 30 次,如果需要一些时间来发送或接收数据,则每秒 30 次。我已经使用阻塞 ReceiveFrom 和 SendTo 方法来避免繁忙的等待或诸如 Sleep(0) 之类的调用。
虽然我会立即处理传入消息,但我会使用传出消息 queue 来帮助实现频道想法 - 每个频道都有自己的优先级,低至 'no priority',会随着时间的推移影响其带宽份额顺利而忙碌的时刻。
为什么不简单地使用标准读取循环设置?
while (true)
ReadMessage();
无需安排或限制。没有必要知道数据包是否就绪。
您可以在同一个 UDP 套接字上同时读取和写入。
也不需要传出队列。发送即可。
是否使用2个socket分别接收和发送,或者只使用一个,视情况而定。如果您要发送大量消息,即使您在专用线程上,如果套接字使用的传出队列已满,套接字也可能会阻塞。
这个问题有几种解决方法。使用 2 个套接字和 2 个不同的线程是其中之一,将 select 与异步套接字结合使用是另一种方法。关键是您不想因为发送可能会阻塞而停止接收。
这些可能的解决方案中的每一个都有其自身的复杂性。
select api 用于检查某个套接字是否有要接收的内容,但您也可以使用它来检测套接字是否再次可写。您需要一个套接字选项来将套接字置于非阻塞状态,并且您需要在每次发送时检查 E_WOULDBLOCK return 代码。如果是这样,发送失败,您必须自己将消息排队。
你并不是真的同时发送和接收,它是顺序的。您使用 select 通过使用 fd_set api 操作的 2 个位掩码来检查套接字是否可写和可读。您甚至可以同时在多个套接字上使用 select。然后,如果 select,这是一个阻塞调用,returns,您可以检查每个单独的位以检查需要执行哪些操作。
如果套接字未处于非阻塞状态,发送可以阻塞的原因是套接字的输出队列可能已满。如果套接字阻塞,它只会等待队列再次准备就绪。但是在那段等待期间,您无法在同一个套接字上收到任何东西。这就是为什么您需要非阻塞套接字和 select api 以及您自己结合某种排队机制的原因。