Websocket在Android中的应用

242 阅读3分钟

WebSocket是一种全双工通信的协议,使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。websocket通信的实现包括服务端和客户端。
本文以java中的websocket和okhttp中的websocket分别进行分析。
首先分析以java中的websocket。
1,服务端
在服务端配置node js后,编写server.js文件,执行命令:node server.js后即可启动服务端。在server.js中

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8888 });

wss.on('connection', function connection(ws) {

	ws.on('message', function incoming(message) {
		try {
			let recvObj = JSON.parse(message);
			console.log('received: recvObj:%s', recvObj);
			switch (recvObj.cmdType) {
				case "cmd_hello":
					handleHello(recvObj, ws)
				break
			}
		} catch (e) {
			console.error('Failed to parse message:', e);
		}
		ws.send('Hello, you sent -> ' + message);
	});
	
	ws.send('Congratulations, you have connected to the server!');
	
	ws.on('close', function close() {
		roomMaps.forEach((room, roomId) => {
          if (room.has(ws.uid)) {
              room.delete(ws.uid);
              if (room.size === 0) {
                  roomMaps.delete(roomId);
              }
          }
		});
		console.log('connection closed');
	});
	
	ws.on('error', function error(error) {
		console.error('WebSocket Error: ', error);
	});
	
});

console.log('WebSocket server listening on port 8880');

class Client {
    constructor(uid,ws,roomId) {
        this.uid = uid;
        this.ws = ws;
        this.ws.uid = this.uid;
        this.roomId = roomId;
        this.ws.roomId = roomId;
    }
}

let roomMaps = new Map();
let room;

function handleHello(recvObj, ws) {
      let uid = recvObj.uid;
      let roomId = recvObj.roomId;
      room = roomMaps.get(roomId);
      if (!room) {
          console.log('handleJoinRoom...new room:');
          room = new Map()
      }
      if (room.has(uid)) {
          console.log('Already say hello');
          return
      }
      let client = new Client(uid,ws,roomId)
      room.set(uid, client)
      roomMaps.set(roomId, room)
      console.log('Entered to say hello');
      console.log('==================================Entered into room,uid:' + uid);
      console.log('Entered to say hello classmates:', room.keys());
}

function handleDadianhua(recvObj, ws) {
    let uid = recvObj.uid;
    let beCalledUser = recvObj.beCalledUser;
    if (room.size > 1){
          let clients = Array.from(room.keys())
          clients.forEach(remoteUid => {
          //if (remoteUid !== uid){
          if (remoteUid == beCalledUser){
              let sendObj = {
                  cmdType: "cmd_shake",
                  uid: uid,
                  remoteUid
                }
              let remoteClient = room.get(remoteUid)
              if (remoteClient) {
                  remoteClient.ws.send(JSON.stringify(sendObj));
                  console.log("successful");
              } else {
                  console.log("Failed");
              }
            }
        })
    }
}

function handleWoshou(recvObj, ws) {
    let uid = recvObj.uid;
    let remoteUid = recvObj.remoteUid;
    if (room.size > 1){
          let clients = Array.from(room.keys())
          clients.forEach(remoteUid => {
          if (remoteUid !== uid) {
              let sendObj = {
                  cmdType: "cmd_new",
                  uid: uid,
                  remoteUid
                }
              let remoteClient = room.get(remoteUid)
              if (remoteClient) {
                  remoteClient.ws.send(JSON.stringify(sendObj));
                  console.log("successful");
              } else {
                  console.log("Failed");
              }
            }
        })
    }
}


function handleOffer(recvObj) {
  let remoteUid = recvObj.remoteUid
  let roomId = recvObj.roomId
  let room = roomMaps.get(roomId)
  let client = room.get(remoteUid)
  if (client) {
    client.ws.send(JSON.stringify(recvObj))
  }
}

