当我在套接字上调用BeginSend时,我会传递一个委托,当发送数据时,该代理将被调用(由另一个线程调用)。
如果我第一次调用BeginSend而第一次还没有'回调',会发生什么?
发送数据的正确行为是什么?做BeginSend,并在回调时做EndSend并开始另一个发送?或者让多个BeginSends同时工作真的很明智吗?
这是MSDN上的BeginSend页面,它没有给出这个问题的答案:BeginSend msdn
答案 0 :(得分:17)
作为O.K.W.说,多个待处理的BeginSend
调用将正常工作。你可能需要记住一些事情。
首先,如果这是一个TCP套接字,那么这仍然是来自点对点的单个数据流。
其次,如果所有BeginSend
调用都发生在同一个线程上,那么结果将是对等体按调用顺序接收数据。如果您的BeginSend
次呼叫来自不同的线程,则数据可以按任何顺序到达,因为每次发送之间可能存在竞争条件。这对您来说可能或不重要(取决于您是否在每次发送时发送离散的完整消息)。
第三,如果您使用TCP并且发送速度超过套接字另一端的代码可以接收,那么您可以填充TCP窗口,TCP堆栈将开始对您的数据流执行流控制。如果你继续发出BeginSend
个调用,那么你最终可能会因为服务器上的TCP堆栈将要发送的数据排队等待你的回调需要更长时间和更长时间而被调用(只有数据有回调才能获得回调)已发送并且基于TCP窗口的流量控制将阻止发送新数据,直到TCP窗口不再“满”;即对等方已为某些数据in flight
发送了ACK。
然后,您可能会以无法控制的方式处理发送计算机上的资源(您发出BeginSend
并且不知道何时完成,并且每个发送都使用内存用于缓冲区在Winsock代码中发送并且潜在non-paged pool
... Non-paged pool
是一个系统范围的资源,并且在Vista之前的操作系统上非常稀缺,如果{{1} non-paged pool
,一些行为不端的驱动程序可以蓝屏你还可能将内存页锁定到内存中,并且锁定内存页的数量还有另一个系统限制。
由于存在这些问题,通常最好实现自己的协议级别流量控制,该控制可以限制任何时候可以挂起的BeginSend
个呼叫的数量(可能使用协议级别的ACK)或使用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数据流的方法,但是您需要确保不要像以前那样以过于消耗资源的方式发送“太快”您无法控制,并且对于运行代码的计算机而言,这可能是致命的。因此,不要允许无限数量的BeginSend
次呼叫,并且理想情况下,应对此框进行分析,以确保您不会耗尽系统范围的资源。
答案 1 :(得分:4)
根据我的read here,似乎有多个并发BeginSend
是可行的。
摘录:
- 您可以同时排队多个BeginSends。你不需要 锁定
- 如果您不创建回调方法,您如何知道何时发送 成功了吗?你可能想忽略 成功 - 更像是火和 忘了 - 方法,但至少你 需要知道何时安全结束 你的计划。
- 您可以做的另一件事是使用您获得的IAsyncResult BeginSend。您可以使用WaitHandle 等待操作完成。 但这违背了整个目的 首先使用Async。
- 我的建议是使用回调,并确保 调用回调和数量 你发送的字节实际上是发送的 恩惠结束发送操作。
醇>
更新:
基于codes on MSDN同时尝试BeginSend
,数据无异常发送。但请记住,必须事先打开相同套接字的连接。同时BeginConnect
将无效。