Android socket与服务器通信及心跳连接的实现

安卓客户端与Python服务器的socket异常断开 环境为安卓手机用socket连接linux里的python服务器。这种跨语言的东西,客户端和服务器端建立链接之后,其中一方发生断网、物理死机等等原因导致socket异常断开,是只有自己知道的,对面的人是不知道你已经断掉,因为你这边根本没有发出请求要求断开。调试bug的情况是:1.python服务器物理断开(断网,没电等),但安卓客户端认为socket仍在链接,且会不断收到空字节包,不做处理会导致卡... 阅读详情

在项目中,有如下需求:Android客户端向服务器发送数据,收到服务器返回的数据发送成功标识后,客户端即与服务器建立数据一来一往的心跳连接,若服务器端断开时,客户端接收到通知,关闭Service停止发送数据;代码如下:

public class BackService extends Service {
    private static final String TAG = "BackService";
    /** 心跳检测时间  */
    private static final long HEART_BEAT_RATE = 3* 1000;
    /** 主机IP地址  */
    private static String HOST = "192.168.1.30";
    /** 端口号  */
    public static final int PORT =10801;
    /** 消息广播  */
    public static final String MESSAGE_ACTION = "org.feng.message_ACTION";
    /** 心跳广播  */
    public static final String HEART_BEAT_ACTION = "org.feng.heart_beat_ACTION";

    private long sendTime = 0L;

    private Socket socket;

    private ReadThread mReadThread;

    private InputStream is;//输入流
    private int count;//读取的字节长度

    private IBackService.Stub iBackService = new IBackService.Stub() {
        @Override
        public boolean sendMessage(String message) throws RemoteException {
            return sendMsg(message);
        }
    };

    @Override
    public IBinder onBind(Intent arg0) {
        return (IBinder) iBackService;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        HOST= (String) Utils.getShare(this, ConfigUrl.SERVER_IP,"ip");
        Log.i(TAG,"ip-->"+HOST);
        Log.i(TAG,"port-->"+PORT);
        new InitSocketThread().start();
    }

    // 发送心跳包
    private Handler mHandler = new Handler();
    private Runnable heartBeatRunnable = new Runnable() {
        @Override
        public void run() {
            ReadThread thread=new ReadThread(socket);
            thread.start();
        }
    };

