精华内容
下载资源
问答
  • OkSocket使用注意事项

    2020-06-05 13:07:44
    作为server时,不要轻易使用client.getHostName(),因为如果没有正确获取到hostName可能会导致socket读写流报错。

    作为server时,不要轻易使用client.getHostName(),因为如果没有正确获取到hostName可能会导致socket读写流报错。

    展开全文
  • okSocket库的使用

    2018-11-06 12:12:44
    优秀第三方库okSocket使用,长连接加喂狗,心跳包等等使用
  • 本篇文章主要介绍了详解OkSocket与Android的简单使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 如何使用oksocket接收发送数据

    千次阅读 2018-11-25 23:58:26
    第一次使用oksocket的同学请先阅读OkSocket在Android上的基本使用 此教程针对阅读OkSocket在Android上的基本使用后对如何接收发送数据不太了解的同学. 接收数据 以我之前项目的协议为例,服务端是C,DWORD占四个字节...

    第一次使用oksocket的同学请先阅读OkSocket在Android上的基本使用
    此教程针对阅读OkSocket在Android上的基本使用后对如何接收发送数据不太了解的同学.
    在这里插入图片描述

    接收数据

    通讯协议

    以我之前项目的协议为例,服务端是C,DWORD占四个字节,unsigned char占两个字节,所以包头长度为22个字节,那么包头就需要这样设置,
    

    自定义解析包头

    而包头中包含的这个len就是包体的长度(有的服务端返回的是整个数据的长度,那么需要减去包头的长度),代码如下,
    

    自定义解析包体

    以上,就是如何设置oksocket接收服务端的数据.
    

    发送数据

    接下来是发送数据,服务端发送和接收的协议都是一样的,所以还是以上面的协议,
    

    拼接参数

    教程结束!在这里插入图片描述

    在这里插入图片描述

    没好的同学请加群737773203,框架开源地址oksocket,欢迎star fork,
    在这里插入图片描述

    展开全文
  • OkSocket在Android上的基本使用

    千次阅读 2018-11-21 16:10:07
    一个Android轻量级Socket通讯框架,既OkHttp后又一力作. 框架开源地址及Demo演示: https://github.com/xuuhaoo/OkSocket 欢迎star,fork,Issue交流 ...你可以使用它进行基于Tcp协议的Socket通讯.就是我们所说的长连接....

    一个Android轻量级Socket通讯框架,既OkHttp后又一力作.
    框架开源地址及Demo演示: https://github.com/xuuhaoo/OkSocket 欢迎star,fork,Issue交流


    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:8080

    开始一个简单的长连接
    • 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_压缩.png

    • 如上图包头中的内容为4个字节长度的int型,该int值标识了包体数据区的长度,这就是默认的头解析,如果需要自定义头请按照如下方法.

    //设置自定义解析头
    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 [2018] [徐昊]
    
       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.
    
    展开全文
  • Android的轻量级Socket通讯框架OkSocket使用demo。要实现安卓Socket客户端与C#服务端Socket通信,使用安卓类库OkSocket完美实现。使用了Socket长连接,断线重连,持续心跳连接,发送握手通讯等
  • 一个Android轻量级Socket通讯框架,既OkHttp后又一力作. 框架开源地址: https://github.com/xuuhaoo/OkSocket ...您可以使用它进行简单的基于Tcp协议的Socket通讯,当然,也可以进行大数据量复杂的Socket通讯, 支持...

    一个Android轻量级Socket通讯框架,既OkHttp后又一力作.
    框架开源地址: https://github.com/xuuhaoo/OkSocket


    OkSocket简介

    Android OkSocket是一款基于阻塞式传统Socket的一款Socket客户端整体解决方案.您可以使用它进行简单的基于Tcp协议的Socket通讯,当然,也可以进行大数据量复杂的Socket通讯,
    支持单工,双工通讯.


    Maven配置

    • OkSocket 目前仅支持 JCenter 仓库
    allprojects {
        repositories {
            jcenter()
        }
    }
    
    • 在Module的build.gradle文件中添加依赖配置
    dependencies {
        compile 'com.tonystark.android:socket:1.0.0'
    }
    

    参数配置

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

    混淆配置

    • 请避免混淆OkSocket,在Proguard混淆文件中增加以下配置:
    -dontwarn com.xuhao.android.libsocket.**
    -keep class com.xuhao.android.socket.impl.abilities.** { *; }
    -keep class com.xuhao.android.socket.impl.exceptions.** { *; }
    -keep class com.xuhao.android.socket.impl.EnvironmentalManager { *; }
    -keep class com.xuhao.android.socket.impl.BlockConnectionManager { *; }
    -keep class com.xuhao.android.socket.impl.UnBlockConnectionManager { *; }
    -keep class com.xuhao.android.socket.impl.SocketActionHandler { *; }
    -keep class com.xuhao.android.socket.impl.PulseManager { *; }
    -keep class com.xuhao.android.socket.impl.ManagerHolder { *; }
    -keep class com.xuhao.android.socket.interfaces.** { *; }
    -keep class com.xuhao.android.socket.sdk.** { *; }
    # 枚举类不能被混淆
    -keepclassmembers enum * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
    -keep class com.xuhao.android.socket.sdk.OkSocketOptions$* {
        *;
    }
    

    OkSocket初始化

    • 将以下代码复制到项目Application类onCreate()中,OkSocket会为自动检测环境并完成配置:
    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            //在主进程初始化一次,多进程时需要区分主进程.
            OkSocket.initialize(this);
            //如果需要开启Socket调试日志,请配置
            //OkSocket.initialize(this,true);
        }
    }
    

    调用演示

    测试服务器

    • 该服务器是专门为初学者调试 OkSocket 库部属的一台测试服务器,初学者可以将项目中的 app 安装到手机上,点击 Connect 按钮即可,该服务器仅为熟悉通讯方式和解析方式使用.该服务器不支持心跳返回,不能作为商用服务器.服务器代码在 SocketServerDemo 文件夹中,请注意参考阅读.
      IP: 104.238.184.237
      Port: 8080

    • 您也可以选择下载 JAR 文件到本地,运行在您的本地进行调试 Download JAR
      下载后使用下面的代码将其运行起来java -jar SocketServerDemo.jar

    简单的长连接

    • 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(Context context, 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(Context context, ConnectionInfo info, String action) {
         //连接成功其他操作...
         //链式编程调用
         OkSocket.open(info)
            .send(new TestSendData());
         
         //此处也可将ConnectManager保存成成员变量使用.
         mManager = OkSocket.open(info);
         if(mManager != null){
            mManager.send(new TestSendData());
         }
         //以上两种方法选择其一,成员变量的方式请注意判空
    }
    

    如何接收数据

    • OkSocket客户端接收服务器数据是要求一定格式的,客户端的OkSocketOptions提供了接口来修改默认的服务器返回的包头解析规则.请看下图为默认的包头包体解析规则


      数据结构示意图

      数据结构示意图

    • 如上图包头中的内容为4个字节长度的int型,该int值标识了包体数据区的长度,这就是默认的头解析,如果需要自定义头请按照如下方法.
    //设置自定义解析头
    OkSocketOptions.Builder okOptionsBuilder = new OkSocketOptions.Builder(mOkOptions);
    okOptionsBuilder.setHeaderProtocol(new IHeaderProtocol() {
        @Override
        public int getHeaderLength() {
            //返回自定义的包头长度,框架会解析该长度的包头
            return 0;
        }
    
        @Override
        public int getBodyLength(byte[] header, ByteOrder byteOrder) {
            //从header(包头数据)中解析出包体的长度,byteOrder是你在参配中配置的字节序,可以使用ByteBuffer比较方便解析
            return 0;
        }
    });
    //将新的修改后的参配设置给连接管理器
    mManager.option(okOptionsBuilder.build());
    
    
    //...正确设置解析头之后...
    @Override
    public void onSocketReadResponse(Context context, 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(Context context, ConnectionInfo info, String action) {
         //连接成功其他操作...
         //链式编程调用,给心跳管理器设置心跳数据,一个连接只有一个心跳管理器,因此数据只用设置一次,如果断开请再次设置.
         OkSocket.open(info)
            .getPulseManager()
            .setPulseSendable(mPulseData)
            .pulse();//开始心跳,开始心跳后,心跳管理器会自动进行心跳触发
                    
         //此处也可将ConnectManager保存成成员变量使用.
         mManager = OkSocket.open(info);
         if(mManager != null){
            PulseManager pulseManager = mManager.getPulseManager();
            //给心跳管理器设置心跳数据,一个连接只有一个心跳管理器,因此数据只用设置一次,如果断开请再次设置.
            pulseManager.setPulseSendable(mPulseData);
            //开始心跳,开始心跳后,心跳管理器会自动进行心跳触发
            pulseManager.pulse();
         }
         //以上两种方法选择其一,成员变量的方式请注意判空
    }
    

    心跳接收到了之后需要进行喂狗

    • 因为我们的客户端需要知道服务器收到了此次心跳,因此服务器在收到心跳后需要进行应答,我们收到此次心跳应答后,需要进行本地的喂狗操作,否则当超过一定次数的心跳发送,未得到喂狗操作后,狗将会将此次连接断开重连.
    //定义成员变量
    private IConnectionManager mManager;
    //当客户端收到消息后
    @Override
    public void onSocketReadResponse(Context context, 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

    示例代码(已传至码云)

    public class MainActivity extends BaseActivity {
    
    	private static final String TAG = MainActivity.class.getSimpleName();
    	private final String CONN_NO = "未连接",CONNECTING="连接中",CONN_FAIL="连接失败",CONN_OK="已连接";
    	@BindView(R.id.bt_start)
    	Button btStart;
    	@BindView(R.id.bt_send)
    	Button btSend;
    	@BindView(R.id.tv_log)
    	TextView tvLog;
    	@BindView(R.id.tv_count)
    	TextView tvCount;
    	@BindView(R.id.tv_conn_status)
    	TextView tvConnStatus;
    	@BindView(R.id.tv_reconn_count)
    	TextView tvReconnCount;
    	@BindView(R.id.et_ip)
    	TextView etIP;
    	@BindView(R.id.et_port)
    	TextView etPort;
    	@BindView(R.id.et_time)
    	TextView etTime;
    	private IConnectionManager manager;
    	private String data = " %s,%s,%s,%s"; //"071 135790246811222,2018-7-9 18:06:20,98,-72,bs[460:0:28730:20736:34]"
    	private String deviceImei;
    	private int counts = 0; //发送数据次数
    	private int reconnCounts = 0; //重连次数
    	private ConnectionInfo connInfo;
    	private Timer dataTimer;
    	private boolean isSendData = false;
    	private String nmeaLogPath;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		ButterKnife.bind(this);
    		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    			requestPermission();
    		} else {
    			init();
    		}
    	}
    
    	private void init() {
    		tvLog.setText(counts+"");
    		tvConnStatus.setText(CONN_NO);
    		File dir = new File(Constants.LOG);
    		if(!dir.exists()){
    			dir.mkdirs();
    		}
    		String timeName = DateUtil.getCurrentDate(DateUtil.dateFormatYMDHMS);
    		nmeaLogPath = Constants.LOG + File.separator + timeName + ".txt";
    		//数据回显
    		String sim_ip = PFUtils.getPrefString(MainActivity.this, "sim_ip", "139.196.255.699");
    		int sim_port = PFUtils.getPrefInt(MainActivity.this, "sim_port", 9999);
    		int sim_time = PFUtils.getPrefInt(MainActivity.this, "sim_time", 1000);
    		etIP.setText(sim_ip);
    		etPort.setText(sim_port+"");
    		etTime.setText(sim_time+"");
    	}
    
    	public void sendData() {
    		if (manager != null) {
    			isSendData = true;
    			String format = String.format(data, deviceImei, DateUtil.getCurrentDate(DateUtil.dateFormatYMDHMS), counts + "", PhoneUtil.getMobileSignal(MainActivity.this));
    			int dataLen = format.length() + 3;
    			String len;
    			if(dataLen < 10){
    				len = "00" + dataLen;
    			}else if(dataLen <100){
    				len = "0"+ dataLen;
    			}else{
    				len = ""+ dataLen;
    			}
    			String data = len + format;
    			manager.send(new SendData(data));
    		} else {
    			showTipsDialog("Please Connect To Server!");
    		}
    	}
    
    	@OnClick(R.id.bt_send)
    	public void onViewClicked2() {
    		Timer timer = new Timer(true);
    		timer.schedule(new TimerTask() {
    			@Override
    			public void run() {
    				PhoneUtil.getBsSignal(MainActivity.this);
    			}
    		},100,1000);
    
    		if (manager != null) {
    			String time = etTime.getText().toString().trim();
    			if(TextUtils.isEmpty(time)){
    				showTipsDialog("IP和Port不能为空");
    				return;
    			}
    			int timeInt = Integer.parseInt(time);
    			if(timeInt <= 0){
    				showTipsDialog("请输入大于0的整数");
    				return;
    			}
    			PFUtils.setPrefInt(MainActivity.this,"sim_time",timeInt);
    			if(!isSendData){
    				dataTimer = new Timer();
    				dataTimer.schedule(new TimerTask() {
    					@Override
    					public void run() {
    						sendData();
    					}
    				},100,timeInt);
    			}else{
    				showTipsDialog("data sending!");
    			}
    		}else{
    			showTipsDialog("Please Connect To Server!");
    		}
    	}
    
    	@OnClick(R.id.bt_start)
    	public void onViewClicked() {
    		String trimIp = etIP.getText().toString().trim();
    		String trimPort = etPort.getText().toString().trim();
    		if(TextUtils.isEmpty(trimIp) || TextUtils.isEmpty(trimPort)){
    			showTipsDialog("IP和Port不能为空");
    			return;
    		}
    		int port = Integer.parseInt(trimPort);
    		PFUtils.setPrefString(MainActivity.this,"sim_ip",trimIp);
    		PFUtils.setPrefInt(MainActivity.this,"sim_port",port);
    		//连接参数设置(IP,端口号),这也是一个连接的唯一标识,不同连接,该参数中的两个值至少有其一不一样
    		connInfo = new ConnectionInfo(trimIp, port);
    		deviceImei = PhoneUtil.getDeviceImei(MainActivity.this);
    		//调用OkSocket,开启这次连接的通道,拿到通道Manager
    		manager = OkSocket.open(connInfo);
    		//注册Socket行为监听器,SocketActionAdapter是回调的Simple类,其他回调方法请参阅类文档
    		manager.registerReceiver(mSocketAdapter);
    		manager.connect(); //调用通道进行连接
    	}
    
    	SocketActionAdapter mSocketAdapter = new SocketActionAdapter() {
    		@Override
    		public void onSocketIOThreadStart(Context context, String action) {
    			super.onSocketIOThreadStart(context, action);
    			MLog.e(TAG, "onSocketIOThreadStart:" + action);
    			saveLog(action);
    		}
    
    		@Override
    		public void onSocketIOThreadShutdown(Context context, String action, Exception e) {
    			super.onSocketIOThreadShutdown(context, action, e);
    			MLog.e(TAG, "onSocketIOThreadShutdown:" + action+" Error:"+e.getMessage());
    			saveLog(action);
    		}
    
    		@Override
    		public void onSocketDisconnection(Context context, ConnectionInfo info, String action, Exception e) {
    			super.onSocketDisconnection(context, info, action, e);
    			MLog.e(TAG, "onSocketDisconnection:" + action+" Error:"+e.getMessage());
    			tvConnStatus.setText(CONN_NO);
    			saveLog(action);
    		}
    
    		@Override
    		public void onSocketConnectionSuccess(Context context, ConnectionInfo info, String action) {
    			super.onSocketConnectionSuccess(context, info, action);
    			MLog.e(TAG, "onSocketConnectionSuccess:" + action);
    			Toast.makeText(context, "连接成功", Toast.LENGTH_SHORT).show();
    			tvConnStatus.setText(CONN_OK);
    			saveLog(action);
    		}
    
    		@Override
    		public void onSocketConnectionFailed(Context context, ConnectionInfo info, String action, Exception e) {
    			super.onSocketConnectionFailed(context, info, action, e);
    			MLog.e(TAG, "onSocketConnectionFailed:" + action+" Error:"+e.getMessage());
    			saveLog(action);
    			reconnCounts++;
    			tvConnStatus.setText(CONN_FAIL);
    			tvReconnCount.setText(reconnCounts+"");
    		}
    
    		@Override
    		public void onSocketReadResponse(Context context, ConnectionInfo info, String action, OriginalData data) {
    			super.onSocketReadResponse(context, info, action, data);
    			MLog.e(TAG, "onSocketReadResponse:" + action);
    			saveLog(action);
    		}
    
    		@Override
    		public void onSocketWriteResponse(Context context, ConnectionInfo info, String action, ISendable data) {
    			super.onSocketWriteResponse(context, info, action, data);
    			saveLog("onSocketWriteResponse:"+action);
    			MLog.e(TAG, "onSocketWriteResponse:数据发送成功" + data.toString());
    			tvCount.setText(counts+"");
    			tvLog.setText(counts+" ->"+data.toString());
    			counts++;
    			saveLog(data.toString());
    		}
    
    		@Override
    		public void onPulseSend(Context context, ConnectionInfo info, IPulseSendable data) {
    			super.onPulseSend(context, info, data);
    			MLog.e(TAG, "onPulseSend:" + data.toString());
    		}
    	};
    
    	private void saveLog(String info){
    		try {
    			String timeName = DateUtil.getCurrentDate(DateUtil.dateFormatYMDHMS);
    			FileWriter fw = new FileWriter(nmeaLogPath , true);
    			BufferedWriter bw = new BufferedWriter(fw);
    			bw.write("["+timeName+"] "+info+"\n");
    			bw.close();
    			fw.close();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    
    
    	@Override
    	protected void onDestroy() {
    		super.onDestroy();
    		if(manager != null) manager.disconnect();
    		if(dataTimer != null)dataTimer.cancel();
    	}
    
    	@Override
    	public boolean onKeyDown(int keyCode, KeyEvent event) {
    		if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
    			new SweetAlertDialog(this, SweetAlertDialog.WARNING_TYPE)
    					.setTitleText("确定退出吗?")
    					.setCancelText("取消")
    					.setConfirmText("确定")
    					.showCancelButton(true)
    					.setCancelClickListener(null)
    					.setConfirmClickListener(sDialog ->{
    						if(manager != null) manager.disconnect();
    						if(dataTimer != null)dataTimer.cancel();
    						MXApp.getInstance().exit();
    					})
    					.show();
    			return true;
    		}
    		return super.onKeyDown(keyCode, event);
    	}
    
    	private void requestPermission() {
    		PermissionUtils.permission(PermissionConstants.PHONE, PermissionConstants.STORAGE, PermissionConstants.LOCATION)
    				.rationale(shouldRequest -> DialogHelper.showRationaleDialog(shouldRequest, MainActivity.this))
    				.callback(new PermissionUtils.FullCallback() {
    					@Override
    					public void onGranted(List<String> permissionsGranted) {
    						init();
    					}
    
    					@Override
    					public void onDenied(List<String> permissionsDeniedForever, List<String> permissionsDenied) {
    						if (!permissionsDeniedForever.isEmpty()) {
    							DialogHelper.showOpenAppSettingDialog(MainActivity.this);
    						} else {
    							MXApp.getInstance().exit();
    						}
    					}
    				})
    				.request();
    	}
    
    }
    

     

    展开全文
  • Android开发OKSocket第三方库的使用

    千次阅读 2018-11-06 12:08:48
    最近看GitHub发现有一个不错的第三方库oksocket,封装的很好,自带连接响应和心跳包等,确实很不错 地址:https://github.com/xuuhaoo/OkSocket 大家有兴趣可以去看看,话不多说,直接上代码,一些基础的设置大家可以...
  • OkSocket需要设置解析头: //设置自定义解析头 OkSocketOptions.Builder okOptionsBuilder = new OkSocketOptions.Builder(mOkOptions); okOptionsBuilder.setReaderProtocol(new IReaderProtocol() { ...
  • 您可以使用它进行简单的基于Tcp协议的Socket通讯,当然,也可以进行大数据量复杂的Socket通讯, 支持单工,双工通讯. 从github上下载了OkSocket源码引入项目中,如下所示: Maven配置 OkSocket 目前仅支持 .
  • 先看一下JT808数据的组成和OkSocket接收消息方式; 1、JT808数据的组成 标识位采用0x7e 表示,若校验码、消息头以及消息体中出现0x7e,则要进行转义处理,转义 规则定义如下: 0x7e <————> 0x7d...
  • Android OkSocket是一款基于JavaNIO和传统Socket的一款Socket客户端整体解决方案.您可以使用它进行简单的基于Tcp协议的Socket通讯,当然,也可以进行大数据量复杂的Socket通讯,支持单工,双工通讯.
  • OkSocket文件 Java应用程序或Andorid的阻塞套接字客户端。 Open source licenses Basic Socket Library Socket Server Plugin Library 中文在线文档 为了照顾中国同学的使用,提供了中文的说明文档: : ...
  • 这个框架必须要固定包头,我们在实际的使用中一般都是固定包头的,但是为了方便调试或者没有固定包头的,我们需要修改一下源码; 把源码中的这个接收消息类'ReaderImpl'修改一下: public class ReaderImpl ...
  • java提供的Socket接口还是比较难用的,而网上有一个开源库OkScoket封装的还是挺好用的,Github地址:https://github.com/xuuhaoo/OkSocket 但即使如此,其没有一对一回调或同步请求方法,只能通过一个或几个统一的回调...
  • Android开发中使用开源库汇总 库名 地址 功能描述 BaseRecyclerViewAdapterHelper https://github.com/CymChad/BaseRecyclerViewAdapterHelper 强大灵活的RecyclerView Adapter SwipeRecyclerView ...
  • 安卓开发领域,很多重要的问题都有了很好的开源解决方案,例如网络请求 OkHttp + Retrofit 简直就是...1,整体思路从使用方法出发,首先是怎么使用,其次是我们使用的功能在内部是如何实现的,实现方案上有什么技...
  • 之前写过两篇关于socket的文章,但是,只是简单的介绍了一下关于socket Tcp和Udp的简单使用。如果没有看过的朋友可以去看看Android Socket编程(tcp)初探和Android Socket编程(udp)初探。相信很多朋友在公司使用...

空空如也

空空如也

1 2 3 4
收藏数 65
精华内容 26
热门标签
关键字:

oksocket使用