OkSocket與Android的簡(jiǎn)單使用

一個(gè)Android輕量級(jí)Socket通訊框架,既OkHttp后又一力作.
框架開(kāi)源地址及Demo演示: https://github.com/xuuhaoo/OkSocket
歡迎star,fork,Issue交流


OkSocket簡(jiǎn)介

  • OkSocket是一款基于阻塞式傳統(tǒng)Socket的一款Socket客戶端整體解決方案.你可以使用它進(jìn)行基于Tcp協(xié)議的Socket通訊.就是我們所說(shuō)的長(zhǎng)連接.
  • 對(duì)通訊協(xié)議幾乎無(wú)限制,可以使用PB,可以使用JSON,可以使用XML.只要可以序列化成Byte數(shù)組的對(duì)象都可以傳輸.
  • 兼容所有語(yǔ)言寫(xiě)的Socket服務(wù)端,解決了Tcp通訊中頭疼的粘包拆包問(wèn)題,斷線重連問(wèn)題,心跳保持問(wèn)題,分片發(fā)送,重定向連接等問(wèn)題.
  • 針對(duì) 手機(jī) < - > 服務(wù)器 , 手機(jī)< - > 手機(jī) 間都可以進(jìn)行tcp通訊,手機(jī)間通訊俗稱(chēng)點(diǎn)對(duì)點(diǎn)通訊,可以很好的支持.
  • OkSocket還支持單工和全雙工通訊.適配各種復(fù)雜業(yè)務(wù)場(chǎng)景.分為 客戶端(OkSocketClient) 服務(wù)端(OkSocketServer)具體的繼承和依賴(lài)方法在下面.
  • 如果需要看demo程序,可以去https://github.com/xuuhaoo/OkSocket地址進(jìn)行clone.之后直接run起來(lái)就可以了.Demo會(huì)自動(dòng)和OkSocket編寫(xiě)的EchoServer進(jìn)行連接通訊,讓使用者更好地了解使用方法
  • OkSocket旨在讓更多不熟悉socket和tcp協(xié)議的朋友可以專(zhuān)注于業(yè)務(wù)開(kāi)發(fā)而不是底層協(xié)議的開(kāi)發(fā)和學(xué)習(xí).

Demo程序截圖

  • 1.簡(jiǎn)單的調(diào)用示例(建立連接,斷開(kāi)連接,發(fā)送數(shù)據(jù),接收數(shù)據(jù))
  • 2.復(fù)雜的調(diào)用示例(簡(jiǎn)單示例中的內(nèi)容,斷線重連,心跳,重定向)
  • 3.OkServer的Android端使用(可以進(jìn)行點(diǎn)對(duì)點(diǎn)通訊,當(dāng)然也可以部署在云端服務(wù)器用作SocketServer)


    首頁(yè)
    簡(jiǎn)單示例
    復(fù)雜示例

Maven配置

  • OkSocket 支持 JCenter 倉(cāng)庫(kù)
allprojects {
    repositories {
        jcenter()
    }
}
  • 在Module的build.gradle文件中添加依賴(lài)配置
dependencies {
    //基礎(chǔ)的 OkSocket 功能集成包.您的Socket開(kāi)發(fā)無(wú)論是客戶端還是Java,都需要此包 (必須集成)
    api 'com.tonystark.android:socket:4.1.0'
    //如果您需要使用 OkSocketServer 功能在客戶端或者Java程序,您還需要依賴(lài)下面的Server插件包和上面的一起依賴(lài).
    api 'com.tonystark.android:socket-server:4.1.0'
}

如果您是 Android 請(qǐng)關(guān)注下權(quán)限

  • 在AndroidManifest.xml中添加權(quán)限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

混淆配置

  • 請(qǐng)避免混淆OkSocket,在Proguard混淆文件中增加以下配置:
-dontwarn com.xuhao.didi.socket.client.**
-dontwarn com.xuhao.didi.socket.common.**
-dontwarn com.xuhao.didi.socket.server.**
-dontwarn com.xuhao.didi.core.**

-keep class com.xuhao.didi.socket.client.** { *; }
-keep class com.xuhao.didi.socket.common.** { *; }
-keep class com.xuhao.didi.socket.server.** { *; }
-keep class com.xuhao.didi.core.** { *; }

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
-keep class com.xuhao.didi.socket.client.sdk.client.OkSocketOptions$* {
    *;
}
-keep class com.xuhao.didi.socket.server.impl.OkServerOptions$* {
    *;
}

回聲服務(wù)器

  • 該回聲服務(wù)器是專(zhuān)門(mén)為初學(xué)者調(diào)試 OkSocket 庫(kù)部屬的一臺(tái)服務(wù)器,初學(xué)者可以將項(xiàng)目中的 app 安裝到手機(jī)上,點(diǎn)擊 Connect 按鈕即可,該服務(wù)器僅為熟悉通訊方式和解析方式使用.不能作為商用服務(wù)器. 不用時(shí)應(yīng)及時(shí)斷開(kāi),保證有限的資源最大化利用

