使用AndroidSocketClient庫建立SSL安全連結

悟靜發表於2016-12-05

AndroidSocketClient

socket client server簡易封裝

Import

JitPack

Add it in your project`s build.gradle at the end of repositories:

repositories {
  // ...
  maven { url "https://jitpack.io" }
}

Step 2. Add the dependency in the form

dependencies {
  compile `com.github.vilyever:AndroidSocketClient:3.0.3`
}

Updates

  • 3.0.3 
    提升readByLength按長度讀取的速度 issue #26

  • 3.0.2 
    修復初次連線失敗時,因CountDownTimer導致崩潰的問題 issue #24

  • 3.0.1 
    修復包頭驗證bug,by zzdwuliang 
    增加地址檢測的詳細log 

  • 3.0.0 
    支援ReadToData和ReadToLength自動讀取以下兩種結構 
    常見包結構1:【包頭(可選)】【正文】【包尾】 
    常見包結構2:【包頭(可選)】【餘下包長度(正文加包尾長度)(此部分也可做包頭)(此部分長度固定)】【正文】【包尾(可選)】

Usage

app模組下包含簡單的使用demo

請一定設定讀取策略socketClient.getSocketPacketHelper().setReadStrategy();

遠端端連線資訊配置

    socketClient.getAddress().setRemoteIP(IPUtil.getLocalIPAddress(true)); // 遠端端IP地址
    socketClient.getAddress().setRemotePort("21998"); // 遠端端埠號
    socketClient.getAddress().setConnectionTimeout(15 * 1000); // 連線超時時長,單位毫秒

預設String編碼配置

    /**
     * 設定自動轉換String型別到byte[]型別的編碼
     * 如未設定(預設為null),將不能使用{@link SocketClient#sendString(String)}傳送訊息
     * 如設定為非null(如UTF-8),在接受訊息時會自動嘗試在接收執行緒(非主執行緒)將接收的byte[]資料依照編碼轉換為String,在{@link SocketResponsePacket#getMessage()}讀取
     */
    socketClient.setCharsetName(CharsetUtil.UTF_8); // 設定編碼為UTF-8

固定心跳包配置

    /**
     * 設定自動傳送的心跳包資訊
     */
    socketClient.getHeartBeatHelper().setDefaultSendData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8));

    /**
     * 設定遠端端傳送到本地的心跳包資訊內容,用於判斷接收到的資料包是否是心跳包
     * 通過{@link SocketResponsePacket#isHeartBeat()} 檢視資料包是否是心跳包
     */
    socketClient.getHeartBeatHelper().setDefaultReceiveData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8));

    socketClient.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 設定自動傳送心跳包的間隔時長,單位毫秒
    socketClient.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 設定允許自動傳送心跳包,此值預設為false

動態變化心跳包配置

    /**
     * 設定自動傳送的心跳包資訊
     * 此資訊動態生成
     *
     * 每次傳送心跳包時自動呼叫
     */
    socketClient.getHeartBeatHelper().setSendDataBuilder(new SocketHeartBeatHelper.SendDataBuilder() {
        @Override
        public byte[] obtainSendHeartBeatData(SocketHeartBeatHelper helper) {
            /**
             * 使用當前日期作為心跳包
             */
            byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F};
            byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F};

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
            byte[] heartBeatInfo = CharsetUtil.stringToData(sdf.format(new Date()), CharsetUtil.UTF_8);

            byte[] data = new byte[heartBeatPrefix.length + heartBeatSuffix.length + heartBeatInfo.length];
            System.arraycopy(heartBeatPrefix, 0, data, 0, heartBeatPrefix.length);
            System.arraycopy(heartBeatInfo, 0, data, heartBeatPrefix.length, heartBeatInfo.length);
            System.arraycopy(heartBeatSuffix, 0, data, heartBeatPrefix.length + heartBeatInfo.length, heartBeatSuffix.length);

            return data;
        }
    });

    /**
     * 設定遠端端傳送到本地的心跳包資訊的檢測器,用於判斷接收到的資料包是否是心跳包
     * 通過{@link SocketResponsePacket#isHeartBeat()} 檢視資料包是否是心跳包
     */
    socketClient.getHeartBeatHelper().setReceiveHeartBeatPacketChecker(new SocketHeartBeatHelper.ReceiveHeartBeatPacketChecker() {
        @Override
        public boolean isReceiveHeartBeatPacket(SocketHeartBeatHelper helper, SocketResponsePacket packet) {
            /**
             * 判斷資料包資訊是否含有指定的心跳包字首和字尾
             */
            byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F};
            byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F};

            if (Arrays.equals(heartBeatPrefix, Arrays.copyOfRange(packet.getData(), 0, heartBeatPrefix.length))
                    && Arrays.equals(heartBeatSuffix, Arrays.copyOfRange(packet.getData(), packet.getData().length - heartBeatSuffix.length, packet.getData().length))) {
                return true;
            }

            return false;
        }
    });

    socketClient.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 設定自動傳送心跳包的間隔時長,單位毫秒
    socketClient.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 設定允許自動傳送心跳包,此值預設為false

自動按包尾分割資訊讀取資料的傳送配置

    /**
     * 根據連線雙方協議設定自動傳送的包尾資料
     * 每次傳送資料包(包括心跳包)都會在傳送包內容後自動傳送此包尾
     *
     * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步驟為
     * 1. socketClient向遠端端傳送包頭(如果設定了包頭資訊)
     * 2. socketClient向遠端端傳送正文資料{0x01, 0x02}
     * 3. socketClient向遠端端傳送包尾
     *
     * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必須設定此項
     * 用於分隔多條訊息
     */
    socketClient.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10});

    /**
     * 根據連線雙方協議設定自動傳送的包頭資料
     * 每次傳送資料包(包括心跳包)都會在傳送包內容前自動傳送此包頭
     *
     * 若無需包頭可刪除此行
     */
    socketClient.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8));

    /**
     * 設定分段傳送資料長度
     * 即在傳送指定長度後通過 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回撥當前傳送進度
     *
     * 若無需進度回撥可刪除此二行,刪除後仍有【傳送開始】【傳送結束】的回撥
     */
    socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 設定傳送分段長度,單位byte
    socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 設定允許使用分段傳送,此值預設為false

    /**
     * 設定傳送超時時長
     * 在傳送每個資料包時,傳送每段資料的最長時間,超過後自動斷開socket連線
     * 通過設定分段傳送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免傳送大資料包時因超時斷開,
     *
     * 若無需限制傳送時長可刪除此二行
     */
    socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 設定傳送超時時長,單位毫秒
    socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 設定允許使用傳送超時時長,此值預設為false

自動按包尾分割資訊讀取資料的接收配置

    /**
     * 設定讀取策略為自動讀取到指定的包尾
     */
    socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadToTrailer);

    /**
     * 根據連線雙方協議設定的包尾資料
     * 每次接收資料包(包括心跳包)都會在檢測接收到與包尾資料相同的byte[]時回撥一個資料包
     *
     * 例:自動接收遠端端所傳送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}為將要接收的資料】的步驟為
     * 1. socketClient接收包頭(如果設定了包頭資訊)(接收方式為一直讀取到與包頭相同的byte[],即可能過濾掉包頭前的多餘資訊)
     * 2. socketClient接收正文和包尾(接收方式為一直讀取到與尾相同的byte[])
     * 3. socketClient回撥資料包
     *
     * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必須設定此項
     * 用於分隔多條訊息
     */
    socketClient.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10});

    /**
     * 根據連線雙方協議設定的包頭資料
     * 每次接收資料包(包括心跳包)都會先接收此包頭
     *
     * 若無需包頭可刪除此行
     */
    socketClient.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8));

    /**
     * 設定接收超時時長
     * 在指定時長內沒有資料到達本地自動斷開
     *
     * 若無需限制接收時長可刪除此二行
     */
    socketClient.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 設定接收超時時長,單位毫秒
    socketClient.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 設定允許使用接收超時時長,此值預設為false

自動按包長度資訊讀取的傳送配置

    /**
     * 設定包長度轉換器
     * 即每次傳送資料時,將包頭以外的資料長度轉換為特定的byte[]傳送到遠端端用於解析還需要讀取多少長度的資料
     *
     * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步驟為
     * 1. socketClient向遠端端傳送包頭(如果設定了包頭資訊)
     * 2. socketClient要傳送的資料為{0x01, 0x02},長度為2(若設定了包尾,還需加上包尾的位元組長度),通過此轉換器將int型別的2轉換為4位元組的byte[],遠端端也照此演算法將4位元組的byte[]轉換為int值
     * 3. socketClient向遠端端傳送轉換後的長度資訊byte[]
     * 4. socketClient向遠端端傳送正文資料{0x01, 0x02}
     * 5. socketClient向遠端端傳送包尾(如果設定了包尾資訊)
     *
     * 此轉換器用於第二步
     *
     * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必須設定此項
     * 用於分隔多條訊息
     */
    socketClient.getSocketPacketHelper().setSendPacketLengthDataConvertor(new SocketPacketHelper.SendPacketLengthDataConvertor() {
        @Override
        public byte[] obtainSendPacketLengthDataForPacketLength(SocketPacketHelper helper, int packetLength) {
            /**
             * 簡單將int轉換為byte[]
             */
            byte[] data = new byte[4];
            data[3] = (byte) (packetLength & 0xFF);
            data[2] = (byte) ((packetLength >> 8) & 0xFF);
            data[1] = (byte) ((packetLength >> 16) & 0xFF);
            data[0] = (byte) ((packetLength >> 24) & 0xFF);
            return data;
        }
    });

    /**
     * 根據連線雙方協議設定自動傳送的包頭資料
     * 每次傳送資料包(包括心跳包)都會在傳送包內容前自動傳送此包頭
     *
     * 若無需包頭可刪除此行
     */
    socketClient.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8));

    /**
     * 根據連線雙方協議設定自動傳送的包尾資料
     * 每次傳送資料包(包括心跳包)都會在傳送包內容後自動傳送此包尾
     *
     * 若無需包尾可刪除此行
     * 注意:
     * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}時不依賴包尾讀取資料
     */
    socketClient.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10});

    /**
     * 設定分段傳送資料長度
     * 即在傳送指定長度後通過 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回撥當前傳送進度
     *
     * 若無需進度回撥可刪除此二行,刪除後仍有【傳送開始】【傳送結束】的回撥
     */
    socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 設定傳送分段長度,單位byte
    socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 設定允許使用分段傳送,此值預設為false

    /**
     * 設定傳送超時時長
     * 在傳送每個資料包時,傳送每段資料的最長時間,超過後自動斷開socket連線
     * 通過設定分段傳送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免傳送大資料包時因超時斷開,
     *
     * 若無需限制傳送時長可刪除此二行
     */
    socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 設定傳送超時時長,單位毫秒
    socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 設定允許使用傳送超時時長,此值預設為false

自動按包長度資訊讀取的接收配置

    /**
     * 設定讀取策略為自動讀取指定長度
     */
    socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadByLength);

    /**
     * 設定包長度轉換器
     * 即每次接收資料時,將遠端端傳送到本地的長度資訊byte[]轉換為int,然後讀取相應長度的值
     *
     * 例:自動接收遠端端所傳送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}為將要接收的資料】的步驟為
     * 1. socketClient接收包頭(如果設定了包頭資訊)(接收方式為一直讀取到與包頭相同的byte[],即可能過濾掉包頭前的多餘資訊)
     * 2. socketClient接收長度為{@link SocketPacketHelper#getReceivePacketLengthDataLength()}(此處設定為4)的byte[],通過下面設定的轉換器,將byte[]轉換為int值,此int值暫時稱為X
     * 3. socketClient接收長度為X的byte[]
     * 4. socketClient接收包尾(如果設定了包尾資訊)(接收方式為一直讀取到與包尾相同的byte[],如無意外情況,此處不會讀取到多餘的資訊)
     * 5. socketClient回撥資料包
     *
     * 此轉換器用於第二步
     *
     * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必須設定此項
     * 用於分隔多條訊息
     */
    socketClient.getSocketPacketHelper().setReceivePacketLengthDataLength(4);
    socketClient.getSocketPacketHelper().setReceivePacketDataLengthConvertor(new SocketPacketHelper.ReceivePacketDataLengthConvertor() {
        @Override
        public int obtainReceivePacketDataLength(SocketPacketHelper helper, byte[] packetLengthData) {
            /**
             * 簡單將byte[]轉換為int
             */
            int length =  (packetLengthData[3] & 0xFF) + ((packetLengthData[2] & 0xFF) << 8) + ((packetLengthData[1] & 0xFF) << 16) + ((packetLengthData[0] & 0xFF) << 24);

            return length;
        }
    });

    /**
     * 根據連線雙方協議設定的包頭資料
     * 每次接收資料包(包括心跳包)都會先接收此包頭
     *
     * 若無需包頭可刪除此行
     */
    socketClient.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8));

    /**
     * 根據連線雙方協議設定的包尾資料
     *
     * 若無需包尾可刪除此行
     * 注意:
     * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}時不依賴包尾讀取資料
     */
    socketClient.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10});

    /**
     * 設定接收超時時長
     * 在指定時長內沒有資料到達本地自動斷開
     *
     * 若無需限制接收時長可刪除此二行
     */
    socketClient.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 設定接收超時時長,單位毫秒
    socketClient.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 設定允許使用接收超時時長,此值預設為false

手動讀取的傳送配置

    /**
     * 設定分段傳送資料長度
     * 即在傳送指定長度後通過 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回撥當前傳送進度
     *
     * 若無需進度回撥可刪除此二行,刪除後仍有【傳送開始】【傳送結束】的回撥
     */
    socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 設定傳送分段長度,單位byte
    socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 設定允許使用分段傳送,此值預設為false

    /**
     * 設定傳送超時時長
     * 在傳送每個資料包時,傳送每段資料的最長時間,超過後自動斷開socket連線
     * 通過設定分段傳送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免傳送大資料包時因超時斷開,
     *
     * 若無需限制傳送時長可刪除此二行
     */
    socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 設定傳送超時時長,單位毫秒
    socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 設定允許使用傳送超時時長,此值預設為false

手動讀取的接收配置

    /**
     * 設定讀取策略為手動讀取
     * 手動讀取有兩種方法
     * 1. {@link SocketClient#readDataToData(byte[], boolean)} )} 讀取到與指定位元組相同的位元組序列後回撥資料包
     * 2. {@link SocketClient#readDataToLength(int)} 讀取指定長度的位元組後回撥資料包
     *
     * 此時SocketPacketHelper中其他讀取相關設定將會無效化
     */
    socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.Manually);

常用回撥配置

    // 對應removeSocketClientDelegate
    socketClient.registerSocketClientDelegate(new SocketClientDelegate() {
        /**
         * 連線上遠端端時的回撥
         */
        @Override
        public void onConnected(SocketClient client) {
             SocketPacket packet = socketClient.sendData(new byte[]{0x02}); // 傳送訊息
             packet = socketClient.sendString("sy hi!"); // 傳送訊息

             socketClient.cancelSend(packet); // 取消傳送,若在等待傳送佇列中則從佇列中移除,若正在傳送則無法取消
        }

        /**
         * 與遠端端斷開連線時的回撥
         */
        @Override
        public void onDisconnected(SocketClient client) {
            // 可在此實現自動重連
            socketClient.connect();
        }

        /**
         * 接收到資料包時的回撥
         */
        @Override
        public void onResponse(final SocketClient client, @NonNull SocketResponsePacket responsePacket) {
            byte[] data = responsePacket.getData(); // 獲取接收的byte陣列,不為null
            String message = responsePacket.getMessage(); // 獲取按預設設定的編碼轉化的String,可能為null
        }
    });

傳送狀態回撥配置

    // 對應removeSocketClientSendingDelegate
    socketClient.registerSocketClientSendingDelegate(new SocketClientSendingDelegate() {
        /**
         * 資料包開始傳送時的回撥
         */
        @Override
        public void onSendPacketBegin(SocketClient client, SocketPacket packet) {
        }

        /**
         * 資料包取消傳送時的回撥
         * 取消傳送回撥有以下情況:
         * 1. 手動cancel仍在排隊,還未傳送過的packet
         * 2. 斷開連線時,正在傳送的packet和所有在排隊的packet都會被取消
         */
        @Override
        public void onSendPacketCancel(SocketClient client, SocketPacket packet) {
        }

        /**
         * 資料包傳送的進度回撥
         * progress值為[0.0f, 1.0f]
         * 通常配合分段傳送使用
         * 可用於顯示檔案等大資料的傳送進度
         */
        @Override
        public void onSendingPacketInProgress(SocketClient client, SocketPacket packet, float progress, int sendedLength) {
        }

        /**
         * 資料包完成傳送時的回撥
         */
        @Override
        public void onSendPacketEnd(SocketClient client, SocketPacket packet) {
        }
    });

接收狀態回撥配置

    // 對應removeSocketClientReceiveDelegate
    socketClient.registerSocketClientReceiveDelegate(new SocketClientReceivingDelegate() {
        /**
         * 開始接受一個新的資料包時的回撥
         */
        @Override
        public void onReceivePacketBegin(SocketClient client, SocketResponsePacket packet) {
        }

        /**
         * 完成接受一個新的資料包時的回撥
         */
        @Override
        public void onReceivePacketEnd(SocketClient client, SocketResponsePacket packet) {
        }

        /**
         * 取消接受一個新的資料包時的回撥
         * 在斷開連線時會觸發
         */
        @Override
        public void onReceivePacketCancel(SocketClient client, SocketResponsePacket packet) {
        }

        /**
         * 接受一個新的資料包的進度回撥
         * progress值為[0.0f, 1.0f]
         * 僅作用於ReadStrategy為AutoReadByLength的自動讀取
         * 因AutoReadByLength可以首先接受到剩下的資料包長度
         */
        @Override
        public void onReceivingPacketInProgress(SocketClient client, SocketResponsePacket packet, float progress, int receivedLength) {
        }
    });

License

Apache License Version 2.0


相關文章