    public boolean sendMsg(String msg) {
        if (null == socket) {
            return false;
        }
        try {
            if (!socket.isClosed() && !socket.isOutputShutdown()) {
                OutputStream os = socket.getOutputStream();
                os.write(msg.getBytes());
                os.flush();
                sendTime = System.currentTimeMillis();// 每次发送成功数据,就改一下最后成功发送的时间,节省心跳间隔时间
                Log.i(TAG, "发送成功的时间:" + sendTime+"  内容-->"+msg);
            } else {
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            Intent intent = new Intent(HEART_BEAT_ACTION);
            intent.putExtra("message", "exit");
            sendBroadcast(intent);
            Log.i(TAG,"send-->"+e.getMessage());
            return false;
        }
        return true;
    }

    // 初始化socket
    private void initSocket() throws UnknownHostException, IOException {
        socket = new Socket(HOST, PORT);
        socket.setSoTimeout(13000);//?
        if (socket.isConnected()){//连接成功
            if (sendMsg("mdc_"+Utils.getShare(this,ConfigUrl.DEVICE_ID,"手机设备id"))){//发送成功
                //接收服务器返回的信息
                mReadThread = new ReadThread(socket);
                mReadThread.start();
            }
        }
    }

    // 释放socket
    private void releaseLastSocket(Socket mSocket) {
        try {
            if (null != mSocket) {
                if (!mSocket.isClosed()) {
                    is.close();
                    mSocket.close();
                }
                mSocket = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    class InitSocketThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                initSocket();
            } catch (UnknownHostException e) {
                e.printStackTrace();
                Log.i(TAG,"socket-->"+e.getMessage());
                Log.i(TAG,"连接失败");
                Intent intent = new Intent(HEART_BEAT_ACTION);
                intent.putExtra("message", "fail");
                sendBroadcast(intent);
            } catch (IOException e) {
                e.printStackTrace();
                Log.i(TAG,"socket-->"+e.getMessage());
                Intent intent = new Intent(HEART_BEAT_ACTION);
                intent.putExtra("message", "fail");
                sendBroadcast(intent);
            }
        }
    }

    public class ReadThread extends Thread {
        private Socket rSocket;
        private boolean isStart = true;

        public ReadThread(Socket socket) {
            rSocket=socket;
        }

        public void release() {
            isStart = false;
            releaseLastSocket(rSocket);
        }

        @SuppressLint("NewApi")
        @Override
        public void run() {
            super.run();
            String line="";
            if (null != rSocket) {
                while (isStart&&!rSocket.isClosed()&&!rSocket.isInputShutdown()){
                    try {
                            Log.i(TAG,"开始读取消息");
                            is=rSocket.getInputStream();
                            count=is.available();
                            byte[] data=new byte[count];
                            is.read(data);
                            line=new String(data);
                            if (line!=null){
                                Log.i(TAG, "收到服务器发送来的消息:"+line);
                                if ("mdc_ok".equals(line)||"mdc_exist".equals(line)||"exist".equals(line)){
                                    sendMsg("connect");
                                    mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);// 初始化成功后,就准备发送心跳
                                    return;
                                }else if ("mdc_connect".equals(line)){//服务器发送继续接收的消息
                                    boolean isSuccess=sendMsg("connect");
                                    if (isSuccess){//成功发送,接收回执信息
                                        mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);// 初始化成功后,就准备发送心跳
                                    }else {//发送失败,处理善后工作
                                        mHandler.removeCallbacks(heartBeatRunnable);
                                        release();
                                        releaseLastSocket(socket);
                                    }
                                    return;
                                }else if ("mdc_connectexit".equals(line)||"exit".equals(line)){//断开连接消息
                                    Intent intent = new Intent(HEART_BEAT_ACTION);
                                    intent.putExtra("message", "exit");
                                    sendBroadcast(intent);
                                    return;
                                }
                            }else if (line==null){
                                Log.i(TAG, "服务器发送过来的消息是空的");
                            }

                        } catch (IOException e) {
                            Log.i(TAG,"Read-->"+e.getClass().getName());
                            e.printStackTrace();
                            continue;
                        }
                }
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG,"onDestroy");
        mHandler.removeCallbacks(heartBeatRunnable);
        mReadThread.release();
        releaseLastSocket(socket);
    }
}

以上代码只是Service中的代码亲测可用;在开发过程中也遇到很多问题,有些已解答,有些仍未解决,在此记录,i希望有了解的可以告知以下。

1.当 OutputStream os,调用os.close()或者 InputStream os,调用is.close()时,会将socket关闭,开始时调用os.close()导致scoket关闭,在后面读取服务器的消息时不断抛出socket closed异常,经检查后发现此处有问题;若想将输出/入流关闭,可调用 socket.shutdownInput();/socket.shutdownOutput();此时socket不会关闭;

2.在ReadThread线程中,读取服务器消息时开始使用BufferedReader in=new BufferedReader(new InputStreamReader(rSocket.getInputStream()));   String line=in.readLine();然后一直抛出超时异常,使用Debug模式调试后发现,line当中已经读取到正确的内容,但是readLine()方法在读到“\n”标识符后才会完成读取,不然会一直等待读取直到抛出超时异常,并且readLIne()是阻塞线程,未完成读取时程序一直阻塞,无法继续向下进行,直到超时进入catch中。此时应该使用字节读取。

3.由于客户端一直在轮询发送并接收服务起的消息,当服务器端socket主动断开时,客户端也要断开,但是在开发中,服务器端的socket断开时,客户端一直在while循环中读取消息,使用字节读取时,调用到is.read(data);方法也不会抛出异常(但是好多文章中都写此时会抛出异常,但是我这里没有抛出,原因未知),而是一直读取字节长度为0 的空数据却不结束,使用socket的isClosed()、isConnected()、isInputStreamShutdown()、isOutputStreamShutdown()等判断socket与服务器的连接状态均无效,原因在于上这些方法都是访问socket在内存驻留的状态,而非与服务器的实时状态,因此判断是无效的,以下三张截图:


此为客户端与服务器刚建立连接,此时未通消息


此为客户端向服务器发送消息成功后


此为服务器socket断开后的情况

