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

如何解决在Java / Android中更快地检测到套接字损坏

| 背景 我的应用程序从电话中收集数据,并将其发送到远程服务器。 数据首先存储在内存中(如果足够大,则存储在文件中),每隔X秒钟左右,应用程序就会刷新该数据并将其发送到服务器。 至关重要的任务是成功发送每个数据,我宁愿将数据发送两次而不是根本不发送。 问题 作为测试,我将应用程序设置为每5秒发送带有时间戳的数据,这意味着每5秒在服务器上出现一条新行。 如果我杀死服务器,我希望这些行停止,那么现在应该将它们写入内存。 当我再次启用服务器时,我应该能够确认没有任何事件丢失。 但是问题是,当我杀死服务器时,IO操作开始需要大约20秒钟才能开始失败,这意味着在这20秒钟内,应用程序很乐意发送事件并将其从内存中删除,但它们从未到达服务器,并且永远丢失了。 我需要一种方法来确保数据实际到达服务器。 这可能是更基本的TCP问题之一,但同样如此,我还没有找到任何解决方案。 我尝试过的东西 设定
Socket.setTcpNoDelay(true)
删除所有缓冲的写入器,仅直接使用ѭ1 每次发送后冲洗流 附加信息 我无法更改服务器的响应方式,这意味着我无法告诉服务器确认数据(不仅仅是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字节。 这可能会在服务器上创建一些重复的事件,但是可以在此过滤。     

解决方法

        请在此处查看已接受的答案:Java套接字和断开的连接 socket.shutdownOutput(); 等待inputStream.read()返回-1,指示对等方也已关闭其套接字     ,        坏消息:您无法检测到失败的连接,除非尝试在该连接上发送或接收数据。 好消息:正如您所说,如果您发送重复数据就可以了。因此,您的解决方案不必担心在不到20秒的时间内检测到故障。相反,只需保留一个循环缓冲区,其中包含最后30或60秒的数据。每次检测到故障然后重新连接时,都可以通过重新发送已保存的数据来启动会话。 (如果服务器在不到一分钟的时间内反复启动和关闭,这可能会成为问题;但是,如果这样做,您还有其他问题要解决。)     ,Dan的解决方案是我在读完您的问题后建议的方案,他得到了我的支持。 现在,我可以建议解决此问题吗?我不知道您的设置是否可行,但是处理设计欠佳的软件(这是您的服务器,对不起)的一种方法是将其包装起来,或者在精美的设计模式中提供外观,或者简而言之,将代理放在您的后台服务器之前。设计有意义的基于ack的协议,使代理在内存中保留足够的数据样本,以便能够检测和容忍断开的连接等。总之,使电话应用程序连接到位于服务器级服务器上的代理。 \“机器使用\” good \“协议,然后使代理使用\” bad \“协议连接到服务器进程。客户负责生成数据。代理负责处理服务器。 只是另一个想法。 编辑0: 您可能会发现这很有趣:最终的SO_LINGER页面,或者:为什么我的tcp不可靠。     ,        无法使用:无法修改服务器 您的服务器无法确认它收到的与另一个数据包一起的每条消息吗?客户端不会删除服务器尚未确认的消息。 这将影响性能。为避免速度变慢,您可以在收到确认之前继续发送消息,并在一条返回消息中确认几条消息。 如果您每5秒钟发送一条消息,并且30秒钟内网络堆栈都未检测到断开连接,则您只需存储6条消息。如果未确认发送的6条消息,则可以认为连接已断开。 (我想您的应用程序中已经实现了重新连接和积压发送的逻辑。)     ,        如何在使远程主机响应每个UDP数据报的同时在单独的UDP套接字上发送UDP数据报,然后在远程主机不响应时杀死TCP连接呢?它足够快地检测到链接断开:)     ,        使用http POST而不是套接字连接,然后可以向每个帖子发送响应。在客户端,仅当响应指示成功时,才从内存中删除数据。 确保更多的开销,但100%的时间给您您想要的东西。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-