公網(wǎng)IP:104.238.184.237
公網(wǎng)Port:8080

開(kāi)始一個(gè)簡(jiǎn)單的長(zhǎng)連接

  • OkSocket 會(huì)默認(rèn)對(duì)每一個(gè) Open 的新通道做緩存管理,僅在第一次調(diào)用 Open 方法時(shí)創(chuàng)建 ConnectionManager 管理器,之后調(diào)用者可以通過(guò)獲取到該ConnectionManager的引用,繼續(xù)調(diào)用相關(guān)方法
  • ConnectionManager 主要負(fù)責(zé)該地址的套接字連接斷開(kāi)發(fā)送消息等操作.
//連接參數(shù)設(shè)置(IP,端口號(hào)),這也是一個(gè)連接的唯一標(biāo)識(shí),不同連接,該參數(shù)中的兩個(gè)值至少有其一不一樣
ConnectionInfo info = new ConnectionInfo("104.238.184.237", 8080);
//調(diào)用OkSocket,開(kāi)啟這次連接的通道,調(diào)用通道的連接方法進(jìn)行連接.
OkSocket.open(info).connect();

有回調(diào)的長(zhǎng)連接

  • 注冊(cè)該通道的監(jiān)聽(tīng)器,每個(gè) Connection 通道中的監(jiān)聽(tīng)器互相隔離,因此如果一個(gè)項(xiàng)目連接了多個(gè) Socket 連接需要在每個(gè) Connection 注冊(cè)自己的連接監(jiān)聽(tīng)器,連接監(jiān)聽(tīng)器是該 OkSocket 與用戶交互的唯一途徑
//連接參數(shù)設(shè)置(IP,端口號(hào)),這也是一個(gè)連接的唯一標(biāo)識(shí),不同連接,該參數(shù)中的兩個(gè)值至少有其一不一樣
ConnectionInfo info = new ConnectionInfo("104.238.184.237", 8080);
//調(diào)用OkSocket,開(kāi)啟這次連接的通道,拿到通道Manager
IConnectionManager manager = OkSocket.open(info);
//注冊(cè)Socket行為監(jiān)聽(tīng)器,SocketActionAdapter是回調(diào)的Simple類(lèi),其他回調(diào)方法請(qǐng)參閱類(lèi)文檔
manager.registerReceiver(new SocketActionAdapter(){
    @Override
    public void onSocketConnectionSuccess(ConnectionInfo info, String action) {
     Toast.makeText(context, "連接成功", LENGTH_SHORT).show();
    }
});
//調(diào)用通道進(jìn)行連接
manager.connect();

可配置的長(zhǎng)連接

  • 獲得 OkSocketOptions 的行為屬于比較高級(jí)的 OkSocket 調(diào)用方法,每個(gè) Connection 將會(huì)對(duì)應(yīng)一個(gè) OkSocketOptions,如果第一次調(diào)用 Open 時(shí)未指定 OkSocketOptions,OkSocket將會(huì)使用默認(rèn)的配置對(duì)象,默認(rèn)配置請(qǐng)見(jiàn)文檔下方的高級(jí)調(diào)用說(shuō)明
//連接參數(shù)設(shè)置(IP,端口號(hào)),這也是一個(gè)連接的唯一標(biāo)識(shí),不同連接,該參數(shù)中的兩個(gè)值至少有其一不一樣
ConnectionInfo info = new ConnectionInfo("104.238.184.237", 8080);
//調(diào)用OkSocket,開(kāi)啟這次連接的通道,拿到通道Manager
IConnectionManager manager = OkSocket.open(info);
//獲得當(dāng)前連接通道的參配對(duì)象
OkSocketOptions options= manager.getOption();
//基于當(dāng)前參配對(duì)象構(gòu)建一個(gè)參配建造者類(lèi)
OkSocketOptions.Builder builder = new OkSocketOptions.Builder(options);
//修改參配設(shè)置(其他參配請(qǐng)參閱類(lèi)文檔)
builder.setSinglePackageBytes(size);
//建造一個(gè)新的參配對(duì)象并且付給通道
manager.option(builder.build());
//調(diào)用通道進(jìn)行連接
manager.connect();

如何發(fā)送數(shù)據(jù)

//類(lèi)A:
//...定義將要發(fā)送的數(shù)據(jù)結(jié)構(gòu)體...
public class TestSendData implements ISendable {
    private String str = "";

