在完成端口中调用WSASend()?

时间:2013-01-15 21:59:56

标签: c++ sockets networking io winsock

你们中许多人都知道原作"发送()"不会写你要求它的字节数。您可以轻松地使用指针和循环来确保所有数据都已发送。

但是,我不知道WSASend()和完成端口在这种情况下是如何工作的。它会立即返回,您无法控制发送的数量(除了您在例程中有权访问的lpLength)。这是如何解决的?

为了获取所有数据,您是否必须多次在例程中调用WSASend()?这不是一个很大的劣势,特别是如果您希望以特定顺序输出数据并且多个线程访问例程吗?

3 个答案:

答案 0 :(得分:7)

当您使用与WSASendIOCP结构相关联的套接字呼叫OVERLAPPED时,您实际上会将数据传递到网络堆栈以进行发送。一旦您使用的数据缓冲区不再需要网络堆栈,网络堆栈将为您提供“完成”。此时,您可以自由地重用或释放用于数据缓冲区的内存。

请注意,数据不太可能在生成完成时到达对等点,并且生成完成意味着网络堆栈已经取得了缓冲区内容的所有权。

这与send的运作方式不同。当send处于阻塞模式时,对send的调用将阻塞,直到网络堆栈使用了您提供的所有数据。对于在非阻塞模式下对send的调用,网络堆栈从缓冲区中获取尽可能多的数据,然后返回给您,详细说明它使用了多少;这意味着您的一些数据已被使用。通常,WSASend会在收到通知之前使用您的所有数据。

由于资源限制或网络错误,重叠的WSASend可能会失败。获得失败表示某些数据已发送但并非全部发送,这是不寻常的。通常它都发送好或根本没发送。但是,可能会出现错误,表示已使用某些数据,但并非全部。从这一点开始如何进行取决于错误(临时资源限制或硬网络故障)以及在该套接字上有多少其他WSASend未决(零或非零)。如果您有临时资源错误且没有其他未完成的WSASend调用此套接字,则您只能尝试发送其余数据;由于您不知道临时资源限制情况何时会通过,这使事情变得更加复杂...如果您有临时资源限制导致部分发送,并且您还有其他WSASend个呼叫待处理您可能应该中止连接,因为您可能会通过此WSASend电话发送部分缓冲区然后发送后续WSASend来电的全部(或部分)来扰乱您的数据流。

请注意,a)有用且b)有效在套接字上进行多个WSASend调用。这是保持连接充分利用的唯一方法。但是,您应该了解一次有多个重叠WSASend调用挂起的内存和资源使用影响(请参阅here),因为您正在有效地处理缓冲区生命周期的控制(以及因此,由于TCP流控制问题,您的代码使用的内存和资源的数量)。如果你想变得非常聪明,请参阅SIO_IDEAL_SEND_BACKLOG_QUERYSIO_IDEAL_SEND_BACKLOG_CHANGE

答案 1 :(得分:3)

完成端口上的

WSASend()不会通知您,直到套接字接受了所有请求的数据,或者直到发生错误为止,以先发生者为准。它在后台继续工作,直到所有数据都被接受(或错误)。在它通知您之前,该缓冲区必须在内存中保持活动状态,但在WSASend()忙时,您的代码可以继续执行其他操作。数据实际传输到对等方时没有通知。如果您需要,那么您必须在数据协议中实现ACK,以便对等方在收到数据时通知您。

答案 2 :(得分:0)

首先关于send。实际上可能会发生两种不同的事情,具体取决于套接字的配置方式。

如果socket处于所谓的阻塞模式(默认) - 对send的调用将阻止调用线程,直到所有输入缓冲区被底层网络驱动程序使用。 (请注意,这并不意味着数据已经到达对等方。)

如果套接字转移到非阻塞模式 - 如果底层驱动程序可能不立即消耗所有输入,则对send的调用将失败。在这种情况下,GetLastError会返回WSAEWOULDBLOCK。应用程序应该等到它可能重试发送。应用程序应该从系统获取有关套接字状态更改的通知,而不是在循环中调用sendWSAEventSelectWSAAsyncSelect等函数可用于此(以及遗留select)。

现在,通过I / O完成端口和WSASend,故事有些不同。当套接字与完成端口关联时 - 它会自动转移到非阻塞模式。

如果无法立即完成对WSASend的调用(即网络驱动程序无法使用所有输入) - WSASend将返回错误,GetLastError将返回{{ 1}}。这实际上意味着异步操作已启动但尚未完成**。

也就是说,您不应该反复调用STATUS_PENDING,因为发送操作已经在进行中。当它完成时(无论成功与否)你将在I / O完成端口上获得通知,但同时调用线程可以自由地做其他事情。