我的应用程序从手机收集数据并将其发送到远程服务器 数据首先存储在内存中(或者当文件足够大时存储在文件中),每隔X秒左右,应用程序就会刷新该数据并将其发送到服务器。
关键是每一条数据都成功发送,我宁愿发送两次数据而不是发送数据。
作为测试,我将应用程序设置为每隔5秒发送一个带有时间戳的数据,这意味着服务器上每5秒钟会出现一个新行。
如果我杀了服务器,我希望线路停止,现在应该将它们写入内存。
当我再次启用服务器时,我应该能够确认没有任何事件丢失。
然而问题是,当我终止服务器时,IO操作开始失败大约需要20秒,这意味着在这20秒内,应用程序会愉快地发送事件并将其从内存中删除,但它们永远不会到达服务器并丢失永远。
我需要一种方法来确保数据实际到达服务器。
这可能是最基本的TCP问题之一,但是我没有找到任何解决方案。
Socket.setTcpNoDelay(true)
OutputStream
我无法改变服务器的响应方式,这意味着我无法告诉服务器确认数据(不仅仅是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;
我再说一遍,我无法改变服务器的行为方式 它通过TCP和UPD接收数据,并且不会发送任何数据以确认接收。这是一个事实,并且在一个完美的世界中,服务器会确认数据,但这根本不会发生。
Fraggle发布的解决方案非常完美(关闭套接字并等待输入流关闭)。
然而,这带来了一系列新问题 由于我在手机上,我必须假设用户无法发送无限量的字节,我希望尽可能将所有数据流量保持在最低限度。
我并不担心打开一个新套接字的开销,这几个字节不会产生任何影响。然而,我担心的是,每次我连接到服务器时,我都必须发送一个短字符串来标识我是谁。
字符串本身不是那么长(大约30个字符)但如果我经常关闭并打开套接字会增加。
一个解决方案只是每隔X字节“刷新”一次数据,问题是我必须明智地选择X;如果太大,如果套接字发生故障,会发送太多重复数据,如果太小,则开销太大。
我的最终解决方案是通过每隔X个字节关闭它来“刷新”套接字,如果一切都不顺利,那么X字节将再次发送。
这可能会在服务器上创建一些重复事件,但可以在那里进行过滤。
答案 0 :(得分:3)
坏消息:You can't detect a failed connection,只是尝试在该连接上发送或接收数据。
好消息:正如您所说,如果您发送重复数据,则可以。因此,您的解决方案不必担心在不到20秒的时间内检测到故障。相反,只需保留一个包含最后30或60秒数据的循环缓冲区。每次检测到故障然后重新连接时,都可以通过重新发送保存的数据来启动会话。
(如果服务器在不到一分钟的时间内重复上下循环,这可能会出现问题;但如果它正在这样做,那么您还有其他问题要处理。)
答案 1 :(得分:3)
Dan的解决方案是我在阅读你的问题后立即建议的解决方案,他得到了我的投票。
现在我可以建议围绕解决这个问题吗?我不知道你的设置是否可行,但处理设计糟糕的软件(这是你的服务器,对不起)的一种方法是包装它,或者在花式设计 - 模式 - 谈话中提供一个外观,或者简单的谈话在你的痛苦的服务器面前放置代理。设计有意义的基于ack的协议,让代理在内存中保留足够的数据样本,以便能够检测和容忍断开的连接等。简而言之,让手机应用程序连接到驻留在“服务器级”某处的代理机器使用“好”协议,然后让代理使用“坏”协议连接到服务器进程。客户负责生成数据。代理负责处理服务器。
只是另一个想法。
你可能会觉得这个很有趣:The ultimate SO_LINGER page, or: why is my tcp not reliable。
答案 2 :(得分:2)
请在此处查看已接受的答案:Java Sockets and Dropped Connections
答案 3 :(得分:0)
无效:服务器无法修改
您的服务器无法确认其收到的每封邮件是否包含其他邮件?客户端不会删除服务器尚未确认的消息。
这会对性能产生影响。为了避免减速,您可以在收到确认之前继续发送消息,并在一条返回消息中确认多条消息。
如果您每隔5秒发送一条消息,并且网络堆栈未检测到断开连接30秒,则您只需存储6条消息。如果未确认6个已发送的消息,则可以认为连接已关闭。 (我想你的应用程序已经实现了重新连接和积压发送的逻辑。)
答案 4 :(得分:0)
如何在远程主机响应每个UDP套接字的同时在单独的UDP套接字上发送UDP数据报,然后当远程主机没有响应时,你会终止TCP连接?它足够快地检测到链路断裂:)
答案 5 :(得分:0)
使用http POST而不是套接字连接,然后您可以向每个帖子发送响应。在客户端,如果响应表明成功,则只从内存中删除数据。
确保它的开销更大,但在100%的时间内都可以提供你想要的东西。