3

使用 Winsock、C++,我使用send()/recv()和 TCP 连接发送和接收数据。我想确定数据是否已经传递给对方,并且想知道是否建议在(如果)使用recv接收数据之后发回一些确认消息。

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

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

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

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

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

如果您推荐第二种方式,您能否给我一些代码片段,让我可以使用带有超时的recv来接收确认?recv是一个阻塞操作,因此如果之前的发送尝试失败(未通知对方),它将永远挂起。是否有任何简单的方法可以使用带有超时的recv(无需每次都创建单独的线程,这可能是每个发送操作的过度杀伤力)。

此外,我传递给发送函数的数据量可能非常大(几兆字节),那么如何选择“确认消息”的超时时间?也许我应该“拆分”大缓冲区并使用多个发送调用?我觉得会很复杂,请指教!

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

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

编辑 2:好的,我认为我们同意即使 TCP 协议在出现问题时考虑错误处理,Winsock 的send()函数也无法报告错误(仅仅是因为它在实际数据传输开始之前返回网络驱动程序)。所以这里有一个百万美元的问题:Winsock 的send()函数是否至少确保在当前数据包发送之前不会有其他数据包被传递给对方?换句话说,如果由于某些网络故障(但没有通过send()调用报告)发送失败,那么网络故障将在下一次调用send()函数和下一个数据块之前修复,是否可以确保前一个数据包(失败但未报告send() ) 将在下一个数据包之前交付吗?换句话说,一个特定的 send() 函数是否有可能“静默”失败,以便后续的 send() 调用会成功但第一个数据包会丢失?再次 - 我不是在说 TCP 级别,我说的是 Winsock API 级别!

4

4 回答 4

3

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

于 2011-06-11T22:45:42.680 回答
3

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

但是对于某些必须将数据完整性提升到一个新级别的系统来说,这是一个危险的观点:通用标准审计要求FAU_STG.4.1需要能够防止审计日志可能丢失审计条目时发生可审计事件。(例如,Linuxauditd(8)审计日志守护进程可以配置为将计算机置于单用户模式或在没有剩余空间用于审计日志时完全停止系统。)来自远程系统的审计日志可能应该一直保存到它已知它们已成功写入集中式日志服务器。

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

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

值得庆幸的是,大多数应用程序并不那么关键。在“将来”某个时间点报告关闭连接的套接字在这里或那里丢失一兆字节的数据真的并不可怕——我们只是重新尝试我们的 HTTP 请求,或者重新尝试 SFTP 连接。

更新

一个套接字将只接受足够的数据来填充它的可用窗口。窗口大小在会话握手期间在两个对等方之间协商。因此,当套接字的窗口填满时,您的调用send()将开始阻塞。(操作系统也可能继续让您将数据添加到其内部缓冲区,但在某些时候写入会阻塞。)如果对等端中断连接并发送RSTICMP无法访问消息,则未来调用send()将返回连接重置或错误值断管。

更新 2

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

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

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

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

于 2011-06-11T23:26:42.857 回答
1

如果您使用 UDP 并且您关心对方实际接收到的数据,您需要使用 ACK,但如果您不需要 UDP 的速度,您应该使用 TCP,因为它会为您进行 ACKing。

于 2011-06-11T22:47:32.720 回答
0

I think you are over complicating this, trust your TCP/IP software stack and the reliable delivery it offers. TCP sockets operate on streams of data, not packets. Also one call to send does not guarantee one call to recv.

于 2011-06-11T23:06:47.213 回答