Android WebSocket

既生HTTP何生WebSockt

与服务端交互的协议中,HTTP只能被动接收客户端请求,然后响应。而WebSocket在客户端与服务端建立连接以后,服务端可以主动发消息给客户端。这就是WebSocket的特点。

应用

因为服务端可以主动发消息的特性,WebSocket在以下场景使用非常多:
1、推送
2、聊天室
3、开发工具
4…

大部分功能依赖于产品的业务,投入使用可能比较困难,而开发工具是开发人员可以肆意发挥的地方!

ReactNative中WebSocket的使用

ReactNative开发过程中,pc端可以控制android端重新加载、打印堆栈等等。这些的实现都依赖于WebSocket。

com.facebook.react.packagerconnection.ReconnectingWebSocket实现了WebSocket的连接,使用了okhttp。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
final public class ReconnectingWebSocket extends WebSocketListener {
public ReconnectingWebSocket(
String url,
MessageCallback messageCallback,
ConnectionCallback connectionCallback) {
super();
mUrl = url;
mMessageCallback = messageCallback;
mConnectionCallback = connectionCallback;
mHandler = new Handler(Looper.getMainLooper());
}

public void connect() {
if (mClosed) {
throw new IllegalStateException("Can't connect closed client");
}

OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read
.build();

Request request = new Request.Builder().url(mUrl).build();
httpClient.newWebSocket(request, this);
}
}

JSPackagerClient包装了ReconnectingWebSocket,在onMessage回调处分发服务端下发的命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
final public class JSPackagerClient implements ReconnectingWebSocket.MessageCallback {
private ReconnectingWebSocket mWebSocket;

public JSPackagerClient(
String clientId, PackagerConnectionSettings settings,
Map<String, RequestHandler> requestHandlers,
@Nullable ReconnectingWebSocket.ConnectionCallback connectionCallback) {
super();

Uri.Builder builder = new Uri.Builder();
builder.scheme("ws")
.encodedAuthority(settings.getDebugServerHost())
.appendPath("message")
.appendQueryParameter("device", AndroidInfoHelpers.getFriendlyDeviceName())
.appendQueryParameter("app", settings.getPackageName())
.appendQueryParameter("clientid", clientId);
String url = builder.build().toString();

mWebSocket = new ReconnectingWebSocket(url, this, connectionCallback);
mRequestHandlers = requestHandlers;
}

@Override
public void onMessage(String text) {
try {
JSONObject message = new JSONObject(text);

int version = message.optInt("version");
String method = message.optString("method");
Object id = message.opt("id");
Object params = message.opt("params");

if (version != PROTOCOL_VERSION) {
FLog.e(
TAG,
"Message with incompatible or missing version of protocol received: " + version);
return;
}

if (method == null) {
abortOnMessage(id, "No method provided");
return;
}

RequestHandler handler = mRequestHandlers.get(method);
if (handler == null) {
abortOnMessage(id, "No request handler for method: " + method);
return;
}

if (id == null) {
handler.onNotification(params);
} else {
handler.onRequest(params, new ResponderImpl(id));
}
} catch (Exception e) {
FLog.e(TAG, "Handling the message failed", e);
}
}

}

命令的具体执行在RequestHandler。注入RequestHandler的时机在DevServerHelper#openPackagerConnection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public void openPackagerConnection(
final String clientId, final PackagerCommandListener commandListener) {
if (mPackagerClient != null) {
FLog.w(ReactConstants.TAG, "Packager connection already open, nooping.");
return;
}
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... backgroundParams) {
Map<String, RequestHandler> handlers = new HashMap<>();
handlers.put("reload", new NotificationOnlyHandler() {
@Override
public void onNotification(@Nullable Object params) {
commandListener.onPackagerReloadCommand();
}
});
handlers.put("devMenu", new NotificationOnlyHandler() {
@Override
public void onNotification(@Nullable Object params) {
commandListener.onPackagerDevMenuCommand();
}
});
handlers.put("captureHeap", new RequestOnlyHandler() {
@Override
public void onRequest(@Nullable Object params, Responder responder) {
commandListener.onCaptureHeapCommand(responder);
}
});
handlers.putAll(new FileIoHandler().handlers());

ConnectionCallback onPackagerConnectedCallback =
new ConnectionCallback() {
@Override
public void onConnected() {
commandListener.onPackagerConnected();
}

@Override
public void onDisconnected() {
commandListener.onPackagerDisconnected();
}
};

mPackagerClient = new JSPackagerClient(
clientId,
mSettings.getPackagerConnectionSettings(),
handlers,
onPackagerConnectedCallback);
mPackagerClient.init();

return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

Android客户端实现一个WebSocket Server

大部分使用WebSocket的开发工具,服务端都是pc,Android端都是一个client。经过okhttp的封装,实现client非常简单。

github上找到了两个java websocket server的库,Java-WebSocketAndroidAsync。尝试写了一下Demo,最终只跑通了AndroidAsync,github:https://github.com/PortgasAce/AndroidWebSocketDemo

Server的主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
AsyncHttpClient.getDefaultInstance().websocket(get, "my-protocol", new WebSocketConnectCallback() {
@Override
public void onCompleted(Exception ex, WebSocket webSocket) {
if (ex != null) {
ex.printStackTrace();
return;
}
webSocket.send("a string");
// webSocket.send(new byte[10]);
webSocket.setStringCallback(new StringCallback() {
public void onStringAvailable(String s) {
System.out.println("I got a string: " + s);
}
});
webSocket.setDataCallback(new DataCallback() {
public void onDataAvailable(DataEmitter emitter, ByteBufferList byteBufferList) {
System.out.println("I got some bytes!");
// note that this data has been read
byteBufferList.recycle();
}
});
}
});

上面是github的readme,注释了一行webSocket.send(new byte[10]);,否则websocket连接会断掉。

参考

知乎WebSocket原理:https://www.zhihu.com/question/20215561

阮一峰WebSocket教程:http://www.ruanyifeng.com/blog/2017/05/websocket.html

Java-WebSocket:https://github.com/TooTallNate/Java-WebSocket

AndroidAsync:https://github.com/koush/AndroidAsync