Winsock2 同步 IO...在 WSAGetOverlappedResult 中使用 fWait==TRUE 等待 WSASend “完成”

Winsock2 synchronous IO...waiting for WSASend to "complete" using fWait==TRUE in WSAGetOverlappedResult

提问人:Sean Lynes 提问时间:9/21/2022 更新时间:9/23/2022 访问量:124

问:

我和一位同事对 WSASend 重叠 IO 请求的“完成”存在分歧。他断言,在 WSAGetOverlappedResult 调用中使用 fWait 作为 TRUE 只会等到消息排队等待发送。他认为等待写入/发送操作“完成”仅意味着消息已成功启动。在我看来,这远非向插座另一端发送的“完成”消息......那只是发送的开始,而不是完成。如果 TRUE 的 fWait 在字节被发送并被 ACK(或返回错误)之前没有阻塞,那么这远非同步......事实上,它的作用与异步 IO 相同,因为它只是一劳永逸。

几十年来,我一直在维护我们公司的通信库,了解如何做以及什么是“同步”IO,所以如果我的理解确实错了,我会感到震惊。但我的同事是一位才华横溢的开发人员,拥有大量的 TCP/IP 经验,并且坚持认为他是对的。他说他甚至在stackoverflow上提出了这个问题,并被告知他是对的。我无法想象我怎么会误解发送的“完成”意味着除了发送请求的字节确实已发送和接受之外的任何事情。但是我之前错了,哈哈

所以。。。谁是对的?等待 WSASend 请求“完成”到底是什么意思?只需等待消息排队等待在 TCP/IP 堆栈中发送...或者等待构成消息的所有数据包被发送和接收???还是真相介于两者之间?

套接字 TCP IO winsock2

评论


答:

4赞 David Schwartz 9/21/2022 #1

你错了。

当不再处理发送请求时,发送请求即告完成。堆栈接管操作后,将不再处理请求,其资源可用于其他目的。

如果你是对的,重叠的 I/O 几乎毫无用处。谁愿意等到另一方完整地确认了之前的发送,然后才能将更多的数据排队到 TCP 连接?

如果无法确定排队过程何时完成,重叠的 I/O 有什么用处?这就是应用程序需要知道的内容,以便它可以发送另一个数据块。

您总是有死区时间,因为发送队列总是必须完全清空,然后才能在发送端排队更多数据。呸!

无论如何,知道何时收到 ACK 是没有用的。这并不意味着另一端的应用程序获得了数据。所以它在应用层没有意义。知道发送已经排队在应用层确实有意义 - 这意味着您现在可以将更多要发送的数据排队,并保证上一次发送的所有数据都将在下一次发送的任何数据之前传递到接收方的应用层。这就是应用程序需要知道的。

当发送排队时,对 WASSend 的同步调用也会完成。异步操作只是意味着您不必等待同步操作中等待的任何内容。所以这是你的理解会很奇怪的另一个原因。

最后,没有标准或通用的方法来等待具有同步操作的远程 ACK。因此,您必须认为异步操作默认提供语义,而同步操作甚至无法真正使用同步操作。这也很奇怪。

评论

1赞 Sean Lynes 9/21/2022
如果同步 wsasend 只等待对消息进行排队,那么除了异步 wsasend 的作用之外,还有什么其他功能呢?异步发送不对字节进行排队?那将是新奇的......如果你是对的,那么同步 IO 既不是同步的,也不是等待发送请求来“完成”任何事情的。
1赞 Sean Lynes 9/21/2022
如果你是对的,那么同步 io 既不是同步的,也不是等待发送请求来“完成”任何事情的,除了希望消息有一天到达某个地方。在我们与客户的沟通中,丢失的信息是巨大的。我需要确切地知道哪条消息失败了。如果任何发送错误直到任意时间后才知道或报告,则这是一个问题。
0赞 Sean Lynes 9/21/2022
“保证所有数据......将通过“......以太网通信并不完美,消息有时会在我们的客户网络上丢失,因此除非低级协议处理握手以确认接收发送的字节,否则这个词保证是一个糟糕的词......WEPIO 请求已完成。如果发送消息时出错,我的应用会记录此事实或尝试重新发送丢失的消息,或将该消息与积压的其他消息合并。如果从未通知丢失消息,我无法知道通信是否正常工作,除非需要回复的消息,对吗?
1赞 Remy Lebeau 9/22/2022
@SeanLynes当操作系统同意从你手中拿走数据时,发送操作是“完成”的,这样你就可以回收用于保存数据的缓冲区。数据将在操作系统选择的时间在后台传输。不能保证数据将到达目的地。只是操作系统承诺尝试交付它。但它释放了你,让你在此期间继续做其他事情。
1赞 Remy Lebeau 9/22/2022
@SeanLynes 当操作系统已向缓冲区提供了一定数量的数据(可能比请求的数据小,因此可能是部分数据)并准备好处理该缓冲区时,读取操作即为“完成”。
1赞 khorton 9/21/2022 #2

