1

我正在开发用 C 语言为 Linux 编写的客户端服务器应用程序,我正在使用 TCP 将数据复制到多个从属副本,我想知道如何处理某些副本的意外临时关闭(可能是 unix 进程的崩溃)或硬件电源关闭)。

当我向内核发出 write() 系统调用时,成功返回意味着数据已复制到套接字,但并不意味着接收端得到了数据。如果目标断电然后再上电,则必须从丢失数据的点将数据重新发送(在建立新的 TCP 连接之后)到副本。

假设我正在处理大量数据并且我不保留我已经发送的数据(即 write() 系统调用返回成功)。我只保留待发送的数据。

当副本从意外关闭中恢复并再次连接时,我如何从内核获取已写入套接字但在目标主机上尚未“确认”的数据?

或者换句话说,我如何从 TCP 连接丢失中恢复并从停止点重新建立客户端和服务器之间的传输?

4

3 回答 3

2

您需要在 TCP 之上添加另一层抽象。在发送每条数据之后(TCP 保证它会完好无损地到达那里),让另一端的进程在您自己的更高级别的协议中发送它自己的 ACK(不管是什么——不管它是什么) ACK\0"、"GOT\n" 或其他任何东西)。在另一端(发起者),读取此数据。如果它通过良好而没有错误,一切都很好。如果您收到错误 - 检查类型。如果你得到 ECONNRESET,那意味着远端已经死了。由此,您可以做出相应的回应。等到可以重新连接,然后重新发送数据。

于 2013-03-21T14:29:23.713 回答
2

没有办法通过标准 API 做你想做的事。

一个解决方案可能是让您的客户端定期将接收到并验证写入磁盘的运行总字节数发送回,然后在服务器上保留一个已发送但未确认数据的缓冲区。然后当客户端重新连接时,它会发送最后一个好的计数,服务器知道从哪里开始重新传输。

于 2013-03-21T14:39:59.770 回答
1

TCP 将处理 TCP 所需的序列号,您不能在应用程序级别大量使用这些序列号

您需要在应用程序级别进行一些序列控制。

在您的情况下,您可以为您发送的每个数据块分配一个数字。目的地需要持续跟踪它收到的最后一个块号。从意外关闭启动时,目标需要返回它处理的最后一个块号,然后您从那里开始发送。

我如何从内核获取已写入套接字的数据,但在目标主机上还没有“确认”?

即使你可以,这也不够。目标主机很可能已经确认了数据,但无论出于何种原因,确认都可能丢失或从未发送,但目标应用程序可能已经接收并处理了该数据。所以如果你在这种情况下使用 TCP 序列号,你最终会得到重复的数据。

另一种情况是 TCP 发回数据的 ack,而目标应用程序在读取该数据时崩溃/关闭,但就在它写入磁盘之前。因此,您最终会丢失数据。

于 2013-03-21T14:47:50.153 回答