function handleAnswer(recvObj) {
    let remoteUid = recvObj.remoteUid
    let roomId = recvObj.roomId
    let room = roomMaps.get(roomId)
    let client = room.get(remoteUid)
    if (client) {
        client.ws.send(JSON.stringify(recvObj))
    }
}

function handleIce(recvObj) {
    let remoteUid = recvObj.remoteUid
    let roomId = recvObj.roomId
    let room = roomMaps.get(roomId)
    let client = room.get(remoteUid)
    if (client) {
        client.ws.send(JSON.stringify(recvObj))
    }
}

function handleGuaduan(recvObj) {
    let uid = recvObj.uid
    let roomId = recvObj.roomId
    let room = roomMaps.get(roomId);
    if (room.size > 1){
        let clients = Array.from(room.keys())
        clients.forEach(remoteUid => {
           if (remoteUid !== uid){
               let sendObj = {
                   cmdType: "cmd_hang,
                   uid: uid,
                   remoteUid
               }
               let remoteClient = room.get(remoteUid)
               if (remoteClient) {
                   remoteClient.ws.send(JSON.stringify(sendObj));
               } else {
                   console.log("Failed");
               }
           }
        })
    }
}

function handleXiaoxi(recvObj) {
    let uid = recvObj.uid
    let roomId = recvObj.roomId
    let room = roomMaps.get(roomId);
    if (room.size > 1){
        let clients = Array.from(room.keys())
        clients.forEach(remoteUid => {
           if (remoteUid !== uid){
               let remoteClient = room.get(remoteUid)
               if (remoteClient) {
                   remoteClient.ws.send(JSON.stringify(recvObj));
               } else {
                   console.log("Failed");
               }
           }
        })
    }
}

//If you want to get the ip address in the ethernet,you could use these codes below:
const { exec } = require('child_process');
console.log('WebSocket server exec arp -a');
exec('arp -a', (error, stdout, stderr) => {
	if (error) {
		console.error(`exec error: ${error}`);
		return;
	}

	const ipRegex = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g;
	const matches = stdout.match(ipRegex);
	if (matches) {
		console.log(matches);
	}
	console.log(`stdout: ${stdout}`);
	if (stderr) {
		console.log(`stderr: ${stderr}`);
	}
});

2,客户端
首先定义一个继承自WebSocketClient的类

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.URI;

public class WsClient extends WebSocketClient {

	public WsClient(URI serverUri) {
        super(serverUri);
    }
	
	private String lookIntoJsonObject(String str) {
        if (TextUtils.isEmpty(str)) {
            return "false";
        }
        String jsonStr = "";
        if (str.contains("{") && str.contains("}")) {
            int index = str.indexOf("{");
            jsonStr = str.substring(index);
        } else {
            Log.v(TAG, "not Json");
            return "";
        }
        return jsonStr;
    }
	
	@Override
    public void onOpen(ServerHandshake handshakedata) {
        Log.d(TAG, "onOpen,server:" + getURI());
    }
	
	@Override
    public void onMessage(String message) {
		String jsonStr = lookIntoJsonObject(message);
        if (TextUtils.isEmpty(jsonStr)) {
            return;
        }
        JSONObject jsonObject = new JSONObject(jsonStr);
        String type = jsonObject.getString("Type");
		switch (type) {
           case CMD_HELLO:
                break;
		}
	}

	@Override
    public void onClose(int code, String reason, boolean remote) {
        Log.v(TAG, "onClose,code:" + code + ",reason:" + reason + ",remote:" + remote);
    }

    @Override
    public void onError(Exception ex) {
        Log.v(TAG, "onError:" + ex);
    }
}

接着在Activity类中调用上面的WebSocketClient类。