大卫是对的。

当数据被移交给TCPIP堆栈(缓冲在其层上)时,WSASend将完成,以便在传输允许时发送。如果是阻塞呼叫,它将等到准备好挂起;就像它是异步的一样,OVERLAPPED I/O 将在挂起后完成。

有人可能会问,为什么这甚至是必要的?此行为对于通过 TCP 连接保持尽可能多的数据传输至关重要。为了在传输中保留尽可能多的数据,发送方应该调用 WSASend 直到它挂起(回想一下,如果它是同步 WSASend 调用,那么该线程将一直阻塞,直到 WSASend 可以完成;如果它是异步的,则一旦 WSASend 调用可以挂起,就会发生异步完成)。

为什么WSASend会挂起,为什么不立即完成?WSASend 只有在传输(在内核中)准备好缓冲更多数据时才会完成。因此,循环直到 WSASend 挂起将使该连接保持足够的数据,以保持最大数据在传输中。

希望这是有道理的。

评论

1赞 khorton 9/21/2022
此外,如上所述,为了“知道”调用方是否接收了数据,应用程序的协议应该有自己的握手,以传达 TCP 或 UDP 连接的另一端正确接收了哪些信息。
0赞 khorton 9/21/2022
Sean:如果由于 TCP 连接以某种方式失败而发送失败,则下一个 recv 或发送请求将立即失败(因为该 TCP 连接现在已断开)。因此,强烈建议始终在 TCP 连接上挂起 recv。
0赞 Sean Lynes 9/21/2022
因此,在本文中,时序图...IO 开始到 IO 结束意味着没有 IO 发生?IO应支持输入和输出...不排队...learn.microsoft.com/en-us/windows/win32/fileio/......
0赞 Sean Lynes 9/21/2022
还行。。因此,如果我使用我的 SendMessage 函数同步 io 发送 10 字节与 60k 字节的时间,我应该几乎没有时差,对吧?对缓冲区进行排队几乎不需要任何时间,但应该需要额外的一两毫秒才能将消息分解为数据包,发送到其他计算机,然后接收确认。如果我的通话花费了 Wireshark 显示正在处理的消息所花费的额外时间,那么我是对的,但如果不是。我一直误解了这一点,所有关于同步io的术语和描述都没有意义。我明天会让你们知道我发现了什么......
0赞 Sean Lynes 9/22/2022
My testing seems to show I am indeed wrong. Synchronous behavior does not exist for sending...just receiving. I see how this is a performance help, but I object to all documentation saying sending fWait of TRUE in WSAGetOverlappedResult will wait until the IO request is "complete". This word implies much more than just it being queued up. Well..I'm flabbergasted that my understanding was off...but TCP handles things well enough that it hasn't caused me issues before.
1赞 Sean Lynes 9/22/2022 #3

My testing seems to show I am indeed wrong. Synchronous behavior does not exist for sending...just receiving. I see how this is a performance help, but I object to all documentation saying sending fWait of TRUE in WSAGetOverlappedResult will wait until the IO request is "complete". This word implies much more than just it being queued up. Well..I'm flabbergasted that my understanding was off...but TCP handles things well enough that it hasn't caused me issues before.

Thanks for the excellent replies and patience with my frustrated replies. I'm incredibly disappointed at how all the documentation is written using words that would imply my understanding was right...when all along waiting for IO to "end", "complete", "finish" absolutely does NOT wait.