从图中可以看到,三种情况下各方法返回的状态均相同,无法作为判断客户端与服务器实时连接情况的方法使用,网上查阅资料有网友提到,可以使用socket.sendUrgentData(0xFF);方法,向服务器发送数据,检测服务器是否断开连接,类似于ping,且该方法可往输出流发送一个字节的数据,只要对方Socket的SO_OOBINLINE属性没有打开,就会自动舍弃这个字节,而SO_OOBINLINE属性默认情况下就是关闭的,这样防止向服务器发送无效数据,若服务器断开了,则会在报出客户端报出异常,此时即可得知服务器的socket是否处于连接状态;本以为找到一个好方法,使用时才发现,即使服务器处于正常的连接状态,也会抛出异常(有好多文章说此方法可行,然而我没有搞通,不知是不是使用方法出错了,有了解相关情况的望告知),最后只有让服务器在断开socket时,发送一个标识符,即在代码中mdc_connectexit及exit(之所以会用这两个是因为服务器在发送数据时,会出现“粘包”的情况)。

Android与服务器实时交互技术详解 本文还有配套的精品资源,点击获取 简介:Android开发中,设备与服务器的交互主要用于实现数据的发送和接收,如登录验证和实时通信等。本文主要探讨通过Socket编程实现Android客户端与服务器之间的通信。内容包括HTTP/HTTPS协议和Socket编程基础,Android中Socket的实现细节,以及Socket连接的异常处理、权限设置、异步处理和安全通信等优化措施... 阅读详情

相关推荐

Android平台TCP长连接通信实战Demo

对于真正需要实现端到端直连的应用(如视频通话、文件共享),可采用WebRTC生态中的ICE框架,结合STUN与TURN服务器协助完成NAT穿透。:帮助设备发现其公网映射地址和端口。:当中继不可避免时,提供数据转发服务。:综合多种候选路径,选择最优连接方式。虽然Android原生不直接支持WebRTC,但可通过libwebrtc库集成相关能力。适用于音视频聊天、远程桌面等高性能需求场景。

weixin_29781865的博客 1361

Android TCP长连接 心跳机制及实现

转自:https://blog.csdn.net/ttkatrina/article/details/72956338             背景知识                 智能手机上的长连接心跳和在Internet上的长连接心跳有什么不同                 Android系统的推送和iOS的推送有什么区别                 几种推送的实现方式      ...

shark1621的博客 473

Android的socket长连接(心跳检测)

这是Android的socket长连接(心跳包),由于本人项目中需要用到长连接,所以先做了个demo,亲测是没有问题的。

android手机端发心跳包给服务端保持socket连接,android socket 发送心跳包

