昨天三点钟才睡觉的,现在胸口感觉闷闷的,兄弟们,我是不是要GG了?如果我G了,求大佬们给我烧个女朋友,

ss.gif

1.在使用Socket连接客户端和服务器端的时候,如果服务端断开了连接,我们客户端是收不到任何回调消息的,只是在你发送消息给服务器的时候,会走异常,表示发送失败。

2.所以要判断服务器是否在线,就需要客户端不停的发送心跳消息给服务器,服务器收到心跳消息,就立马回复给你消息,这样就 能知道双方是否都在线。

3.如果在一段时间内,还是没有收到服务器回复的消息,就表示服务器可能已经死了,这时候你可能需要去做一些提示信息给Android前台。

4.在这一段时间内,你可以不停的尝试重新建立Socket连接,即断线重连。

上代码吧:

首先正常创建一个Activity,并创建一个TcpService服务,在服务中去进行Socket的相关操作。在connection中回调的clientBinder 对象,就是Activity和Service通讯的桥梁。上一篇我们是在Activity里去进行Socket测试的,用Service显然要比用Activity好的多。

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Intent intent = new Intent(this,TcpService.class);

bindService(intent,connection,BIND_AUTO_CREATE);

}

ServiceConnection connection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

TcpService.ClientBinder clientBinder = (TcpService.ClientBinder) service;

clientBinder.startConnect();

}

@Override

public void onServiceDisconnected(ComponentName name) {

}

};

}

在TcpService的onBind()方法中,我们首先需要返回ClientBinder这个对象,然后调用clientBinder.startConnect()建立Socket连接。

public void startConnect() {

//在子线程进行网络操作

// Service也是运行在主线程,千万不要以为Service意思跟后台运行很像,就以为Service运行在后台子线程

if (mExecutorService == null) {

mExecutorService = Executors.newCachedThreadPool();

}

mExecutorService.execute(connectRunnable);

}

private Runnable connectRunnable = new Runnable() {

@Override

public void run() {

try {

// 建立Socket连接

mSocket = new Socket();

mSocket.connect(new InetSocketAddress("192.168.1.186", 8292), 10);

bis = new BufferedInputStream(mSocket.getInputStream());

bos = new BufferedOutputStream(mSocket.getOutputStream());

// 创建读取服务器心跳的线程

mReadThread = new ReadThread();

mReadThread.start();

//开启心跳,每隔3秒钟发送一次心跳

mHandler.post(mHeartRunnable);

tryCount = 1;

} catch (Exception e) {

tryCount ++ ;

e.printStackTrace();

Log.d(TAG, "Socket连接建立失败,正在尝试第"+ tryCount + "次重连");

mHandler.postDelayed(new Runnable() {

@Override

public void run() {

mExecutorService.execute(connectRunnable);

}

},mHeart_spacetime);

}

}

};

这里我创建了一个线程池,用线程池去处理Socket的需要联网的Runnable。然后就是正常的创建Socket连接,新建读取线程以及开启mHeartRunnable。在异常处理里面,如果建立Socket失败,就发送一个延时消息,重新去创建连接。下面我们看一下ReadThread。

