使用Winsock的send()/recv()时是否需要确认响应

Is acknowledgment response necessary when using send()/recv() of Winsock?

本文关键字:是否 响应 确认 Winsock send 使用 recv      更新时间:2023-10-16

使用Winsock,C++,我通过send()/recv(),TCP连接发送和接收数据。我想确保数据已发送给另一方,并想知道是否建议在接收到带有recv的数据后(如果)发回一些确认消息。

这里有两种可能性,请建议走哪条路:

  1. 如果send返回传递的缓冲区大小,则假设数据至少已传递到线路另一侧的recv函数。当我说"至少"时,我的意思是,即使recv在那里失败(例如,由于缓冲区不足等),我也不在乎,我只想确保我已经正确完成了服务器部分的工作-我已经完全发送了数据(即,数据到达了另一台机器)。

  2. 使用附加确认:在接收到带有recv的数据后,发回接收到的数据包的某些ID(发送的每个数据的标头的一部分),以表明该数据包的接收操作成功。如果在一段时间后我没有收到这样的"确认消息",请从sender函数返回失败代码。

第二个答案看起来更安全,但如果它是多余的,我不想使传输协议复杂化。另外请注意,我说的是TCP连接(它本身比UDP更安全)。

是否有其他机制(可能是一些其他API?可能是WSARecv()/WSAEnd()工作方式不同?)确保数据被传递到另一端的recv函数?

如果你推荐第二种方式,你能给我一些代码片段吗?它允许我在超时的情况下使用recv来接收确认recv是一个阻塞操作,因此如果上次发送尝试失败(未通知对方),它将永远挂起。有没有任何简单的方法可以在超时的情况下使用recv(而不是每次都创建单独的线程,这可能会对每个send操作造成过度影响)。

此外,我传递给send函数的数据量可能相当大(几兆字节),那么如何选择"确认消息"的超时?也许我应该"拆分"大缓冲区,并使用几个发送调用?我想这会变得很复杂,请给我建议!

编辑:好的,你们建议TCP/IP堆栈来处理它(即不需要手动确认),但这就是我在MSDN页面上发现的:"成功完成发送功能并不意味着数据已成功传递和接收到收件人。此功能只表示数据已成功发送。"因此,即使TCP机制有能力确保数据传输,我也无法通过send()函数或我所知道的任何其他Winsock函数获得状态(成功与否)。你知道从TCP层获得状态的任何方法吗?再次强调,send()数的返回值似乎是不够的!

=============================================

第2版:好吧,我认为我们同意,即使TCP协议在出现问题时考虑了错误处理,Winsock的send()函数也无法报告错误(只是因为它在网络驱动程序开始实际传输数据之前返回)。因此,这里有一个价值百万美元的问题:Winsock的send()函数是否至少确保在当前数据包到达之前不会向另一方发送其他数据包?换言之,如果由于某些网络故障而发送失败(但send()调用未报告),然后网络故障将在下一个数据块的send()数的下一次调用之前修复,是否可以确保上一个数据包(失败但send(()未报告)将在下个数据包之前交付?换句话说,一个特定的send()函数是否有可能"静默"失败,从而使后续的send)调用成功,但第一个数据包将丢失?再说一次-我不是在TCP级别,我是在Winsock API级别

为什么不相信您的TCP/IP堆栈可以保证交付。毕竟,这就是使用TCP而不是UDP的全部意义。

这里现有的答案大多是正确的:如果你使用TCP,你真的不需要担心数据包可靠地传递到你的对等端。

但是,对于某些系统来说,这是一种危险的观点,在这些系统中,数据完整性必须提升到一个新的水平:通用标准审核要求FAU_STG.4.1要求在审核日志可能丢失审核条目的情况下,能够阻止可审核事件。(例如,Linux auditd(8)审核日志守护程序可以配置为将计算机置于单用户模式,或者在没有更多空间用于审核日志时完全停止系统。)远程系统的审核日志可能需要维护,直到知道它们已成功写入集中式日志服务器为止。

金融交易可能最好用比简单TCP更可靠的协议来处理——贷记或借记账户最好用多阶段协议来处理,以确保资金可用,执行交易,然后向发起点报告交易结果。

TCP允许两个对等点之间(在极端条件下)有近千兆字节的飞行中数据;根据您的应用程序的要求,您可能需要在发送端维护该数据,直到您收到同行对数据已得到正确处理的肯定确认。

值得庆幸的是,大多数应用程序并没有那么关键;在"未来"的某个时刻,在报告连接关闭的套接字中到处丢失一兆字节的数据真的并不可怕——我们只是重新尝试HTTP请求,或者重新尝试SFTP连接。

更新

套接字只接受足够的数据来填充其可用的窗口。在会话握手期间,在两个对等方之间协商窗口大小。因此,当套接字的窗口填满时,对send()的调用将开始阻塞。(操作系统可能也会继续允许您将数据添加到其内部缓冲区,但在某个时候写入操作会被阻止。)如果对等方使用RSTICMP Unreachable消息断开连接,则将来对send()调用将返回connection Reset或Broken Pipe的错误值。

更新2

我说的不是TCP级别,而是Winsock API级别的

这可能是混乱的根源。send()在与TCP一起使用时除了遵守TCP行为之外别无选择。

TCP保证按顺序可靠地传递字节流,在一定程度上可以传递数据包。(请参阅@Hans关于一匹小马和粗心的人踢电源线的评论。)对等程序按正确的发送顺序查看字节。(好吧,好吧,TCP有带外紧急数据包传送,但我实际上还没有看到任何使用它的应用程序。使用OOB数据包,你可以获得一些数据。忘了我提到过了。)

如果远程程序接收到在TCP流上发送的字节,那么它也会可靠地接收到之前的所有字节。(好吧,有整整一类重放攻击将远程对等端的合法和伪造数据包拼接在一起,但在具有随机初始序列号的系统上,这些攻击越来越困难。如果这在你的威胁模型中,你应该在TCP之上使用TLS来提供加密的强篡改信息。但TLS无法提供更好的逐数据包传递。)通知。)

如果你使用UDP,并且你关心对方实际接收到的数据,你需要使用ACK,但如果你不需要UDP的速度,你应该使用TCP,就像它为你做ACK一样。

我认为您已经过于复杂了,请相信您的TCP/IP软件堆栈及其提供的可靠交付。TCP套接字操作数据流,而不是数据包。对send的一次调用也不能保证对recv的一次呼叫。