在项目中,有如下需求:Android客户端向服务器发送数据,收到服务器返回的数据发送成功标识后,客户端即与服务器建立数据一来一往的心跳连接,若服务器端断开时,客户端接收到通知,关闭Service停止发送数据;代码如下:publicclassBackServiceextendsService{privatestaticfinalStringTAG="BackService";/...

weixin_34261380的博客 459

Androidsocket通信加长连接(有心跳检测)

Android_socket通信加长连接(有心跳检测)都有注释,代码简洁

Matlab实战:12种优化算法在CEC2014测试集上的性能对比(附完整代码)

本文详细对比了12种优化算法在CEC2014测试集上的性能表现,包括PSO、DE、GA等主流算法。通过Matlab实现完整的实验流程,涵盖算法实现、参数设置、结果分析与可视化,为工程优化问题提供实用解决方案。特别关注收敛曲线和评价指标,帮助读者选择最适合特定场景的优化算法。

wine的专栏 681

android长连接心跳机制

在写之前,我们首先了解一下为什么android维护长连接需要心跳机制,首先我们知道,维护任何一个长连接都需要心跳机制,客户端发送一个心跳给 服务器,服务器给客户端一个心跳应答,这样就形成客户端服务器的一次完整的握手,这个握手是让双方都知道他们之间的连接是没有断开,客户端是在线 的。如果超过一个时间的阈值,客户端没有收到服务器的应答,或者服务器没有收到客户端的心跳,那么对客户端来说则断开

rabbit_in_android的博客 2万+

通义千问对接FreeSWITCH实现大模型呼叫中心

通义千问是由阿里云开发的大规模语言模型,具备优秀的自然语言处理能力,支持文本生成、问答对话、摘要提取等多种任务。它不仅拥有丰富的知识储备,还能根据上下文灵活调整回答策略,适用于各种复杂场景下的自动化交流。FreeSWITCH是一个开源的多媒体通信服务器,提供了包括VoIP通话、即时消息传递在内的多种通信服务。它的模块化设计允许开发者根据实际需求定制功能,广泛应用于企业级通信系统中。通过集成通义千问,FreeSWITCH能够实现更智能的客户服务体验。

技术永不眠 Q:53195017 1498

超简单心跳包

 一、先新建一个发送心跳的广播 public class MainActivity extends Activity {  private Timer mTimer;  private int timejumping = 0;    @Override  protected void onCreate(Bundle savedInstanceState) {   sup

qq_34499965的博客 986

如何修复Windows卡在“正在准备Windows”的问题?这里有详细步骤

如何修复Windows卡在“正在准备Windows”的问题?这里有详细步骤。

wyxtx的博客 5662

空地多无人平台协同路径规划复现:从建模到Matlab仿真全解析

路径规划是机器人领域的核心基础技术,从单机避障到多平台协同,问题复杂度显著提升。空地协同规划融合无人机与无人车的异构优势,通过将地图分层建模为空中与地面栅格,并采用A*算法生成初始路径、B样条平滑以及改进遗传算法进行任务分配与时间同步优化,能够有效处理几何约束、运动学约束和避碰约束等。该技术广泛应用于区域巡逻、目标搜索、物资转运等任务场景,也是高校论文复现和工程仿真的常见课题。本文从问题建模、算法设计到Matlab代码结构,系统梳理了一套空地多无人平台协同路径规划的复现思路,同时给出坐标系校准、遗传算法收敛

weixin_32175665的博客 248

Android的socket通信的长连接,有心跳检测

        在Android开发中,我们可能需要和服务器保持连接不断开,这时需要用到socket通信的长连接,并且定时发送消息检测是否是连接状态——心跳检测。         我们需要一个客户端和一个服务器端的demo,现在我就贴出重要代码,主要是Android客户端的,服务器端的demo供大家下载。 首先我们需要新建一个BackService类来继承Service:   packa...

IT张的专栏 1万+

《遥感大模型生产级实战:从数据到决策的端到端AI工程》系列第一部分:AI遥感工程师的基础设施与工具链-文章 4:传统遥感分析与机器学习基线

本章将系统性地介绍经典遥感分析工作流,从数据预处理到多维度特征工程,再到如何利用经典的机器学习模型(如随机森林、支持向量机)进行监督与非监督学习。我们将通过一个完整的“智慧农业作物分类”项目实战,展示如何利用我们之前构建的数据湖和工具链,从原始数据到最终的分析结果,构建一个性能可靠、可解释性强的基线系统。更重要的是,我们将深入剖析这种传统方法的局限性,从而为后续章节引入遥感大模型的必要性提供强有力的论证。

panshengwu的博客 705

Android socket通信的长连接与心跳检测

Android socket通信的长连接与心跳检测 2017年08月02日 17:13:15 阅读数:1804 标签: socketandroid通信心跳检测 更多 个人分类: android-杂项 在Android开发中,我们可能需要和服务器保持连接不断开,这时需要用到socket通信的长连接,并且定时发送消息检测是否是连接状态——心跳检测。  我们需要一个客户端和一个服务器端的demo,...

Stable_magma 694

⭐️【2024年新版教程】vmware tools安装步骤(Windows、Linux、MacOS版本)

VMware Tools是VMware提供的一套实用工具,它可以增强虚拟机与宿主机之间的交互性能,如改进显示性能、优化鼠标和键盘性能、提供时间同步功能等。无论是在Windows、Linux还是MacOS虚拟机上,安装VMware Tools都可以提高虚拟机的使用体验。以上就是在不同操作系统上安装VMware Tools的步骤,希望对您有所帮助。

Python老吕的博客 6万+

ExpressLRS(ELRS)接收机技术全景:从历史演进、技术底层到产业生态全解析——上

ExpressLRS(ELRS)是一种由全球开源社区驱动的双向射频遥控链路协议栈,其接收机作为核心终端,承担信号接收解调、遥控指令解析、飞控数据交互与遥测回传等关键功能。自2018年问世以来,ELRS凭借极致性能、低成本、开放生态和强兼容性,从航模DIY方案发展为消费级无人机领域的事实标准,并逐步渗透工业级场景。 ELRS接收机采用模块化分层架构,支持2.4GHz和900MHz双频段,通过LoRa、FLRC、FSK多调制模式实现低延迟与远距离平衡。其核心技术包括自适应跳频抗干扰、TDD双向链路和极简数据包。

bother3000的博客 303

Android 心跳检查更新,在Android上面如何使用带有心跳检测的Socket

由于移动设备的网络的复杂性,经常会出现网络断开,如果没有心跳包的检测,客户端只会在需要发送数据的时候才知道自己已经断线,会延误,甚至丢失服务器发送过来的数据。一下简单建立一个带有心跳检测的SocketDemo。Android 后台Servevicepackage org.feng.sockettest.server;import java.io.IOException;import java.io...

weixin_33574009的博客 400

【DaVinci Configurator专题】-6-将UDS&OBD诊断数据库文件CDD和PDX导入到CFG

CDD和ODX是两种诊断数据库文件。机器可读的诊断数据库其实就是将传统的Excel调查表等信息编辑成CDD或ODX文件。CDD是Vector私有的一种诊断数据库格式;ODX是国际标准的诊断数据交互格式。CANdelaStudio编辑CDD文件,以及也可以导出ODX文件;ODX Studio用于编辑ODX文件,诊断数据库的编辑其实就是诊断需求的定义。 下面以PDX为例,将其导入到DaVinci Configurator。 目录 1 打开DaVinci Configurator,导入PDX文件 2 解.

「汽车电子助手」的博客 6751

Java Socket通信及心跳包

最近android项目中有需要用到Socket与服务器连接的项目,自己之前没有使用过Socket套接字这种方案,所以对Socket通信机制就是个也很有限,领导在会议上讲Socket通讯呢就是客户端和服务器端先进行一次握手,双方同意后就可以进行数据传送。我心里最反感别人说这句话如果是大学老师第一次给我们讲Socket概念的话,这话我乐意听。对这有开发经验的程序员竟然还这么跟我介绍Socket,我要的

jbgtwang的专栏 2万+

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Android socket与服务器通信长连接通信

近期因项目需要用到Socket通信,实现客户端Socket 与 服务器通信。实现客户端发送信息--》服务器接收信息---》服务器返回信息--》客户端接收返回信息的长连接通讯,已测试通过。     服务端代码:      package com.cj.socket; import java.io.BufferedReader; import java.io.BufferedWrite

宝爷的专栏 1616

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

socket长时间连接,需要心跳包。

其次socket的长时间连接的话,是需要心跳包。

qingzi635533的专栏 1万+

Android通过socket长连接实现推送

工具:Android studio 软件方法及协议:socket、protobuf 实现原理: 通过本地建立一个socket,绑定服务器IP和port,然后connect,再开启另外线程定时心跳(注意这里的心跳不是自定义发送数据,而是采用socket本身的心跳功能sendUrgentData,否则有坑),心跳失败则自动重连,另一方面,启动循环从缓存获取socket数据。 大致框架 推送实...

飞剑的博客 166

Android_Java Socket连接参数传递与心跳启动

Socket连接与心跳机制实现摘要 本教程展示了Android平台下Socket客户端的完整实现,主要包含以下核心功能: 连接管理:支持IP地址和端口配置,通过多线程处理连接建立和断开 参数传递:在连接成功后立即发送初始化参数(如任务ID) 心跳机制:5秒间隔的心跳检测,确保连接活跃 自动重连:3秒延迟后自动重连机制 状态回调:通过接口提供连接状态、心跳状态和消息接收回调 READY响应处理:服务器返回READY后启动心跳并通知UI 实现采用生产者-消费者模式,通过Handler实现线程间通信,确保UI线程

记录工作中的一些心得,学习并整理为教程 987

Android service 心跳检测

package org.feng.sockettest.server; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.net.Socket; import java.net.U

qizhenghao的专栏 1016

android socket第三方库,OkSocket 一个Android轻量级Socket通讯框架

OkSocket DocumentAn blocking socket client for Java application or Andorid. Open source licensesBasic Socket LibrarySocket Server Plugin Library中文在线文档OkEchoServer Open SourceBecause the echo s...

weixin_39604092的博客 1143
上一篇: Android 集合操作关于Exception in thread "main" java.util.ConcurrentModificationException
下一篇: Android ScrollView中嵌套ExpandableListView,item不显示的问题
解牛之术
博客等级 码龄11年 49粉丝 46原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值