安卓手機遠程桌面連接軟件,Android 實現WebSocket長連接

 2023-11-18 阅读 42 评论 0

摘要:最近項目中引入了實時接收服務器數據的功能,考量后通過WebSocket長鏈接來實現。 接下來了解一下webSocket 的特點: 1、建立在 TCP 協議之上,服務器端的實現比較容易。 2、與 HTTP 協議有著良好的兼容性。默認端口也是80和443,并且握手階段采用 HT

最近項目中引入了實時接收服務器數據的功能,考量后通過WebSocket長鏈接來實現。

接下來了解一下webSocket 的特點:
1、建立在 TCP 協議之上,服務器端的實現比較容易。
2、與 HTTP 協議有著良好的兼容性。默認端口也是80和443,并且握手階段采用 HTTP 協議,因此握手時不容易屏蔽,能通過各種 HTTP 代理服務器。
3、支持雙向通信,實時性更強。
4、數據格式比較輕量,性能開銷小,通信高效。
5、可以發送文本,也可以發送二進制數據。
6、沒有同源限制,客戶端可以與任意服務器通信。
7、協議標識符是ws(如果加密,則為wss),服務器網址就是 URL。

在我的項目計劃中是通過Okhttp3實現socket長連接,先看一下效果:
依次展示的效果圖為開啟長鏈接------關閉長鏈接-------重連長鏈接

安卓手機遠程桌面連接軟件?

接下來看一下具體的代碼
1、長鏈接初始化,創建Service服務,通過bindService啟動Service服務

/*** 獲取單例,非appContext,要先init*/public static ImWebSocketBackgroundService getInstance(Context context) {if (instance == null) {synchronized (ImWebSocketBackgroundService.class) {if (instance == null) {instance = new ImWebSocketBackgroundService(context);}}}return instance;}private ImWebSocketBackgroundService(Context context) {this.context = context;}private void onCreate() {if(webSocketServiceManager == null){webSocketServiceManager = new WebSocketServiceManager(context, this);webSocketServiceManager.bindService();sendGeneration = 0;}}
public class MyWebSocketService extends Service implements WebSocketResultListener {private WebSocketThread webSocketThread;private IWebsocketResponseDispatcher responseDispatcher;private SocketResultListenerStorage socketResultListenerStorage = new SocketResultListenerStorage();@Overridepublic void onCreate() {super.onCreate();webSocketThread = new WebSocketThread();webSocketThread.setWebSocketResultListener(this);webSocketThread.start();responseDispatcher = new WebsocketResponseDispatcher();}@Nullable@Overridepublic IBinder onBind(Intent intent) {return new WebSocketBinder();}@Overridepublic void onDestroy() {super.onDestroy();if (webSocketThread.getHandler() != null)webSocketThread.getHandler().sendEmptyMessage(MessageType.QUIT);}public boolean sendText(String text) {if (webSocketThread.getHandler() != null) {Message message = Message.obtain();message.obj = text;message.what = MessageType.SEND;return webSocketThread.getHandler().sendMessage(message);}return false;}
}

安卓自動連接wifi,在WebSocketServiceManager中創建ServiceConnection類型實例,并重寫onServiceConnected()方法和onServiceDisconnected()方法。
當執行到onServiceConnected回調時通過IBinder實例得到Service,實現client與Service的連接
onServiceDisconnected回調被執行時,表示client與Service斷開連接

public WebSocketServiceManager(Context context, WebSocketResultListener socketResultListener) {this.context = context;this.socketResultListener = socketResultListener;}private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.e(WebSocketThread.TAG, "WebSocketService 已經連接");serviceBindSucc = true;binding = false;bindTime = 0;   //連接成功后歸零myWebSocketService = ((MyWebSocketService.WebSocketBinder)service).getWebSocketService();myWebSocketService.addSocketListener(socketResultListener);if(myWebSocketService.isSendConnected()) {if (socketResultListener != null) {socketResultListener.connection();}}}@Overridepublic void onServiceDisconnected(ComponentName name) {     //service異常被關閉 回調該方法binding = false;serviceBindSucc = false;if (bindTime<5 && !binding){Log.e(WebSocketThread.TAG, String.format("WebSocketService 連接斷開,開始第%s次重連", bindTime));bindService();}}};//綁定服務public void bindService(){serviceBindSucc = false;binding = true;Intent intent = new Intent(context, MyWebSocketService.class);context.bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);bindTime++;}

2、在service創建時初始化Thread線程,通過Handler發送消息建立連接
? ?2.1?配置OkHttpClient
其中head中需要的參數依照大家項目中需要參數隨時改變

private void initHeader() {headerMap.put("sourse", "android");headerMap.put("appVersion", Constants.VERSION_CODE);headerMap.put("token", Constants.TOKEN);headerMap.put("devCode",Constants.DevCode);}
OkHttpClient mClient = new OkHttpClient.Builder().readTimeout(3, TimeUnit.SECONDS)//設置讀取超時時間.writeTimeout(3, TimeUnit.SECONDS)//設置寫的超時時間.connectTimeout(3, TimeUnit.SECONDS)//設置連接超時時間.build();

?2.2 使用Url,構建WebSocket請求
WEB_SOCKET_URL = "ws://47.74.235.101:8190/ws-community-im-websocket";
WEB_SOCKET_URL 依照大家項目中具體路徑改變

