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());
}