目的

学习websocket

基础知识

  1. 了解maven,能新建maven工程
  2. 熟悉java
  3. 了解html及javascript

pom依赖

<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>3.8.0</version>
</dependency>

Websocket server for pc

package com.carl.websocketdemo;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import okhttp3.Response;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;

public class WebsocketServer {
public final static MockWebServer mockWebServer = new MockWebServer();
public static boolean isConnected = false;
public static final ExecutorService writeExecutor = Executors.newSingleThreadExecutor();

public static void createWebsocket() {
mockWebServer.enqueue(new MockResponse().withWebSocketUpgrade(new WebSocketListener() {
WebSocket webSocketServer = null;

public void onClose(WebSocket webSocket, int code, String reason) {
System.out.println("server onClose");
createWebsocket();//如果服务端关闭,则重新建立websocket server
}

public void onFailure(WebSocket webSocket, Throwable t, Response response) {
System.out.println("server onFailure");
System.out.println("ioException:" + t.getMessage());
System.out.println("response:" + response);

isConnected = false;

this.onClose(webSocket, 1, t.getMessage());
}

public void onMessage(WebSocket webSocket, String text) {

System.out.println("onMessage body:" + text);
if ("start".equals(text)) {
//replay it
writeExecutor.execute(new Runnable() {

public void run() {
while(isConnected) {
try {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
webSocketServer.send(df.format(new Date()));
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
}

public void onOpen(WebSocket webSocket, Response res) {

this.webSocketServer = webSocket;

System.out.println("server onOpen");
isConnected = true;

}

}));
}

public static void main( String[] args ) {
createWebsocket();

try {
//System.out.println(mockWebServer.getPort());

mockWebServer.start(9990);
System.out.println(mockWebServer.getPort());
System.out.println(mockWebServer.getRequestCount());

} catch (IOException e) {
e.printStackTrace();
}
}
}

Websocket client for pc

package com.carl.websocketdemo;

import java.util.concurrent.TimeUnit;

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

public class WebsocketClient {

static String hostName = WebsocketServer.mockWebServer.getHostName();
static int port = 9991;
static WebSocket webSocketClient = null;
static int receiveTimeCount =0;

public static void main(String[] args) {
System.out.println("hostName:" + hostName);
System.out.println("port:" + port);

OkHttpClient client = new OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build();

//构造request对象
Request request = new Request.Builder().url("ws://" + hostName + ":" + port + "/").build();

//new 一个websocket调用对象并建立连接
client.newWebSocket(request, new WebSocketListener() {

public void onOpen(final WebSocket webSocket, Response response) {
//保存引用,用于后续操作
//webSocketClient = webSocket;
//打印一些内容
System.out.println("client onOpen");

//注意下面都是write线程回写给客户端
//建立连接成功后,发生command 1给服务器端
webSocket.send("start");

}

@Override
public void onMessage(WebSocket webSocket, String message) {
System.out.println("client onMessage " + receiveTimeCount + ", " + message);
receiveTimeCount++;
}

public void onClose(WebSocket webSocket, int code, String reason) {
System.out.println("client onClose");
System.out.println("code:" + code + " reason:" + reason);

System.exit(0);

}

public void onFailure(WebSocket webSocket, Throwable t, Response response) {
//发生错误时会回调到这
System.out.println("client onFailure");
System.out.println("throwable info is: " + t);

this.onClose(webSocket, 1, t.getMessage());
}
});
}
}

Websocket server for android

package com.sn.websocketdemo;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import okhttp3.Response;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;

public class WebsocketServer {
public MockWebServer mockWebServer = new MockWebServer();
public boolean isConnected = false;
public ExecutorService writeExecutor = Executors.newSingleThreadExecutor();

public void createWebsocket() {
mockWebServer.enqueue(new MockResponse().withWebSocketUpgrade(new WebSocketListener() {
WebSocket webSocketServer = null;

public void onClose(WebSocket webSocket, int code, String reason) {
System.out.println("server onClose, code is:" + code + ", reason is:" + reason);
createWebsocket();
}

public void onFailure(WebSocket webSocket, Throwable t, Response response) {
System.out.println("server onFailure");
System.out.println("ioException:" + t.getMessage());
System.out.println("response:" + response);

isConnected = false;

this.onClose(webSocket, 1, t.getMessage());
}

public void onMessage(WebSocket webSocket, String text) {

System.out.println("onMessage body:" + text);
if ("start".equals(text)) {
//replay it
writeExecutor.execute(new Runnable() {

public void run() {
while(isConnected) {
try {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
webSocketServer.send(df.format(new Date()));
Thread.sleep(1000);
connectTimeout = defaultTimeout;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
}

public void onOpen(WebSocket webSocket, Response res) {

this.webSocketServer = webSocket;

System.out.println("server onOpen");
isConnected = true;
//System.out.println("server request header:" + res.request().headers());
//System.out.println("server response header:" + res.headers());
//System.out.println("server response:" + res);

}

}));
}
}


package com.sn.websocketdemo;

import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Looper;

import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

public class WebsocketServerThread extends Thread { //在android中网络交互的动作需要放在一个独立的线程中,不能在主线程中进行网络交互
private int port = 9991;
private WebsocketServer ws;
private Looper looper;
boolean isRunning = false;

WebsocketServerThread(int port) {
this.port = port;

ws = new WebsocketServer();
ws.createWebsocket();
}

boolean isServerRunning() {
return isRunning;
}

@Override
public void run() {
// Looper.prepare();
// looper = Looper.myLooper();
startServer();
// Looper.loop();
}

public void startServer() {
// if (isServerRunning()) {
// return;
// }

try {
System.out.println("WebsocketServerTest stat");

String str = getHostIP();
String[] ipStr = str.split("\\.");
byte[] ipBuf = new byte[4];
for(int i = 0; i < 4; i++){
ipBuf[i] = (byte)(Integer.parseInt(ipStr[i])&0xff);
}

InetAddress inetAddress = InetAddress.getByAddress(ipBuf);

ws.mockWebServer.start(inetAddress, 9991);
isRunning = true;
System.out.println("WebsocketServerTest stat:" + ws.mockWebServer.getPort());
} catch (IOException e) {
e.printStackTrace();
}
}

public static String getHostIP() {

String hostIp = null;
try {
Enumeration nis = NetworkInterface.getNetworkInterfaces();
InetAddress ia = null;
while (nis.hasMoreElements()) {
NetworkInterface ni = (NetworkInterface) nis.nextElement();
Enumeration<InetAddress> ias = ni.getInetAddresses();
while (ias.hasMoreElements()) {
ia = ias.nextElement();
if (ia instanceof Inet6Address) {
continue;// skip ipv6
}
String ip = ia.getHostAddress();
if (!"127.0.0.1".equals(ip)) {
hostIp = ia.getHostAddress();
break;
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return hostIp;

}
}

Websocket client with javascript

<!DOCTYPE html>  
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript"type="text/javascript">

function createWebSocket() {
websocket = new WebSocket('ws://10.**.**.**:9991/');
console.log("websocket.readyState before onopen:" + websocket.readyState);

websocket.onopen = function() {
console.log("websocket.readyState:" + websocket.readyState);
websocket.send('start');
}

websocket.onclose = function(message) {
console.log('onclose', message);
//sleep(1000);
}

websocket.onerror = function(message) {
console.log('onerror', message.data);

}

websocket.onmessage = function(message) {
console.log(message.data);
document.getElementById("showTime").innerHTML = message.data;
}
}

function closeWebSocket() {
websocket.close();
}

window.onbeforeunload = function () {
closeWebSocket();
}

window.onload = function() {
//console.log("websocket.readyState onload:" + websocket.readyState);
createWebSocket();
};

</script>
<h2>WebSocket Test</h2>
<h2 id="showTime">show time</h2>
</html>

问题

  1. 参考appium源码中的uiautomatorserver代码,暂时不明白Looper的作用
  2. 打开本地的html页面后,能正常展示服务端的时间,但是在刷新页面的时候,会造成异常,暂未解决,本人想到的解决思路大概有两种: a. 在刷新页面之前,判断websocket连接状态,如果已存在连接则关闭,在页面刷新完成后重新创建连接 b. 在刷新页面后返回原来的连接

其他应用举例

stf中获取手机屏幕


↙↙↙阅读原文可查看相关链接,并与作者交流