private WsClient wsClient;
	public void initWebsocket() {
        try {
            URI uri = new URI("ws://192.168.99.99:8888");
            wsClient = new WsClient(uri);
            wsClient.connect();
        } catch (URISyntaxException e) {
            Log.v(TAG, "initWebsocket...URISyntaxException:" + e);
        }
    }
	上面的代码初始化并连接websocket。
	
	public void handleHello() {
        JSONObject sendObj = new JSONObject();
        try {
            sendObj.put("Type", CMD_HELLO);
			JSONObject msgObj = new JSONObject();
            msgObj.put("roomId", roomId);
			sendObj.put("msg", msgObj);
            if (wsClient != null) {
                boolean isOpen = wsClient.getConnection().isOpen();
                if (isOpen) {
                    wsClient.send(sendObj.toString());
                } else {
                    reconnect();
                }
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }
	
	public void reconnect() {
        if (reconTimes.getAndIncrement() >= MAX_RECONNECT_TIMES) {
            Log.v(TAG, "reconnect...reached max times");
            return;
        }
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.schedule(()-> {
            try {
                URI uri = new URI("ws://...");
                wsClient = new WSClient(uri);
                wsClient.connect();
            } catch (Exception e) {
                Log.e(TAG, "reconnect...reconnect failed:" + e);
                reconnect();
            }
        }, RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
    }

3,下面分析下okhttp下的websocket应用。
在build.gradle下

implementation(libs.okhttp3)
okhttp3 = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "4.12.0" }

在一个java类文件中

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;

public class WsCtrl {
	private OkHttpClient client = new OkHttpClient();
    private AtomicInteger cmdId = new AtomicInteger(1);
    private WebSocket webSocket;
	
	WebSocketListener webSocketListener = new WebSocketListener() {
        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.v(TAG, "webSocketListener,onClosed...webSocket:" + webSocket + ",code:" + code + ",reason:" + reason);
            webSocket.close(code, reason);
            wsOpened = false;
        }

        @Override
        public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosing(webSocket, code, reason);
            Log.v(TAG, "webSocketListener,onClosing...webSocket:" + webSocket + ",code:" + code + ",reason:" + reason);
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
            super.onFailure(webSocket, t, response);
            Log.v(TAG, "webSocketListener,onFailure...webSocket:" + webSocket + ",response:" + response);
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
            super.onMessage(webSocket, text);
            Log.v(TAG, "webSocketListener,onMessage...webSocket:" + webSocket + ",text:" + text);
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
            super.onMessage(webSocket, bytes);
            Log.v(TAG, "webSocketListener,onMessage...webSocket:" + webSocket + ",bytes:" + bytes);
        }

        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.v(TAG, "webSocketListener,onOpen...webSocket:" + webSocket + ",response:" + response);
            wsOpened = true;
            JSONObject authObj = buildAuthData();
            Log.v(TAG, "webSocketListener,onOpen...send auth");
            webSocket.send(authObj.toString());


        }
    };
	
	public void initWsSocket() {
        Request request = new Request.Builder().url(WS_URI).build();
        webSocket = client.newWebSocket(request, webSocketListener);
    }
	
	private JSONObject buildAuthData() {
        JSONObject authObj = new JSONObject();
        try {
            authObj.put("type", "auth");
            authObj.put("token", TOKEN);
        } catch (JSONException e) {
            Log.e(TAG, "buildAuthData,e:" + e);
        }
        return authObj;
    }
	
	private int getIncrementInteger() {
        cmdId.incrementAndGet();
        return cmdId.get();
    }

	private JSONObject buildSendObj() {
        JSONObject sendObj = new JSONObject();
        try {
            sendObj.put("id", getIncrementInteger());
            sendObj.put("type", "type");
            JSONObject dataObj = new JSONObject();
            dataObj.put("otherid", otherId);
            sendObj.put("data", dataObj);
        } catch (JSONException jse) {
            Log.e(TAG, "sendHaAuthObj,jse:" + jse);
        }
        return sendObj;
    }
	
	private void sendDataCtrl() {
        if (webSocket == null) {
            return;
        }
        JSONObject obj = buildSendObj();
        webSocket.send(obj.toString());
    }