在完成端口调用WSASend()

Calling WSASend() in completion port?

本文关键字:WSASend 调用      更新时间:2023-10-16

很多人都知道原来的"send()"不会向网络写入您要求的字节数。你可以很容易地使用指针和循环来确保你的数据都被发送了。

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

您是否必须在例程中多次调用WSASend()才能获得所有数据?这似乎不是一个很大的缺点,特别是如果您希望以特定顺序输出数据并且多个线程访问例程时?

当您使用与IOCPOVERLAPPED结构相关联的套接字调用WSASend时,您有效地将数据传递给网络堆栈以发送。一旦网络堆栈不再需要您使用的数据缓冲区,网络堆栈将给您一个"完成"。此时,您可以自由地重用或释放用于数据缓冲区的内存。

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

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

重叠的WSASend可能由于资源限制或网络错误而失败。得到一个失败是不寻常的,这表明一些数据已经发送,但不是全部。通常是全部发送OK,或者根本没有发送。然而,它是有可能得到一个错误的完成,这表明一些数据已被使用,但不是全部。如何从这一点开始取决于错误(临时资源限制或硬网络故障)以及该套接字上挂起了多少其他WSASend(零或非零)。你只能尝试发送剩余的数据,如果你有一个临时的资源错误,没有其他未完成的WSASend调用这个套接字;由于您不知道临时资源限制情况何时会过去,这使得情况变得更加复杂。如果你曾经有一个临时资源限制引起的部分发送,你确实有其他WSASend调用挂起,那么你应该中止连接,因为你可能已经混淆了你的数据流,从这个WSASend调用发送缓冲区的一部分,然后全部(或部分)随后的WSASend调用。

请注意,在套接字上有多个未完成的WSASend调用是a)有用的,b)高效的。这是保持连接充分利用的唯一方法。但是,您应该意识到同时有多个重叠的WSASend调用挂起的内存和资源使用含义(见这里),因为由于TCP流控制问题,您正在有效地将缓冲区的生命周期(以及代码使用的内存和资源的数量)的控制权交给对等端)。看看SIO_IDEAL_SEND_BACKLOG_QUERYSIO_IDEAL_SEND_BACKLOG_CHANGE,如果你真的想变得聪明…

完成端口上的

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

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

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

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

现在,对于I/O完成端口和WSASend,情况有些不同。当套接字与完成端口相关联时,它会自动转移到非阻塞模式。

如果对WSASend的调用不能立即完成(即网络驱动程序不能消耗所有输入)- WSASend返回错误,GetLastError返回STATUS_PENDING。这实际上意味着异步操作已经开始但尚未完成**.

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