    public TestSendData() {
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("cmd", 14);
            jsonObject.put("data", "{x:2,y:1}");
            str = jsonObject.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    @Override
    public byte[] parse() {
        //根據(jù)服務(wù)器的解析規(guī)則,構(gòu)建byte數(shù)組
        byte[] body = str.getBytes(Charset.defaultCharset());
        ByteBuffer bb = ByteBuffer.allocate(4 + body.length);
        bb.order(ByteOrder.BIG_ENDIAN);
        bb.putInt(body.length);
        bb.put(body);
        return bb.array();
    }
}

//類(lèi)B:
private IConnectionManager mManager;
//...省略連接及設(shè)置回調(diào)的代碼...
@Override
public void onSocketConnectionSuccess(ConnectionInfo info, String action) {
     //連接成功其他操作...
     //鏈?zhǔn)骄幊陶{(diào)用
     OkSocket.open(info)
        .send(new TestSendData());
}

如何接收數(shù)據(jù)

  • OkSocket客戶端接收服務(wù)器數(shù)據(jù)是要求一定格式的,客戶端的OkSocketOptions提供了接口來(lái)修改默認(rèn)的服務(wù)器返回的包頭解析規(guī)則.請(qǐng)看下圖為默認(rèn)的包頭包體解析規(guī)則


    payload_explain_en_壓縮.png
  • 如上圖包頭中的內(nèi)容為4個(gè)字節(jié)長(zhǎng)度的int型,該int值標(biāo)識(shí)了包體數(shù)據(jù)區(qū)的長(zhǎng)度,這就是默認(rèn)的頭解析.
  • 如果需要自定義頭請(qǐng)按照如下方法

注意:
Header :指的是數(shù)據(jù)協(xié)議中定長(zhǎng)部分,該部分應(yīng)該包含后面不定長(zhǎng)的Body(Payload)的字節(jié)長(zhǎng)度.
Body(Payload) :指的是不定長(zhǎng)部分,通常是數(shù)據(jù)區(qū):

//設(shè)置自定義解析頭
OkSocketOptions.Builder okOptionsBuilder = new OkSocketOptions.Builder(mOkOptions);
okOptionsBuilder.setReaderProtocol(new IReaderProtocol() {
    @Override
    public int getHeaderLength() {
        / *
         * 返回不能為零或負(fù)數(shù)的報(bào)文頭長(zhǎng)度(字節(jié)數(shù))。
         * 您返回的值應(yīng)符合服務(wù)器文檔中的報(bào)文頭的固定長(zhǎng)度值(字節(jié)數(shù)),可能需要與后臺(tái)同學(xué)商定
         * /
        return / *固定報(bào)文頭的長(zhǎng)度(字節(jié)數(shù))* /;
    }

    @Override
    public int getBodyLength(byte[] header, ByteOrder byteOrder) {
     / *
         * 體長(zhǎng)也稱(chēng)為有效載荷長(zhǎng)度,
         * 該值應(yīng)從作為函數(shù)輸入?yún)?shù)的header中讀取。
         * 從報(bào)文頭數(shù)據(jù)header中解析有效負(fù)載長(zhǎng)度時(shí),最好注意參數(shù)中的byteOrder。
         * 我們強(qiáng)烈建議您使用java.nio.ByteBuffer來(lái)做到這一點(diǎn)。
         * 你需要返回有效載荷的長(zhǎng)度,并且返回的長(zhǎng)度中不應(yīng)該包含報(bào)文頭的固定長(zhǎng)度
         * /
        return /*有效負(fù)載長(zhǎng)度(字節(jié)數(shù)),固定報(bào)文頭長(zhǎng)度(字節(jié)數(shù))除外*/;
    }
});
//將新的修改后的參配設(shè)置給連接管理器
mManager.option(okOptionsBuilder.build());


//...正確設(shè)置解析頭之后...
@Override
public void onSocketReadResponse(ConnectionInfo info, String action, OriginalData data) {
    //遵循以上規(guī)則,這個(gè)回調(diào)才可以正常收到服務(wù)器返回的數(shù)據(jù),數(shù)據(jù)在OriginalData中,為byte[]數(shù)組,該數(shù)組數(shù)據(jù)已經(jīng)處理過(guò)字節(jié)序問(wèn)題,直接放入ByteBuffer中即可使用
}

如何保持心跳

//類(lèi)A:
//...定義心跳管理器需要的心跳數(shù)據(jù)類(lèi)型...
public class PulseData implements IPulseSendable {
    private String str = "pulse";

    @Override
    public byte[] parse() {
        byte[] body = str.getBytes(Charset.defaultCharset());
        ByteBuffer bb = ByteBuffer.allocate(4 + body.length);
        bb.order(ByteOrder.BIG_ENDIAN);
        bb.putInt(body.length);
        bb.put(body);
        return bb.array();
    }
}

