5

你们中的许多人都知道原始的“send()”不会将您要求的字节数写入线路。您可以轻松地使用指针和循环来确保您的数据已全部发送。

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

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

4

3 回答 3

9

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

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

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

WSASend由于资源限制或网络错误,重叠可能会失败。出现表明某些数据已发送但不是全部的失败是不常见的。通常它都发送好或根本没有发送。但是,可能会出现错误,这表明某些数据已被使用但不是全部。从这一点开始的方式取决于错误(临时资源限制或硬网络故障)以及WSASend您在该套接字上挂起的其他 s 数量(零或非零)。如果您有临时资源错误并且没有其他未解决的问题,您只能尝试发送其余数据WSASend调用此套接字;而且由于您不知道临时资源限制情况何时会过去,这使情况变得更加复杂......如果您曾经有临时资源限制导致部分发送并且您确实有其他WSASend呼叫未决,那么您可能应该中止连接,因为您可能通过从该调用发送部分缓冲区WSASend,然后发送后续WSASend调用的全部(或部分)而使您的数据流出现乱码。

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

于 2013-01-16T09:18:31.840 回答
3

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

于 2013-01-16T01:54:12.500 回答
0

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

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

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

现在,有了 I/O 完成端口,WSASend情况就有些不同了。当套接字与完成端口相关联时 - 它会自动转换为非阻塞模式。

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

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

于 2013-01-15T22:26:09.243 回答