Android Websocket+protobuf使用
2017-11-06 13:52
477 查看
前言:
环境使用Android studio
websocket 选用的jar包是java-websocket
protobuf选择的是google针对android简化版本的protobuf。
说明:如果使用eclipse作为开发环境,以上两个jar包要自己下载,如果是Android studio开发,可直接在build.gradle中配置或或者下载jar包配置也行。
以下均以Android studio做开发环境说明。
java-websocket 自己下载的jar包,这里jar包如何配置就不具体描述了(略);
protobuf在build.gradle配置的,具体如下:
并增加下面配置引入protoc编辑器,可以在Build -> Make Project 自动把.proto文件生成java类,如果自己在外部能生成java类,这里可以不用配置:
在dependencies中增加protobuf依赖:
到此,配置结束,下面进行具体使用。
将xxx.proto文件放到主项目的一个目录下,例如:
编译生成java文件。如果是通过上面配置引入了protoc编辑器,则可以直接通过 Build -> Make Project 生成相关的java文件,生成的java文件目录:
build > generated > source > proto > debug/release > javalite > 目录中
创建一个WebsocketManager.java类:
1.创建websocket对象,并建立连接:
注:websocket连接address格式是以ws:// 做开头的,例如:ws//192.168.1.1:8080。
2.关闭websocket方式
以上两步就是websocket建立打开及关闭的方式。
3.最重要的一步,通信(客户端与服务器端的通信),通信数据格式通过byte传递,一般要与服务器端进行约定数据格式
传输数据格式如下:
1) client -----> server数据转化,
先将protobuf 对象进行toByteArray()转化成byte数组;
将byte数组长度int类型转化成byte数组 A;
定义的protobuf消息类型的id,int类型转化成byte数组 B
最后使用ByteArrayOutputStream,依次写入 B ,A ,及msg,然后传给服务器处理。参考如下代码
2)接收到服务器端数据,同样需要转换成客户端需要的数据格式,即:
先取出前面N位内容byte数组A转化成int类型值,该值用于判断消息类型,用于区分是哪个消息;
取出N+1位到M位byte数组B转化成int类型,该值用于设置消息包含的数据内容长度;
最后取出后续内容的byte数据,通过id找到对应的实体类型,在根据对应的parseFrom方法,解析byte数组数据。
参考代码如下:
在数据转化过程中需要将int 和short类型进行转换成byte[] 及逆向转换,转换代码如下:
注:在转换中,有些时候可能高字节转换的,请注意区分!!!
另外,如需进行websocket断开重连,建议断开后延迟1秒在进行websocket重连,否则可能会导致,websocket断开操作未结束,新的websocket已建立,从而导致异常。
环境使用Android studio
websocket 选用的jar包是java-websocket
protobuf选择的是google针对android简化版本的protobuf。
说明:如果使用eclipse作为开发环境,以上两个jar包要自己下载,如果是Android studio开发,可直接在build.gradle中配置或或者下载jar包配置也行。
以下均以Android studio做开发环境说明。
java-websocket 自己下载的jar包,这里jar包如何配置就不具体描述了(略);
protobuf在build.gradle配置的,具体如下:
apply plugin: 'com.google.protobuf'
并增加下面配置引入protoc编辑器,可以在Build -> Make Project 自动把.proto文件生成java类,如果自己在外部能生成java类,这里可以不用配置:
protobuf { //这里配置protoc编译器 protoc { artifact = 'com.google.protobuf:protoc:3.0.0-alpha-3' } plugins { javalite { // The codegen for lite comes as a separate artifact artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0' } } //这里配置生成目录,编译后会在build的目录下生成对应的java文件 generateProtoTasks { all().each { task -> task.plugins { javalite {} } } } }
在dependencies中增加protobuf依赖:
dependencies{ ... compile 'com.google.protobuf:protobuf-lite:3.0.0' //引入java-websocket compile files('libs/java-websocket-1.3.0.jar') ... }
到此,配置结束,下面进行具体使用。
将xxx.proto文件放到主项目的一个目录下,例如:
编译生成java文件。如果是通过上面配置引入了protoc编辑器,则可以直接通过 Build -> Make Project 生成相关的java文件,生成的java文件目录:
build > generated > source > proto > debug/release > javalite > 目录中
创建一个WebsocketManager.java类:
1.创建websocket对象,并建立连接:
webSocketClient = new WebSocketClient(URI.create(address),new Draft_17()) { @Override public void onOpen(ServerHandshake serverHandshake) { AppLogUtil.logLocal("onOpen ----->>>> websocke connect success"); } @Override public void onMessage(String s) { AppLogUtil.logLocal("onMessage : " + s); } @Override public void onMessage(ByteBuffer bytes) { super.onMessage(bytes); // AppLogUtil.logLocal("onMessage : " + bytes); } @Override public void onClose(int i, String s, boolean b) { AppLogUtil.logLocal("onClose ----->>>> websocket close .i = "+i+",s = "+s); } @Override public void onError(Exception e) { AppLogUtil.logLocal("onError ----->>>> websocket onError"); } }; webSocketClient.connect();
注:websocket连接address格式是以ws:// 做开头的,例如:ws//192.168.1.1:8080。
2.关闭websocket方式
/** * app 退出时,关闭socket */ public void closeWebSocket(){ if (webSocketClient != null && webSocketClient.getConnection().isOpen()){ AppLogUtil.logLocal("closeWebSocket"); webSocketClient.close(); webSocketClient = null; } }
以上两步就是websocket建立打开及关闭的方式。
3.最重要的一步,通信(客户端与服务器端的通信),通信数据格式通过byte传递,一般要与服务器端进行约定数据格式
传输数据格式如下:
1) client -----> server数据转化,
先将protobuf 对象进行toByteArray()转化成byte数组;
将byte数组长度int类型转化成byte数组 A;
定义的protobuf消息类型的id,int类型转化成byte数组 B
最后使用ByteArrayOutputStream,依次写入 B ,A ,及msg,然后传给服务器处理。参考如下代码
/** * 发送protobuf 格式转换 * * @param cmdid * @param msg */ private void sendMessage(Songid.enSongCmdId cmdid, byte[] msg){ if(webSocketClient != null && webSocketClient.getConnection().isOpen()){ try { //消息id short id = (short) cmdid.getNumber(); //消息id short转byte数组 byte[] ba_cmdid = DataConvertUtils.shortToByteArray(id); //转成byte后数组的长度 int cid_len = ba_cmdid.length; //消息内容protobuf 数据结构转byte数组后的长度 int msg_len = msg.length; //消息长度转byte数组 byte[] ba_msglen = DataConvertUtils.intToBytes(msg_len); //转成byte数组后的长度 int len2 = ba_msglen.length; //依次写入bytearray中 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); outputStream.write(ba_cmdid); outputStream.write(ba_msglen); outputStream.write(msg); beginSendTime = System.currentTimeMillis(); webSocketClient.send(outputStream.toByteArray()); outputStream.close(); }catch (Exception ex){ ex.printStackTrace(); } } }
2)接收到服务器端数据,同样需要转换成客户端需要的数据格式,即:
先取出前面N位内容byte数组A转化成int类型值,该值用于判断消息类型,用于区分是哪个消息;
取出N+1位到M位byte数组B转化成int类型,该值用于设置消息包含的数据内容长度;
最后取出后续内容的byte数据,通过id找到对应的实体类型,在根据对应的parseFrom方法,解析byte数组数据。
参考代码如下:
/** * 接收消息 bytebuffer格式 * * @param bytes */ private void receiveMessage(ByteBuffer bytes){ //取出消息类型id byte[] cmdbyte = new byte[2]; bytes.get(cmdbyte,0,cmdbyte.length); short cmdid = DataConvertUtils.lBytesToShort(cmdbyte); //取出消息内容的长度 byte[] msglenbyte = new byte[4]; bytes.get(msglenbyte,0,msglenbyte.length); int msgLen = DataConvertUtils.lBytesToInt(msglenbyte); //取出消息内容 byte[] msgbyte = new byte[msgLen]; bytes.get(msgbyte,0,msgbyte.length); // AppLogUtil.logLocal(" cmdid ---> " + cmdid + ",msgLen ---> " + msgLen); //消息返回 switch (cmdid){ case (short)Songid.enSongCmdId.enReqResponse_VALUE: //如果是服务器请求延迟的心跳包,则直接返回给服务器 if(webSocketClient != null){ webSocketClient.send(bytes.array()); } break; case (short)Songid.enSongCmdId.enRetLogin_VALUE: //登录 notifyRetLogin(cmdid,msgbyte); break; } }
在数据转化过程中需要将int 和short类型进行转换成byte[] 及逆向转换,转换代码如下:
/** * 将基本数据类型转换为byte数组,以及反向转换的方法 * * Created on 2017/6/8. */ public class DataConvertUtils { /** * 将16位的short转换成byte数组 * * @param s * short * @return byte[] 长度为2 * */ public static byte[] shortToByteArray(short s) { byte[] b = new byte[2]; b[0] = (byte) (s & 0xff); b[1] = (byte) (s >> 8 & 0xff); return b; } /** * 低字节数组到short的转换 * @param b byte[] * @return short */ public static short lBytesToShort(byte[] b) { int s = 0; if (b[1] >= 0) { s = s + b[1]; } else { s = s + 256 + b[1]; } s = s * 256; if (b[0] >= 0) { s = s + b[0]; } else { s = s + 256 + b[0]; } short result = (short)s; return result; } /** * 将int类型的数据转换为byte数组 * 原理:将int数据中的四个byte取出,分别存储 * @param n int数据 * @return 生成的byte数组 */ public static byte[] intToBytes(int n){ byte[] b = new byte[4]; b[0] = (byte) (n & 0xff); b[1] = (byte) (n >> 8 & 0xff); b[2] = (byte) (n >> 16 & 0xff); b[3] = (byte) (n >> 24 & 0xff); return b; } /** * 将低字节数组转换为int * @param b byte[] * @return int */ public static int lBytesToInt(byte[] b) { int s = 0; for (int i = 0; i < 3; i++) { if (b[3-i] >= 0) { s = s + b[3-i]; } else { s = s + 256 + b[3-i]; } s = s * 256; } if (b[0] >= 0) { s = s + b[0]; } else { s = s + 256 + b[0]; } return s; } }
注:在转换中,有些时候可能高字节转换的,请注意区分!!!
另外,如需进行websocket断开重连,建议断开后延迟1秒在进行websocket重连,否则可能会导致,websocket断开操作未结束,新的websocket已建立,从而导致异常。
相关文章推荐
- Websocket Netty protobuf协作使用
- protobuf使用NDK编译Android的静态库(工作记录)
- CocoaAsyncSocket 网络通信使用之Protobuf安装(五)
- Android中基于protobuf的socket通信的实例
- android使用wire方式生成protobuf的Java文件
- Android上GTalk以及Push机制的XMPP数据选择使用protobuf格式而非XML格式
- android与PC,C#与Java 利用protobuf 进行无障碍通讯【Socket
- java(Android):windows系统 ProtoBuf(3.1.0)编译及使用
- CocoaAsyncSocket 网络通信使用之Protobuf安装(五)
- Android:ProtoBuf编译及使用
- Android与PC,C#与Java 利用protobuf 进行无障碍通讯【Socket】
- [转]Android上GTalk以及Push机制的XMPP数据选择使用protobuf格式而非XML格式
- Android下使用Protobuf进行序列化
- 使用protobuf和socket实现服务器间消息的传递
- android与PC,C#与Java 利用protobuf 进行无障碍通讯【Socket】
- iOS中protobuf和tcp长链接(AsyncSocket)scoket配合使用
- android与PC,C#与Java 利用protobuf 进行无障碍通讯【Socket】 推荐
- Android与PC,C#与Java 利用protobuf 进行无障碍通讯【Socket】
- Unity跨iOS、Android平台使用protobuf-net的方法
- ProtocolBuffers (二) android与PC,C#与Java 利用protobuf 进行无障碍通讯【Socket】