private void initRequest() {Request.Builder requestBuilder = new Request.Builder().url(WEB_SOCKET_URL);if(headerMap == null){initHeader();}if (headerMap != null && !headerMap.isEmpty()) {Set<String> strings = headerMap.keySet();StringBuffer stringBuffer = new StringBuffer();for (String string : strings) {stringBuffer.append(string).append(" ").append(headerMap.get(string)).append("\t");}Log.e("web", stringBuffer.toString());requestBuilder.headers(Headers.of(headerMap));}request = requestBuilder.build();}

打開遠程桌面連接。2.3 發起連接,配置回調。

  • onOpen(),連接成功
  • onMessage(String text),收到字符串類型的消息,一般我們都是使用這個
  • onMessage(ByteString bytes),收到字節數組類型消息,我這里沒有用到
  • onClosed(),連接關閉
  • onFailure(),連接失敗,一般都是在這里發起重連操作
//開始連接
WebSocket websocket = mClient.newWebSocket(request, new WebSocketListener() {@Overridepublic void onOpen(WebSocket webSocket, Response response) {super.onOpen(webSocket, response);//連接成功...}@Overridepublic void onMessage(WebSocket webSocket, String text) {super.onMessage(webSocket, text);//收到消息...(一般是這里處理json)}@Overridepublic void onMessage(WebSocket webSocket, ByteString bytes) {super.onMessage(webSocket, bytes);//收到消息...(一般很少這種消息)}@Overridepublic void onClosed(WebSocket webSocket, int code, String reason) {super.onClosed(webSocket, code, reason);//連接關閉...}@Overridepublic void onFailure(WebSocket webSocket, Throwable throwable, Response response) {super.onFailure(webSocket, throwable, response);//連接失敗...}
});

2.4 使用WebSocket對象發送消息,msg為消息內容,send方法會馬上返回發送結果

//發送消息
webSocket.send(msg);


3、通過第2步連接成功后啟動心跳,每隔10秒鐘發起一次心跳,在onMessageReceive中接收到后臺返回的json信息,在本項目中通過后臺返回的code值判斷此時時在線收到消息還是斷開連接,大家依照各自項目改變

SEND_MESSAGE = 601;  //在線接收消息
private static final int DISCONNECT_MESSAGE = 602; //斷開連接
   //開始心跳public void startHeartBeat() {Log.e(WebSocketThread.TAG, "啟動心跳");runHeartBeat = true;if (scheduler == null) {scheduler = Executors.newScheduledThreadPool(1);scheduler.schedule(this, getDelay(), TimeUnit.SECONDS);}}@Overridepublic void onMessageReceive(String jsonText) {try {JSONObject jsonObject = new JSONObject(jsonText);Log.i(WebSocketThread.TAG,jsonObject+"");if(jsonObject.has("msgType") && Integer.valueOf(jsonObject.getInt("msgType")) != null){int msgType = jsonObject.getInt("msgType");if (msgType == SEND_MESSAGE) {     //發送消息String msg = jsonObject.getString("msg");Log.d(WebSocketThread.TAG, msg);} else if (msgType == DISCONNECT_MESSAGE) {     //斷開連接stopHeartBeat();}}} catch (JSONException e) {e.printStackTrace();}}@Overridepublic void connection() {Log.e(WebSocketThread.TAG, "連接成功");startHeartBeat();}@Overridepublic void run() {if (runHeartBeat) {if (sendText(defineText())) {sendGeneration++;Log.d(WebSocketThread.TAG,"發送心跳成功"+sendGeneration);}scheduler.schedule(this, getDelay(), TimeUnit.SECONDS);}}private int getDelay() {return 10;}

4、重連機制----處理切換網絡,被擠掉線等情況
在第3步中提到的onFailure()方法中進行重連,連接成功后重新接收到后臺返回的json信息

public class ReconnectWebSocketManager {private WebSocketThread webSocketThread;private volatile boolean retrying;      //正在重新連接private volatile boolean destroyed ;private final ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();       //單線程public ReconnectWebSocketManager(WebSocketThread webSocketThread) {this.webSocketThread = webSocketThread;this.retrying = false;this.destroyed = false;}synchronized void performReconnect(){retrying = false;retry();}private synchronized void retry() {if (!retrying){retrying = true;synchronized (singleThreadPool){singleThreadPool.execute(new ReconnectRunnable());}}}//銷毀public void destroy(){destroyed = true;if (singleThreadPool !=null)singleThreadPool.shutdownNow();webSocketThread = null;}public class ReconnectRunnable implements Runnable {public void run() {retrying = true;for (int i = 0;i <20 ; i++){if (destroyed){retrying = false;return;}Handler handler = webSocketThread.getHandler();if (handler !=null){if (webSocketThread.getConnectStatus() ==2) //已連接break;if (webSocketThread.getConnectStatus() ==1) //正在連接continue;if (webSocketThread.getConnectStatus() ==0)handler.sendEmptyMessage(MessageType.CONNECT);}else {break;}try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}retrying = false;}}}
}

到此長鏈接主要代碼已經講完了,別忘了在清單文件中注冊service,添加網絡權限

<service android:name="com.example.msg.imwebsocket.MyWebSocketService"/><uses-permission android:name="android.permission.INTERNET" />

在build.gradle中添加okhttp3依賴

implementation 'com.squareup.okhttp3:okhttp:3.10.0'

希望能對于長鏈接需求的同學幫助,歡迎討論,附上項目的代碼地址demo
?

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/180572.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息