自分で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を実現します.
    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);
                }
            };
    

    ピットポイント:
  • natタイムアウト、pingフレームの心拍間隔は一定の正確な値
  • ではない.
  • チャネルを新規作成するたびに、アクティブ/パッシブに元のチャネルを閉じ、サービス側圧力
  • をタイムリーに減少しなければならない.
  • が再開するタイミングで、短時間での繰り返しによる内部ddos
  • の発生を防止する.
  • メッセージロス問題、バックエンドデータ状態に同期するポリシー
  • 次の内容:
  • 送信データのタイムアウト検出機構
  • 断線再接続後、データの自動再送機能設計