自分でIMフレームワークを作る(一)
9517 ワード
IM機能はソーシャルappにおいて重要な地位を果たしているが、appの核心機能ではない場合が多いため、多くのプロジェクトのIM機能は第三者にソリューションとしてアクセスされている. IM通信の核心機能は、サービス側との信頼性の高い長接続チャネル を維持する必要がある.
一般的な接続方法: xmpp socket長接続 三方sdk(融雲、環信) websocket
Websocketは天然にハートビートプロトコル(トラフィックが小さい)をサポートしているため、端末間(HTML 5はサポートを持参し、既存のwebエンドソリューションは、一部の会社の大フロントエンド戦略に合致している)、オープンソースサポートは十分であるなどの利点があり、websocketを採用することを提案しています.
Websocketプロトコル実装 java-websocket okhttp 自己実現rfc 6455 okhttpの流行度を考慮して、okhttpを採用してWebSocketのClientを実現します.
Websocket状態の変化と断線再接続を同時に処理しnatタイムアウト
ピットポイント: natタイムアウト、pingフレームの心拍間隔は一定の正確な値 ではない.チャネルを新規作成するたびに、アクティブ/パッシブに元のチャネルを閉じ、サービス側圧力 をタイムリーに減少しなければならない.が再開するタイミングで、短時間での繰り返しによる内部ddos の発生を防止する.メッセージロス問題、バックエンドデータ状態に同期するポリシー 次の内容:送信データのタイムアウト検出機構 断線再接続後、データの自動再送機能設計
一般的な接続方法:
Websocketは天然にハートビートプロトコル(トラフィックが小さい)をサポートしているため、端末間(HTML 5はサポートを持参し、既存のwebエンドソリューションは、一部の会社の大フロントエンド戦略に合致している)、オープンソースサポートは十分であるなどの利点があり、websocketを採用することを提案しています.
Websocketプロトコル実装
private void initialization() {
if (TextUtils.isEmpty(url)) {
throw new AssertionError("Please initialize url first");
}
if (webSocket == null) {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(WSConstant.OKHTTP_CLIENT_READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WSConstant.OKHTTP_CLIENT_WRITE_TIMEOUT, TimeUnit.SECONDS)
.connectTimeout(WSConstant.OKHTTP_CLIENT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.pingInterval(30 * 1000, TimeUnit.MILLISECONDS) //ping
.build();
Request request = new Request.Builder()
.url(url)
.build();
webSocket = okHttpClient.newWebSocket(request, webSocketListener);
}
}
Websocket状態の変化と断線再接続を同時に処理しnatタイムアウト
mWebSocketListener = new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
// client
IncomingHandler.postDelayed(new WebSocketCheckScanner(), WebSocketCheckScanner.SCANNER_DELAY);
//
shutDownReConnectRunner();
//
socketStatus = STATUS_OPEN;
//
sendWebSocketStatusMessage(SERVICE_SOCKET_OPEN);
}
@Override
public void onMessage(WebSocket webSocket, String text) {
// ,
socketStatus = STATUS_OPEN;
L.i(TAG, "[onMessage]" + text);
//
dispatchResponse(text);
super.onMessage(webSocket, text);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes);
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
// ,
L.i(TAG, "onClosing:");
super.onClosing(webSocket, code, reason);
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
socketStatus = STATUS_CLOSED;
L.i(TAG, "onClosed:");
super.onClosed(webSocket, code, reason);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
// ,
if (isShutDown) {
//
sendWebSocketStatusMessage(SERVICE_SOCKET_BROKEN);
closeConnection();
L.i(TAG, "[ ]");
return;
}
// error java.io.EOFException , 10:28:21 ~ 10:41:51 15
//java.net.SocketException: Connection reset error
// : doze ping-pong , https://github.com/square/okhttp/issues/3722
// java.net.SocketException: Software caused connection abort
//java.net.ConnectException
//if (t instanceof SocketException || t instanceof UnknownHostException || t instanceof EOFException) {
L.i(TAG, "socket :" + IMUtils.isConnected(getApplication()));
switch (socketStatus) {
case STATUS_OPEN:
// ,
sendWebSocketStatusMessage(SERVICE_SOCKET_BROKEN);
initReConnectRunner();
break;
case STATUS_CONNECTTING:
//
if (scheduledExecutor == null) {
initReConnectRunner();
break;
} else {
//
if (retryTime >= ImConfig.RECONNECT_TIME) {
shutDownReConnectRunner();
}
break;
}
case STATUS_RECONNECTTING:
// ,
if (retryTime > ImConfig.RECONNECT_TIME) {
shutDownReConnectRunner();
}
break;
default:
break;
}
//} else {
//if (t instanceof EOFException)
//L.i(TAG, " ");
//}
t.printStackTrace();
super.onFailure(webSocket, t, response);
}
};
ピットポイント: