当我在套接字上调用BeginSend时,我传递了一个委托(delegate),该委托(delegate)将在发送数据后(由另一个线程)调用。
如果在第一次尚未“回调”的情况下再次调用BeginSend,会发生什么情况?
发送数据的正确行为是什么?做BeginSend,然后在回调上做EndSend并开始另一个发送?或者同时运行多个BeginSends实际上是明智的吗?
这是MSDN上的BeginSend页面,它没有提供此问题的答案:BeginSend msdn
最佳答案
身为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
太低或耗尽,某些行为不佳的驱动程序会蓝屏显示该框,此外,您还可能将内存页面锁定在内存中,并且锁定的内存页数还有另一个系统范围的限制。
由于这些问题,通常最好实现自己的协议(protocol)级别的流控制,以限制在任何时候(可能使用协议(protocol)级别的ACK)可能挂起的BeginSend
调用的数量,或者与TCP Window流控制一起使用完成挂起的发送以发出新的发送,您可以将数据排队发送到自己的内存中,并且可以完全控制所使用的资源以及如果将“太多”数据排队的话该怎么做。有关更多详细信息,请参见此处的我的博客文章: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
调用,并且理想情况下,请对配置文件进行概要分析,以确保您不会耗尽系统范围的资源。