Android的Socket開發之OkSocket

一個Android輕量級Socket通信框架,既OkHttp後又一力做.
框架開源地址及Demo演示: https://github.com/xuuhaoo/OkSocket
歡迎star,fork,Issue交流
---java

OkSocket簡介

  • OkSocket是一款基於阻塞式傳統Socket的一款Socket客戶端總體解決方案.你可使用它進行基於Tcp協議的Socket通信.就是咱們所說的長鏈接.
  • 對通信協議幾乎無限制,可使用PB,可使用JSON,可使用XML.只要能夠序列化成Byte數組的對象均可以傳輸.
  • 兼容全部語言寫的Socket服務端,解決了Tcp通信中頭疼的粘包拆包問題,斷線重連問題,心跳保持問題,分片發送,重定向鏈接等問題.
  • 針對 手機 < - > 服務器 , 手機< - > 手機 間均可以進行tcp通信,手機間通信俗稱點對點通信,能夠很好的支持.
  • OkSocket還支持單工和全雙工通信.適配各類複雜業務場景.分爲 客戶端(OkSocketClient) 服務端(OkSocketServer)具體的繼承和依賴方法在下面.
  • 若是須要看demo程序,能夠去https://github.com/xuuhaoo/OkSocket地址進行clone.以後直接run起來就能夠了.Demo會自動和OkSocket編寫的EchoServer進行鏈接通信,讓使用者更好地瞭解使用方法
  • OkSocket旨在讓更多不熟悉socket和tcp協議的朋友能夠專一於業務開發而不是底層協議的開發和學習.

Demo程序截圖

  • 1.簡單的調用示例(創建鏈接,斷開鏈接,發送數據,接收數據)
  • 2.複雜的調用示例(簡單示例中的內容,斷線重連,心跳,重定向)
  • 3.OkServer的Android端使用(能夠進行點對點通信,固然也能夠部署在雲端服務器用做SocketServer)
    首頁
    簡單示例
    複雜示例

Maven配置

  • OkSocket 支持 JCenter 倉庫
allprojects {
    repositories {
        jcenter()
    }
}
  • 在Module的build.gradle文件中添加依賴配置
dependencies {
    //基礎的 OkSocket 功能集成包.您的Socket開發不管是客戶端仍是Java,都須要此包 (必須集成)
    api 'com.tonystark.android:socket:4.1.0'
    //若是您須要使用 OkSocketServer 功能在客戶端或者Java程序,您還須要依賴下面的Server插件包和上面的一塊兒依賴.
    api 'com.tonystark.android:socket-server:4.1.0'
}

若是您是 Android 請關注下權限

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

混淆配置

  • 請避免混淆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$* {
    *;
}

回聲服務器

  • 該回聲服務器是專門爲初學者調試 OkSocket 庫部屬的一臺服務器,初學者能夠將項目中的 app 安裝到手機上,點擊 Connect 按鈕便可,該服務器僅爲熟悉通信方式和解析方式使用.不能做爲商用服務器. 不用時應及時斷開,保證有限的資源最大化利用

公網IP:104.238.184.237
公網Port:8080android

開始一個簡單的長鏈接
  • OkSocket 會默認對每個 Open 的新通道作緩存管理,僅在第一次調用 Open 方法時建立 ConnectionManager 管理器,以後調用者能夠經過獲取到該ConnectionManager的引用,繼續調用相關方法
  • ConnectionManager 主要負責該地址的套接字鏈接斷開發送消息等操做.
//鏈接參數設置(IP,端口號),這也是一個鏈接的惟一標識,不一樣鏈接,該參數中的兩個值至少有其一不同
ConnectionInfo info = new ConnectionInfo("104.238.184.237", 8080);
//調用OkSocket,開啓此次鏈接的通道,調用通道的鏈接方法進行鏈接.
OkSocket.open(info).connect();
有回調的長鏈接
  • 註冊該通道的監聽器,每一個 Connection 通道中的監聽器互相隔離,所以若是一個項目鏈接了多個 Socket 鏈接須要在每一個 Connection 註冊本身的鏈接監聽器,鏈接監聽器是該 OkSocket 與用戶交互的惟一途徑