//類(lèi)B:
private IConnectionManager mManager;
private PulseData mPulseData = new PulseData;
//...省略連接及設(shè)置回調(diào)的代碼...
@Override
public void onSocketConnectionSuccess(ConnectionInfo info, String action) {
     //連接成功其他操作...
     //鏈?zhǔn)骄幊陶{(diào)用,給心跳管理器設(shè)置心跳數(shù)據(jù),一個(gè)連接只有一個(gè)心跳管理器,因此數(shù)據(jù)只用設(shè)置一次,如果斷開(kāi)請(qǐng)?jiān)俅卧O(shè)置.
     OkSocket.open(info)
        .getPulseManager()
        .setPulseSendable(mPulseData)//只需要設(shè)置一次,下一次可以直接調(diào)用pulse()
        .pulse();//開(kāi)始心跳,開(kāi)始心跳后,心跳管理器會(huì)自動(dòng)進(jìn)行心跳觸發(fā)
}

心跳接收到了之后需要進(jìn)行喂狗

  • 因?yàn)槲覀兊目蛻舳诵枰婪?wù)器收到了此次心跳,因此服務(wù)器在收到心跳后需要進(jìn)行應(yīng)答,我們收到此次心跳應(yīng)答后,需要進(jìn)行本地的喂狗操作,否則當(dāng)超過(guò)一定次數(shù)的心跳發(fā)送,未得到喂狗操作后,狗將會(huì)將此次連接斷開(kāi)重連.
//定義成員變量
private IConnectionManager mManager;
//當(dāng)客戶端收到消息后
@Override
public void onSocketReadResponse(ConnectionInfo info, String action, OriginalData data) {
    if(mManager != null && 是心跳返回包){//是否是心跳返回包,需要解析服務(wù)器返回的數(shù)據(jù)才可知道
        //喂狗操作
        mManager.getPulseManager().feed();
    }
}

如何手動(dòng)觸發(fā)一次心跳,在任何時(shí)間

//定義成員變量
private IConnectionManager mManager;
//...在任意地方...
mManager = OkSocket.open(info);
if(mManager != null){
    PulseManager pulseManager = mManager.getPulseManager();
    //手動(dòng)觸發(fā)一次心跳(主要用于一些需要手動(dòng)控制觸發(fā)時(shí)機(jī)的場(chǎng)景)
    pulseManager.trigger();
}

OkSocket參配選項(xiàng)及回調(diào)說(shuō)明

  • OkSocketOptions

    • Socket通訊模式mIOThreadMode
    • 連接是否管理保存isConnectionHolden
    • 寫(xiě)入字節(jié)序mWriteOrder
    • 讀取字節(jié)序mReadByteOrder
    • 頭字節(jié)協(xié)議mHeaderProtocol
    • 發(fā)送單個(gè)數(shù)據(jù)包的總長(zhǎng)度mSendSinglePackageBytes
    • 單次讀取的緩存字節(jié)長(zhǎng)度mReadSingleTimeBufferBytes
    • 脈搏頻率間隔毫秒數(shù)mPulseFrequency
    • 脈搏最大丟失次數(shù)(狗的失喂次數(shù))mPulseFeedLoseTimes
    • 后臺(tái)存活時(shí)間(分鐘)mBackgroundLiveMinute
    • 連接超時(shí)時(shí)間(秒)mConnectTimeoutSecond
    • 最大讀取數(shù)據(jù)的兆數(shù)(MB)mMaxReadDataMB
    • 重新連接管理器mReconnectionManager
  • ISocketActionListener

    • Socket讀寫(xiě)線程啟動(dòng)后回調(diào)onSocketIOThreadStart
    • Socket讀寫(xiě)線程關(guān)閉后回調(diào)onSocketIOThreadShutdown
    • Socket連接狀態(tài)由連接->斷開(kāi)回調(diào)onSocketDisconnection
    • Socket連接成功回調(diào)onSocketConnectionSuccess
    • Socket連接失敗回調(diào)onSocketConnectionFailed
    • Socket從服務(wù)器讀取到字節(jié)回調(diào)onSocketReadResponse
    • Socket寫(xiě)給服務(wù)器字節(jié)后回調(diào)onSocketWriteResponse
    • 發(fā)送心跳后的回調(diào)onPulseSend
   Copyright [2017] [徐昊]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說(shuō)閱讀 12,356評(píng)論 6 13
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,941評(píng)論 25 709
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,540評(píng)論 19 139
  • iPhone的標(biāo)準(zhǔn)推薦是CFNetwork 庫(kù)編程,其封裝好的開(kāi)源庫(kù)是 cocoa AsyncSocket庫(kù),用它...
    Ethan_Struggle閱讀 2,357評(píng)論 2 12
  • 洛桑陀美上師:《和諧中的圓滿》—【惡趣之苦】 關(guān)于三惡道的痛苦 , 尤其是地獄道的痛苦,這方面我們要好好的去...
    祥云_17ec閱讀 653評(píng)論 0 0

友情鏈接更多精彩內(nèi)容