public class ReadThread extends Thread {

@Override

public void run() {

int size;

byte[] buffer = new byte[1024];

try {

while ((size = bis.read(buffer)) != -1) {

String str = new String(buffer, 0, size);

Log.d(TAG,"我收到来自服务器的消息: " +str);

//收到心跳消息以后,首先移除断连消息,然后创建一个新的60秒后执行断连的消息。

//这样每次收到心跳后都会重新创建一个60秒的延时消息,在60秒后还没收到心跳消息,表明服务器已死,就会执行断开Socket连接

//在60秒钟内如果收到过一次心跳消息,就表明服务器还活着,可以继续与之通讯。

mHandler.removeCallbacks(disConnectRunnable);

mHandler.postDelayed(disConnectRunnable, mHeart_spacetime * 40);

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

ReadThread主要处理的是disConnectRunnable,接收到服务器的心跳消息就移除断连任务,然后重新创建一个新的断连任务。在指定的时间内没有收到服务端的心跳消息,断连任务就会执行。反之,则会又进入一个 "移除旧的—创建新的" 的循环。当然,这个发送心跳消息的时间间隔(mHeart_spacetime )肯定是要小于这个断连任务延时时间的(mHeart_spacetime * 40)。接下来,看一下mHeartRunnable,心跳发送失败以后立马执行重连操作。

private Runnable mHeartRunnable = new Runnable() {

@Override

public void run() {

sendData();

}

};

private void sendData() {

mExecutorService.execute(new Runnable() {

@Override

public void run() {

try {

bos.write("给你一张过去的CD,听听那时我们的爱情!".getBytes());

//一定不能忘记这步操作

bos.flush();

//发送成功以后,重新建立一个心跳消息

mHandler.postDelayed(mHeartRunnable, mHeart_spacetime);

Log.d(TAG, "我发送给服务器的消息: 给你一张过去的CD,听听那时我们的爱情!");

} catch (Exception e) {

e.printStackTrace();

Log.d(TAG, "心跳任务发送失败,正在尝试第"+ tryCount + "次重连");

//mExecutorService.schedule(connectRunnable,mHeart_spacetime, TimeUnit.SECONDS);

mExecutorService.execute(connectRunnable);

}

}

});

}

下面来看一下效果图:

客户端和服务器正常收发心跳。客户端每隔3秒钟发送一次心跳,服务器收到心跳后立马回复心跳。此时双方都正常在线。

当我停止服务端连接的时候,程序开始自动重连

然后我又重新开启了服务,可以看到在重连到第10次的时候,Socket连接重新建立,并正常收发心跳消息

最后,附上TcpService类和AppServer类的代码:

客户端代码

/**

* Create by Fiora on 2018/10/24 0024

*/

public class TcpService extends Service {

public static final String TAG = TcpService.class.getSimpleName();

@Nullable

@Override

public IBinder onBind(Intent intent) {

return new ClientBinder();

}

public class ClientBinder extends Binder {

private int mHeart_spacetime = 3 * 1000; //心跳间隔时间

private BufferedInputStream bis;

private BufferedOutputStream bos;

private ReadThread mReadThread;

private Handler mHandler = new Handler();

private Socket mSocket;

private ExecutorService mExecutorService;

private int tryCount = 0;//重试次数

public void startConnect() {

//在子线程进行网络操作

// Service也是运行在主线程,千万不要以为Service意思跟后台运行很像,就以为Service运行在后台子线程

if (mExecutorService == null) {

mExecutorService = Executors.newCachedThreadPool();

}

mExecutorService.execute(connectRunnable);

}

private Runnable connectRunnable = new Runnable() {

@Override

public void run() {

try {

// 建立Socket连接

mSocket = new Socket();

mSocket.connect(new InetSocketAddress("192.168.1.186", 8292), 10);

bis = new BufferedInputStream(mSocket.getInputStream());

bos = new BufferedOutputStream(mSocket.getOutputStream());

// 创建读取服务器心跳的线程

mReadThread = new ReadThread();

mReadThread.start();

//开启心跳,每隔15秒钟发送一次心跳

mHandler.post(mHeartRunnable);

tryCount = 1;

} catch (Exception e) {

tryCount ++ ;

e.printStackTrace();

Log.d(TAG, "Socket连接建立失败,正在尝试第"+ tryCount + "次重连");

mHandler.postDelayed(new Runnable() {

@Override

public void run() {

mExecutorService.execute(connectRunnable);

}

},mHeart_spacetime);

}

}

};

public class ReadThread extends Thread {

@Override

public void run() {

int size;

byte[] buffer = new byte[1024];

try {

while ((size = bis.read(buffer)) != -1) {

String str = new String(buffer, 0, size);

Log.d(TAG,"我收到来自服务器的消息: " +str);

//收到心跳消息以后,首先移除断连消息,然后创建一个新的60秒后执行断连的消息。

//这样每次收到心跳后都会重新创建一个60秒的延时消息,在60秒后还没收到心跳消息,表明服务器已死,就会执行断开Socket连接

//在60秒钟内如果收到过一次心跳消息,就表明服务器还活着,可以继续与之通讯。

mHandler.removeCallbacks(disConnectRunnable);

mHandler.postDelayed(disConnectRunnable, mHeart_spacetime * 40);

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

private Runnable mHeartRunnable = new Runnable() {

@Override

public void run() {

sendData();

}

};

private void sendData() {

mExecutorService.execute(new Runnable() {

@Override

public void run() {

try {

bos.write("给你一张过去的CD,听听那时我们的爱情!".getBytes());

//一定不能忘记这步操作

bos.flush();

//发送成功以后,重新建立一个心跳消息

mHandler.postDelayed(mHeartRunnable, mHeart_spacetime);

Log.d(TAG, "我发送给服务器的消息: 给你一张过去的CD,听听那时我们的爱情!");

} catch (Exception e) {

e.printStackTrace();

Log.d(TAG, "心跳任务发送失败,正在尝试第"+ tryCount + "次重连");

//mExecutorService.schedule(connectRunnable,mHeart_spacetime, TimeUnit.SECONDS);

mExecutorService.execute(connectRunnable);

}

}

});

}

private Runnable disConnectRunnable = new Runnable() {

@Override

public void run() {

disConnect();

}

};

private void disConnect() {

mExecutorService.execute(new Runnable() {

@Override

public void run() {

try {

Log.d(TAG, "正在执行断连: disConnect");

//执行Socket断连

mHandler.removeCallbacks(mHeartRunnable);

if (mReadThread != null) {

mReadThread.interrupt();

}

if (bos != null) {

bos.close();

}

if (bis != null) {

bis.close();

}

if (mSocket != null) {

mSocket.shutdownInput();

mSocket.shutdownOutput();

mSocket.close();

}

} catch (Exception e) {

e.printStackTrace();

}

}

});

}

}

}

服务端代码:

/**

* Create by Fiora on 2018/10/24 0024

*/

public class AppServer {

public static final String TAG = AppServer.class.getSimpleName();

private static BufferedOutputStream bos;

private static BufferedInputStream bis;

private static Socket acceptSocket;

public static void main (String args[]){

try{

ServerSocket serverSocket = new ServerSocket(8292);

while(true) {

acceptSocket = serverSocket.accept();

bos = new BufferedOutputStream(acceptSocket.getOutputStream());

bis = new BufferedInputStream(acceptSocket.getInputStream());

ReadThread readThread = new ReadThread();

readThread.start();

}

}catch (Exception e){

e.printStackTrace();

}

}

private static class ReadThread extends Thread {

@Override

public void run() {

while (true) {

byte[] data = new byte[1024];

int size = 0;

try {

while ((size = bis.read(data)) != -1) {

String str = new String(data,0,size);

System.out.println(TAG+"----"+str);

//收到客户端发送的请求后,立马回一条心跳给客户端

bos.write("有时会突然忘了,我依然爱着你!".getBytes());

bos.flush();

}

} catch (Exception e) {

e.printStackTrace();

break;

}

}

}

}

}

android 心跳 简书,Android Socket保持心跳长连接,断线重连相关推荐

  1. android 心跳 简书,如何高效维持网络长连接:手把手教你实现 自适应的心跳保活机制...

    前言 当实现具备实时性需求时,我们一般会选择长连接的通信方式 而在实现长连接方式时,存在很多性能问题,如 长连接保活 今天,我将 手把手教大家实现自适应的心跳保活机制,从而能高效维持长连接 目录 示意 ...

  2. android 购物车 简书,Android仿饿了么购物车效果

    先看下效果图: ezgif-1-8f133ca916.gif 1.首先列表布局采用Recycleview android:id="@+id/container" android:l ...

  3. android matrix 简书,[Android] ImageView ScaleType完全解析

    ImageView有一个ScaleType的属性,该属性决定了图片在ImageView上的展现形式,包括:是否进行缩放.如何进行缩放.缩放之后图片的摆放位置等等.官方介绍如下: Options for ...

  4. android spinner 简书,Android NiceSpinner

    NiceSpinner 是 Android 端的一款第三方控件,自带箭头动画效果 真的是简单又好用哦!有需要的小伙伴们可以试试啦. 效果图如下: image 1. 导入模块 在 Github 下载并导 ...

  5. Android入门简书,android ndk开发入门随笔(一)

    ndk,jni入门随笔 因为工作缘故最近在研究jni,ndk方面知识,在此总结入坑以来的一些问题. 配置环境可以在下面geogle官方看.下面是链接 我一说ndk,jni可能小伙伴要问了这是什么,在此 ...

  6. android opengl 简书,Android OpenGL入门

    如今VR这么火,感觉有必要先把OpenGL学好,为以后转VR奠定一些基础.一年前,接触过Android的OpenGL,当时是实现了在Android上显示标准的3D文件(STL格式).现在打算整理一下O ...

  7. android room 简书,Android Room 的坑

    在添加依赖时,官网给出的是: def room_version = "2.0.0-beta01" implementation "androidx.room:room-r ...

  8. android 音乐 简书,Android音频开发(7):音乐可视化-FFT频谱图

    Android 音频开发 目录 一.演示 image 二.实现 实现流程: 使用MediaPlayer播放传入的音乐,并拿到mediaPlayerId 使用Visualizer类拿到拿到MediaPl ...

  9. android zxing简书,Android集成zxing 版本3.4.1

    第一步:去官网下载最新jar 或者 'implementation 'com.google.zxing:core:3.4.1''引入项目. 如下图: yhx.png 如何查看最新版本: yhx.png ...

最新文章

  1. 别盲目调参!深度学习要先找到最佳策略
  2. java string 后几位_java中String占几个位元组
  3. php pdo输出数据库,PHP中PDO对像及PDOStatement::fetch()的用法数据库查询,结果输出处理...
  4. ubuntu之安装显卡驱动
  5. 仿生软体机器人就业咋样_SRT近亿元B轮融资,中国软体机器人技术从空白到全球领先...
  6. spring入门——注入demo
  7. 181123每日一句
  8. firefly-rk3288使用USB GADGET实现大容量存储脚本
  9. 记解决win10报错“任务管理器已被管理员禁用”
  10. outlook qr码在哪里_原平防伪码溯源_橙程(北京)科技有限公司
  11. 1003: 两个整数的四则运算 Python
  12. 【爱吃肉的阿C】使用URL类将文件下载到本地
  13. jovi语音助手安装包_Jovi语音助手安装包下载-vivoJovi语音助手v3.1.1.0 最新版-腾牛安卓网...
  14. 基于python和高德地图租房系统的设计与实现
  15. 超越预期的精彩,2020深圳高博会开幕在即!
  16. 1 php方式实现购物车原理,PHP购物车实现的原理
  17. UE4蓝图节点翻译--- Get All Child Actors
  18. webgis中自定义地图颜色实现思路
  19. 新版标准日本语中级_第七课
  20. 内网web页面集成海康威视网络摄像头

热门文章

  1. 金融科技排头兵金证股份携手微丰,基于企业微信的新一代CRM为客户提供智能服务
  2. 如何领4只百度莱茨狗?莱茨狗微积分怎么获得?
  3. 市场调研公司欧睿国际揭晓描绘中国城市千禧一代和Z世代的8个消费趋势
  4. 2019年AI芯片产业深度研究报告
  5. SaaSBase:什么是Oracle Fusion ERP?
  6. Simscape Multibody --驱动关节添加摩擦力
  7. 电网变电站高空作业规范检测-安全绳、安全带佩戴检测图像数据集(voc,yolo两类标签)
  8. Swift语言基础笔记(二)
  9. 安装软件 错误:2503
  10. flex布局的整理 弹性布局