android使用websocket保持长连接 后台为nodejs带心跳检测
16lz
2021-01-23
本人原来是java工程师,但是最近没事就干起了android的活,我的具体需求是android使用websocket保持长连接 后台为nodejs 好了 不多说 开撸
nodejs方面使用的是nodejs-websocket,而不是http+ws+socket.io,使用后者在我的android代码会有bug:流意外断开(下图)java.io.IOException: unexpected end of stream on Connection 总之就是连接不上
为解决这个bug,困扰了我很久 查了很多资料 都是如下两图两种方法 还是不能解决 所以我就把想法放到了nodejs中 不使用socket.io 使用nodejs-websocket
说了这么多,放代码了nodejs:
//引入nodejs-websocket 如果报错 在cmd里命令:npm install nodejs-websocketvar ws= require("nodejs-websocket"); //广播通知方法 可有可无const broadcast = (server, info) => { console.log('broadcast', info) server.connections.forEach(function(conn) { conn.sendText(JSON.stringify(info)) })}var server = ws.createServer(function(conn){ //从前端收到信息 conn.on("text", function (str) {//发送消息使用 conn.send("发送消息:"+str);broadcast(server,str); //广播通知收到的消息 }); conn.on("close", function (code, reason ) { console.log("退出了连接=========》close"); }); conn.on("error", function (code, reason) { console.log("异常关闭"); });}).listen(3000);
android方面代码:
- InitSocketThread.java 我们在线程里执行连接操作
package com.Piggysnow.RtcView.util;import android.os.Handler;import android.support.annotation.Nullable;import android.util.Log;import com.Piggysnow.RtcView.MediaUtil;import com.Piggysnow.RtcView.service.BackService;import java.io.IOException;import java.net.UnknownHostException;import java.util.concurrent.TimeUnit;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;import okhttp3.WebSocket;import okhttp3.WebSocketListener;import okio.ByteString;public class InitSocketThread extends Thread{ BackService service; Handler serviceHandler; public InitSocketThread(){} public InitSocketThread(BackService service,Handler handler) { this.service = service; this.serviceHandler=handler; } @Override public void run() { super.run(); try { initSocket(); } catch (Exception e) { e.printStackTrace(); } } /** * 心跳检测时间 */ private static final long HEART_BEAT_RATE = 15 * 1000;//每隔15秒进行一次对长连接的心跳检测 private static final String WEBSOCKET_HOST_AND_PORT = "ws://192.168.8.104:3000";//可替换为自己的主机名和端口号 public static WebSocket mWebSocket; // 初始化socket public void initSocket() throws UnknownHostException, IOException { Log.e("INITSOCKET", "initsocket in"); OkHttpClient client = new OkHttpClient.Builder().readTimeout(5000, TimeUnit.MILLISECONDS).connectTimeout(6000,TimeUnit.MINUTES).build(); Request request = new Request.Builder() .url(WEBSOCKET_HOST_AND_PORT).build(); Log.e("INITSOCKET", request.url().uri().toString()); client.newWebSocket(request, new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) {//开启长连接成功的回调 super.onOpen(webSocket, response); Log.e("INITSOCKET", "initsocket onOpen"); mWebSocket = webSocket; mWebSocket.send("MyId?id=123456"); //向服务器发送id } @Override public void onMessage(WebSocket webSocket, String text) {//接收消息的回调 super.onMessage(webSocket, text); Log.e("SENDTEXT", text); //输入消息 } @Override public void onMessage(WebSocket webSocket, ByteString bytes) { super.onMessage(webSocket, bytes); } @Override public void onClosing(WebSocket webSocket, int code, String reason) { super.onClosing(webSocket, code, reason); } @Override public void onClosed(WebSocket webSocket, int code, String reason) { Log.e("INITSOCKET", "initsocket onClosed"); super.onClosed(webSocket, code, reason); } @Override public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) {//长连接连接失败的回调 mWebSocket=webSocket; Log.e("INITSOCKET", "initsocket onFailure"); super.onFailure(webSocket, t, response); t.printStackTrace(); } }); mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//开启心跳检测 } private static long sendTime = 0L; // 发送心跳包 private static Handler mHandler = new Handler(); private static Runnable heartBeatRunnable = new Runnable() { @Override public void run() { if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) { if(mWebSocket==null) { mHandler.postDelayed(this, HEART_BEAT_RATE);//每隔一定的时间,对长连接进行一次心跳检测 return; } boolean isSuccess = mWebSocket.send("");//发送一个空消息给服务器,通过发送消息的成功失败来判断长连接的连接状态 if (!isSuccess) {//长连接已断开 mHandler.removeCallbacks(heartBeatRunnable); mWebSocket.cancel();//取消掉以前的长连接 new InitSocketThread().start();//创建一个新的连接 } else {//长连接处于连接状态 } sendTime = System.currentTimeMillis(); } mHandler.postDelayed(this, HEART_BEAT_RATE);//每隔一定的时间,对长连接进行一次心跳检测 } };}
- BackService
package com.Piggysnow.RtcView.service;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.Handler;import android.os.IBinder;import android.support.annotation.Nullable;import android.util.Log;import com.Piggysnow.RtcView.util.InitSocketThread;public class BackService extends Service { protected Handler handler=new Handler(); @Override public void onCreate() { super.onCreate(); Log.e("INIT", "Backserver init on create"); new InitSocketThread(this,handler).start(); } @Nullable @Override public IBinder onBind(Intent intent) { return new MyBinder(); } public class MyBinder extends Binder { public BackService getMyService(){ return BackService.this; } } /** * 在Service中定义这个方法,用于测试 * @return */ public String getAuthorName(){ return "guchuanhang"; } @Override public void onDestroy() { super.onDestroy(); if (InitSocketThread.mWebSocket != null) { InitSocketThread.mWebSocket.close(1000, null); } } public static void main(String[] args) { BackService service=new BackService(); service.onCreate(); }}
- Activity类中需要新增的代码(记住是新增)
BackService myService=null; ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { myService= ((BackService.MyBinder) service).getMyService(); String authorName = myService.getAuthorName(); Toast.makeText(UnityPlayerActivity.this, "author name is: " + authorName, Toast.LENGTH_LONG).show(); } @Override public void onServiceDisconnected(ComponentName name) { } };
- Activity类onCreate方法中需要新增的代码(记住是新增)
Intent myServiceIntent = new Intent(UnityPlayerActivity.this, BackService.class); bindService(myServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
然后就到这里结束了 希望大家能够连接成功!!!欢迎大家评论
更多相关文章
- Ubuntu共享WiFi(AP)给Android方法【修正版】
- android 使用代码方式创建自定义progressBar——自定义控件学习(
- Android中外部程序调用方法总结
- Android http请求例子、Unicode转UTF-8 java代码收藏
- Android SQLite数据库 《第一行代码》
- [置顶] Android中数据存储的5种方法