在Java / Android中更快地检测到损坏的套接字

时间:2011-06-11 22:29:24

标签: java android networking tcp

背景

我的应用程序从手机收集数据并将其发送到远程服务器 数据首先存储在内存中(或者当文件足够大时存储在文件中),每隔X秒左右,应用程序就会刷新该数据并将其发送到服务器。

关键是每一条数据都成功发送,我宁愿发送两次数据而不是发送数据。

问题

作为测试,我将应用程序设置为每隔5秒发送一个带有时间戳的数据,这意味着服务器上每5秒钟会出现一个新行。
如果我杀了服务器,我希望线路停止,现在应该将它们写入内存。

当我再次启用服务器时,我应该能够确认没有任何事件丢失。

然而问题是,当我终止服务器时,IO操作开始失败大约需要20秒,这意味着在这20秒内,应用程序会愉快地发送事件并将其从内存中删除,但它们永远不会到达服务器并丢失永远。

我需要一种方法来确保数据实际到达服务器。

这可能是最基本的TCP问题之一,但是我没有找到任何解决方案。

我试过的东西

其他信息

我无法改变服务器的响应方式,这意味着我无法告诉服务器确认数据(不仅仅是TCP的机制),服务器只是默默接受数据而不发回任何内容。

代码片段

课程初始化:

socket = new Socket(host, port);
socket.setTcpNoDelay(true);

发送数据的地方:

while(!dataList.isEmpty()) {
    String data = dataList.removeFirst();
    inMemoryCount -= data.length();
    try {
        OutputStream os = socket.getOutputStream();
        os.write(data.getBytes());
        os.flush();
    }
    catch(IOException e) {
        inMemoryCount += data.length();
        dataList.addFirst(data);
        socket = null;
        return false;
    }
}

return true;

更新1

我再说一遍,我无法改变服务器的行为方式 它通过TCP和UPD接收数据,并且不会发送任何数据以确认接收。这是一个事实,并且在一个完美的世界中,服务器会确认数据,但这根本不会发生。


更新2

Fraggle发布的解决方案非常完美(关闭套接字并等待输入流关闭)。

然而,这带来了一系列新问题 由于我在手机上,我必须假设用户无法发送无限量的字节,我希望尽可能将所有数据流量保持在最低限度。

我并不担心打开一个新套接字的开销,这几个字节不会产生任何影响。然而,我担心的是,每次我连接到服务器时,我都必须发送一个短字符串来标识我是谁。

字符串本身不是那么长(大约30个字符)但如果我经常关闭并打开套接字会增加。

一个解决方案只是每隔X字节“刷新”一次数据,问题是我必须明智地选择X;如果太大,如果套接字发生故障,会发送太多重复数据,如果太小,则开销太大。


最终更新

我的最终解决方案是通过每隔X个字节关闭它来“刷新”套接字,如果一切都不顺利,那么X字节将再次发送。

这可能会在服务器上创建一些重复事件,但可以在那里进行过滤。

6 个答案:

答案 0 :(得分:3)

坏消息:You can't detect a failed connection,只是尝试在该连接上发送或接收数据。

好消息:正如您所说,如果您发送重复数据,则可以。因此,您的解决方案不必担心在不到20秒的时间内检测到故障。相反,只需保留一个包含最后30或60秒数据的循环缓冲区。每次检测到故障然后重新连接时,都可以通过重新发送保存的数据来启动会话。

(如果服务器在不到一分钟的时间内重复上下循环,这可能会出现问题;但如果它正在这样做,那么您还有其他问题要处理。)

答案 1 :(得分:3)

Dan的解决方案是我在阅读你的问题后立即建议的解决方案,他得到了我的投票。

现在我可以建议围绕解决这个问题吗?我不知道你的设置是否可行,但处理设计糟糕的软件(这是你的服务器,对不起)的一种方法是包装它,或者在花式设计 - 模式 - 谈话中提供一个外观,或者简单的谈话在你的痛苦的服务器面前放置代理。设计有意义的基于ack的协议,让代理在内存中保留足够的数据样本,以便能够检测和容忍断开的连接等。简而言之,让手机应用程序连接到驻留在“服务器级”某处的代理机器使用“好”协议,然后让代理使用“坏”协议连接到服务器进程。客户负责生成数据。代理负责处理服务器。

只是另一个想法。

编辑0:

你可能会觉得这个很有趣:The ultimate SO_LINGER page, or: why is my tcp not reliable

答案 2 :(得分:2)

请在此处查看已接受的答案:Java Sockets and Dropped Connections

  1. socket.shutdownOutput();
  2. 等待inputStream.read()返回-1,表示对等体也已关闭其套接字

答案 3 :(得分:0)

无效:服务器无法修改

您的服务器无法确认其收到的每封邮件是否包含其他邮件?客户端不会删除服务器尚未确认的消息。

这会对性能产生影响。为了避免减速,您可以在收到确认之前继续发送消息,并在一条返回消息中确认多条消息。

如果您每隔5秒发送一条消息,并且网络堆栈未检测到断开连接30秒,则您只需存储6条消息。如果未确认6个已发送的消息,则可以认为连接已关闭。 (我想你的应用程序已经实现了重新连接和积压发送的逻辑。)

答案 4 :(得分:0)

如何在远程主机响应每个UDP套接字的同时在单独的UDP套接字上发送UDP数据报,然后当远程主机没有响应时,你会终止TCP连接?它足够快地检测到链路断裂:)

答案 5 :(得分:0)

使用http POST而不是套接字连接,然后您可以向每个帖子发送响应。在客户端,如果响应表明成功,则只从内存中删除数据。

确保它的开销更大,但在100%的时间内都可以提供你想要的东西。