//鏈接參數設置(IP,端口號),這也是一個鏈接的惟一標識,不一樣鏈接,該參數中的兩個值至少有其一不同
ConnectionInfo info = new ConnectionInfo("104.238.184.237", 8080);
//調用OkSocket,開啓此次鏈接的通道,拿到通道Manager
IConnectionManager manager = OkSocket.open(info);
//註冊Socket行爲監聽器,SocketActionAdapter是回調的Simple類,其餘回調方法請參閱類文檔
manager.registerReceiver(new SocketActionAdapter(){
    @Override
    public void onSocketConnectionSuccess(ConnectionInfo info, String action) {
     Toast.makeText(context, "鏈接成功", LENGTH_SHORT).show();
    }
});
//調用通道進行鏈接
manager.connect();
可配置的長鏈接
  • 得到 OkSocketOptions 的行爲屬於比較高級的 OkSocket 調用方法,每一個 Connection 將會對應一個 OkSocketOptions,若是第一次調用 Open 時未指定 OkSocketOptions,OkSocket將會使用默認的配置對象,默認配置請見文檔下方的高級調用說明
//鏈接參數設置(IP,端口號),這也是一個鏈接的惟一標識,不一樣鏈接,該參數中的兩個值至少有其一不同
ConnectionInfo info = new ConnectionInfo("104.238.184.237", 8080);
//調用OkSocket,開啓此次鏈接的通道,拿到通道Manager
IConnectionManager manager = OkSocket.open(info);
//得到當前鏈接通道的參配對象
OkSocketOptions options= manager.getOption();
//基於當前參配對象構建一個參配建造者類
OkSocketOptions.Builder builder = new OkSocketOptions.Builder(options);
//修改參配設置(其餘參配請參閱類文檔)
builder.setSinglePackageBytes(size);
//建造一個新的參配對象而且付給通道
manager.option(builder.build());
//調用通道進行鏈接
manager.connect();
如何進行數據發送
//類A:
//...定義將要發送的數據結構體...
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() {
        //根據服務器的解析規則,構建byte數組
        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();
    }
}

//類B:
private IConnectionManager mManager;
//...省略鏈接及設置回調的代碼...
@Override
public void onSocketConnectionSuccess(ConnectionInfo info, String action) {
     //鏈接成功其餘操做...
     //鏈式編程調用
     OkSocket.open(info)
        .send(new TestSendData());
}
如何接收數據
  • OkSocket客戶端接收服務器數據是要求必定格式的,客戶端的OkSocketOptions提供了接口來修改默認的服務器返回的包頭解析規則.請看下圖爲默認的包頭包體解析規則
    payload_explain_en_壓縮.pnggit

  • 如上圖包頭中的內容爲4個字節長度的int型,該int值標識了包體數據區的長度,這就是默認的頭解析,若是須要自定義頭請按照以下方法.github

//設置自定義解析頭
OkSocketOptions.Builder okOptionsBuilder = new OkSocketOptions.Builder(mOkOptions);
okOptionsBuilder.setReaderProtocol(new IReaderProtocol() {
    @Override
    public int getHeaderLength() {
        / *
         * 返回不能爲零或負數的報文頭長度(字節數)。
         * 您返回的值應符合服務器文檔中的報文頭的固定長度值(字節數),可能須要與後臺同窗商定
         * /
        return / *固定報文頭的長度(字節數)* /;
    }

    @Override
    public int getBodyLength(byte[] header, ByteOrder byteOrder) {
     / *
         * 體長也稱爲有效載荷長度,
         * 該值應從做爲函數輸入參數的header中讀取。
         * 從報文頭數據header中解析有效負載長度時,最好注意參數中的byteOrder。
         * 咱們強烈建議您使用java.nio.ByteBuffer來作到這一點。
         * 你須要返回有效載荷的長度,而且返回的長度中不該該包含報文頭的固定長度
         * /
        return /*有效負載長度(字節數),固定報文頭長度(字節數)除外*/;
    }
});
//將新的修改後的參配設置給鏈接管理器
mManager.option(okOptionsBuilder.build());


