2017-07-16 87 views
2

我有一个使用SocketAsyncEventArgs异步接收UDP数据包的标准实现。我不明白从文档和一些谷歌搜索是,如果我应该在回调本身内处理消息的真实工作,如我指的完整实现中的this comment indicates,或者我应该将处理卸载到其他线程,例如通过ConcurrentQueue或BlockingCollection。在哪些线程上调用SocketAsyncEventArgs完成并处理消息

我关注的是以下几点:

  • 如果直接在回调处理,可以把它降低接收性能或引入由于临时线程池饥饿或其他一些实现细节随机延迟?
  • 由于处理过程中的轻微延迟(而不是仅仅卸载到一个集合)可能会由于缓冲区而丢弃更多的包?
  • 是否可以按照与包实际上从网络到达不同的顺序调用回调的意义来重新排序更多包。

那么,什么是最好的做法或加工使用的SocketAsyncEventArgs保证最低错过的数据报,回调调用没有额外的重新排序,没有额外的延迟消息的预期呢?

和一个相关的问题 - 是否ReceiveAsync保证任何命令或至少尝试调用回调以相同的顺序,从网络接收包,或者我应该使用阻止接收的呢?目标用例是订阅6-8个UDP通道,其中每个通道的顺序非常重要。运行一些阻塞线程看起来比处理回调要复杂得多,但如果只有这样的解决方案可以保证消息顺序,那就不那么难。

回答

3

什么是最好的做法,或使用的SocketAsyncEventArgs保证最低错过的数据报

坦白处理消息的预期的方式,这是真正的意见在一定程度上的问题,以及非常依赖于你的确切场景。作为一般规则,你的I/O完成例程应该很快。如果你的处理速度很快,那么在例程中就可以这么做了。如果没有,你应该尽可能少地做些工作,即把数据移动到一个队列中,然后在其他地方处理,然后从完成例程返回。

请记住,这里的“快速”是相对的。你只需要比网络更快,尽管网络速度不断提高,但在现代CPU上却不是很难做到。网络层将代表您进行缓冲,因此假设您的工作负载吞吐量大于网络上的吞吐量,那么在完成例程中完成这项工作可能很不错。

但真的,它只是取决于。通常没有办法说哪个更好。每种特定情况都不相同。

ReceiveAsync是否保证任何订单或至少尝试以与从网络接收包相同的顺序调用回调,或者我应该使用阻止接收的呢?

阻挡接收将无济于事。

异步方法都具有相同的特征:您可以一次发布多个异步方法,并且它们将按照它们的发布顺序完成。但是您仍然需要跟踪您发布读取操作的顺序。缓冲区将按照您给予网络层的顺序填充,但完成例程可能不按顺序执行,因为它们在线程上执行,并且线程调度程序不保证线程执行的顺序。只是因为一个线程在另一个线程之前可以运行,这并不意味着它会在另一个线程之前实际得到它的下一个时间片。

但它实际上更糟糕:

目标的使用情况是订阅6-8 UDP通道,在每个人的顺序是非常重要的。

如果订单在您的业务情景中很重要,那么您需要在数据报中包含序列号,并确保在收到这些数据报时使用这些序列号以正确的顺序排列数据。

UDP不保证排序。数据报可以以任何顺序接收,与发送顺序无关。 UDP也不保证传送。数据报可能随时丢失。 UDP也不保证唯一性。给定的数据报可以被多次传送。

如果可靠性和排序在您的方案中很重要,那么您可能应该使用TCP,而不是UDP。

如果你唯一关心的是订购,那么UDP可能仍然适用于你。在这种情况下,由于无论如何您都需要在数据报中使用序列号,所以它确实使“多个并发读取操作”场景更简单,因为数据本身带有序列号,因此您无需分别跟踪(例如在与每个读取操作相关联的状态对象中)。

+0

谢谢!序列号已由外部数据源(交换)提供,并且无论如何将存在恢复顺序的逻辑,并且TCP不是一个选项。所以你说在任何意义上,循环中的阻塞读取都不比ReceiveAsync更可靠? –

+0

_“在任何意义上来说,循环中的阻塞读取都不如ReceiveAsync可靠吗?” - “在任何意义上”都非常广泛。循环中的阻塞读取肯定可以更容易地进行正确编码,所以至少肯定它在这个“意义上”是“更可靠的”。此外,即使使用异步读取,您也可以选择一次只有一个未完成的读取操作,这在语义上与具有阻止读取的循环相同; ... –

+0

...唯一的区别是更好的可伸缩性和缺乏线程阻塞,并且实现在某种意义上是简单的(和TCP相比,多个并发读取操作在实践中更简单)。但是,是的......从某种意义上说,对于使用UDP的情况,您必须处理乱序数据,从而引入读取完成例程相对于读取操作本身实际完成的顺序执行的可能性,比一般用_any_其他读取实现来处理UDP。 –