【问题标题】:how many async socket requests can be going on on the same socket?同一个套接字上可以进行多少个异步套接字请求?
【发布时间】:2011-01-18 07:15:16
【问题描述】:

当我在套接字上调用 BeginSend 时,我传递了一个委托,该委托将在发送数据时(由不同的线程)调用。

如果我在第一次尚未“回调”时再次调用 BeginSend 会发生什么?

发送数据的正确行为是什么?做 BeginSend,然后在回调上做 EndSend 并开始另一个发送?或者让多个 BeginSends 同时工作是否明智?

这是 MSDN 上的 BeginSend 页面,它没有回答这个问题:BeginSend msdn

【问题讨论】:

    标签: .net multithreading sockets asynchronous


    【解决方案1】:

    作为 O.K.W.说,多个待处理的BeginSend 呼叫将正常工作。不过,您可能确实需要记住一些事情。

    首先,如果这是一个 TCP 套接字,那么这仍然是一个点对点的数据流。

    其次,如果您的所有BeginSend 调用都发生在同一个线程上,那么结果将是对等方按调用顺序接收数据。如果您的BeginSend 调用来自不同的线程,那么数据可能以任何顺序到达,因为每次发送之间可能存在竞争条件。这对您来说可能很重要,也可能无关紧要(取决于您是否在每次发送时发送离散、完整的消息)。

    第三,如果您使用 TCP 并且发送的速度比套接字另一端的代码可以接收的速度快,那么您可以填充 TCP 窗口并且 TCP 堆栈将开始对您的数据流执行流量控制。如果您继续发出BeginSend 调用,那么您可能最终会遇到这样一种情况,即您的回调需要越来越长时间才能被调用,因为服务器上的 TCP 堆栈将数据发送到队列中(只有在数据发送后才会收到回调并且基于 TCP 窗口的流量控制将阻止新数据的发送,直到 TCP 窗口不再“满”;即对等方已为 in flight 的某些数据发送了 ACK。

    然后您可能会遇到一种情况,即您正在以一种无法控制的方式耗尽发送机器上的资源(您发出 BeginSend 并且不知道它何时会完成并且每次发送都使用内存来发送缓冲区,并且non-paged pool 可能在 Winsock 代码中... Non-paged pool 是系统范围的资源,在 Vista 之前的操作系统上非常稀缺,如果 non-paged pool 低或用尽,一些表现不佳的驱动程序可能会蓝屏框。您也可能将内存页锁定到内存中,并且对锁定的内存页数还有另一个系统范围的限制。

    由于这些问题,通常最好实现您自己的协议级别流控制,以限制在任何时候(可能使用协议级别 ACK)可能挂起的 BeginSend 调用的数量或使用 TCP窗口流控制并使用待处理发送的完成来发出新的发送,您可以在自己的内存中排队发送数据,并且可以完全控制所使用的资源以及如果排队“太多”数据时您会做什么。有关详细信息,请参阅我的博客文章:http://www.serverframework.com/asynchronousevents/2011/06/tcp-flow-control-and-asynchronous-writes.html

    请参阅此回复:what happens when tcp/udp server is publishing faster than client is consuming?,了解有关 TCP 窗口流控制的更多信息,以及当您忽略它并发出太多重叠发送时,重叠 I/O(在 C++ 领域中)会发生什么...

    总之,发布多个并发BeginSend 调用是优化 TCP 数据流的方法,但您需要确保不要发送“太快”,因为一旦发送,您正在以一种您可以使用的方式消耗资源't control 并且这对于运行您的代码的机器可能是致命的。因此,不要让无限数量的 BeginSend 调用未完成,理想情况下,对框进行分析以确保您不会耗尽系统范围的资源。

    【讨论】:

    • @lens:我在发送的协议 (smpp) 中内置了窗口,因此它们永远不会是发送太快的问题。不过,您提出的乱序点将是一个问题。
    • @Len,在我上面的评论中拼错了你的名字,我很抱歉 :)
    【解决方案2】:

    根据我的read here,似乎有多个并发BeginSend 是可行的。

    摘录:

    1. 您可以同时对多个 BeginSend 进行排队。你不需要 锁
    2. 如果不创建回调方法,怎么知道什么时候发送 成功了吗?你可能想忽略 成功 - 更像是一团火 忘记 - 方法,但你至少 需要知道什么时候可以安全结束 你的程序。
    3. 您可以做的另一件事是使用您从中获得的 IAsyncResult 开始发送。您可以使用 WaitHandle 等待操作完成。 但这违背了整个目的 首先使用异步。
    4. 我的建议是使用回调,并确保 回调被调用和数量 您发送的字节实际上已发送并且 优雅地结束发送操作。

    更新:
    基于codes on MSDN 尝试同时BeginSend 并且数据发送无一例外。但是请记住,必须先打开同一个套接字的连接。同时BeginConnect 将不起作用。

    【讨论】:

    • 那么这会在一个套接字上多路复用多个独立的数据流吗?这是微软对 SCTP 的回应吗?
    • 有趣。但是,它是否比仅使用一个待处理的 BeginSend 每个数据包发送数据包更快?我之所以问这个问题,是因为我正在制作一台性能应该不错的服务器,并且可能应该至少有 X 个待处理的 BeginSends 才能获得不错的吞吐量。这一刻,我不知道
    • 罗伯特——不,一点也不。如果套接字是 TCP 套接字,那么您只有一个流。发出多个 BeginSend 调用只是允许您将多个写入异步排队到流中。
    猜你喜欢
    • 1970-01-01
    • 2018-11-28
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 2010-10-13
    • 2021-01-12
    • 1970-01-01
    相关资源
    最近更新 更多