c# - 在哪些线程上调用 SocketAsyncEventArgs Completed 以及在何处处理消息

标签 c# multithreading sockets asynchronous

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

我的担忧如下:

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

那么,使用 SocketAsyncEventArgs 处理消息的最佳实践或预期方式是什么,以确保最少丢失的数据报,不会对回调调用进行额外的重新排序,也不会产生额外的延迟?

还有一个相关的问题 - ReceiveAsync 是否完全保证任何顺序,或者至少尝试按照从网络接收包的相同顺序调用回调,或者我应该为此使用阻塞接收?目标用例是订阅 6-8 个 UDP channel ,在每个 channel 中,顺序非常重要。运行多个阻塞线程看起来比仅处理回调更复杂,但如果只有这样的解决方案可以保证消息顺序,则并不难。

最佳答案

what is the best practice or intended way of processing messages using SocketAsyncEventArgs to ensure minimum missed datagrams

坦率地说,这在某种程度上确实是一个见仁见智的问题,并且在很大程度上取决于您的具体情况。不过作为一般规则,您的 I/O 完成例程应该很快。如果你的处理速度很快,那么在例程中这样做就可以了。如果没有,您应该做尽可能少的工作,即将数据移动到可以在其他地方处理的队列中,然后从完成例程返回。

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

但实际上,这取决于情况。没有办法笼统地说哪个更好。每个具体场景都不同。

does ReceiveAsync guarantees any order at all or at least tries to call the callback in the same order that packages are received from a network, or I should use blocking receives for that?

阻止接收无济于事。

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

但实际上比这更糟:

The target use case is to subscribe to 6-8 UDP channels, within each of them the order is highly important.

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

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

如果可靠性和顺序在您的场景中很重要,您可能应该使用 TCP,而不是 UDP。

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

关于c# - 在哪些线程上调用 SocketAsyncEventArgs Completed 以及在何处处理消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45133069/

相关文章:

c# - 当开关不处理一个或多个枚举值时,我可能会出现 C# 编译时错误吗?

c - 套接字编程 C/C++ - recv 函数挂起?

c# - RavenDb 搜索多个字符串属性

c# - 两次调用 ReadAsFormDataAsync

java - 等待另一个线程中的邮件发送完成

c++ - 与阻塞调用同步

c++ - 即发即弃 std::thread 对象自行清理

c - 在Linux内核中设置socket输出端口号

sockets - 为什么UDP套接字是通过目的IP地址和目的端口来识别的?

c# - 日期选择器 ASP.NET c# mvc4