//...正確設置解析頭以後...
@Override
public void onSocketReadResponse(ConnectionInfo info, String action, OriginalData data) {
    //遵循以上規則,這個回調才能夠正常收到服務器返回的數據,數據在OriginalData中,爲byte[]數組,該數組數據已經處理過字節序問題,直接放入ByteBuffer中便可使用
}
如何保持心跳
//類A:
//...定義心跳管理器須要的心跳數據類型...
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();
    }
}

//類B:
private IConnectionManager mManager;
private PulseData mPulseData = new PulseData;
//...省略鏈接及設置回調的代碼...
@Override
public void onSocketConnectionSuccess(ConnectionInfo info, String action) {
     //鏈接成功其餘操做...
     //鏈式編程調用,給心跳管理器設置心跳數據,一個鏈接只有一個心跳管理器,所以數據只用設置一次,若是斷開請再次設置.
     OkSocket.open(info)
        .getPulseManager()
        .setPulseSendable(mPulseData)//只須要設置一次,下一次能夠直接調用pulse()
        .pulse();//開始心跳,開始心跳後,心跳管理器會自動進行心跳觸發
}
心跳接收到了以後須要進行喂狗
  • 由於咱們的客戶端須要知道服務器收到了這次心跳,所以服務器在收到心跳後須要進行應答,咱們收到這次心跳應答後,須要進行本地的喂狗操做,不然當超過必定次數的心跳發送,未獲得喂狗操做後,狗將會將這次鏈接斷開重連.
//定義成員變量
private IConnectionManager mManager;
//當客戶端收到消息後
@Override
public void onSocketReadResponse(ConnectionInfo info, String action, OriginalData data) {
    if(mManager != null && 是心跳返回包){//是不是心跳返回包,須要解析服務器返回的數據纔可知道
        //喂狗操做
        mManager.getPulseManager().feed();
    }
}
如何手動觸發一次心跳,在任什麼時候間
//定義成員變量
private IConnectionManager mManager;
//...在任意地方...
mManager = OkSocket.open(info);
if(mManager != null){
    PulseManager pulseManager = mManager.getPulseManager();
    //手動觸發一次心跳(主要用於一些須要手動控制觸發時機的場景)
    pulseManager.trigger();
}

OkSocket參配選項及回調說明

  • OkSocketOptions
    • Socket通信模式mIOThreadMode
    • 鏈接是否管理保存isConnectionHolden
    • 寫入字節序mWriteOrder
    • 讀取字節序mReadByteOrder
    • 頭字節協議mHeaderProtocol
    • 發送單個數據包的總長度mSendSinglePackageBytes
    • 單次讀取的緩存字節長度mReadSingleTimeBufferBytes
    • 脈搏頻率間隔毫秒數mPulseFrequency
    • 脈搏最大丟失次數(狗的失喂次數)mPulseFeedLoseTimes
    • 後臺存活時間(分鐘)mBackgroundLiveMinute
    • 鏈接超時時間(秒)mConnectTimeoutSecond
    • 最大讀取數據的兆數(MB)mMaxReadDataMB
    • 從新鏈接管理器mReconnectionManager
  • ISocketActionListener
    • Socket讀寫線程啓動後回調onSocketIOThreadStart
    • Socket讀寫線程關閉後回調onSocketIOThreadShutdown
    • Socket鏈接狀態由鏈接->斷開回調onSocketDisconnection
    • Socket鏈接成功回調onSocketConnectionSuccess
    • Socket鏈接失敗回調onSocketConnectionFailed
    • Socket從服務器讀取到字節回調onSocketReadResponse
    • Socket寫給服務器字節後回調onSocketWriteResponse
    • 發送心跳後的回調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.
相關文章
相關標籤/搜索