当前位置:   article > 正文

Android Websocket+protobuf使用_android websocket + proto

android websocket + proto

前言:

环境使用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对象,并建立连接:

  1. webSocketClient = new WebSocketClient(URI.create(address),new Draft_17()) {
  2. @Override
  3. public void onOpen(ServerHandshake serverHandshake) {
  4. AppLogUtil.logLocal("onOpen ----->>>> websocke connect success");
  5. }
  6. @Override
  7. public void onMessage(String s) {
  8. AppLogUtil.logLocal("onMessage : " + s);
  9. }
  10. @Override
  11. public void onMessage(ByteBuffer bytes) {
  12. super.onMessage(bytes);
  13. // AppLogUtil.logLocal("onMessage : " + bytes);
  14. }
  15. @Override
  16. public void onClose(int i, String s, boolean b) {
  17. AppLogUtil.logLocal("onClose ----->>>> websocket close .i = "+i+",s = "+s);
  18. }
  19. @Override
  20. public void onError(Exception e) {
  21. AppLogUtil.logLocal("onError ----->>>> websocket onError");
  22. }
  23. };
  24. webSocketClient.connect();

注:websocket连接address格式是以ws:// 做开头的,例如:ws//192.168.1.1:8080。



2.关闭websocket方式

  1. /**
  2. * app 退出时,关闭socket
  3. */
  4. public void closeWebSocket(){
  5. if (webSocketClient != null && webSocketClient.getConnection().isOpen()){
  6. AppLogUtil.logLocal("closeWebSocket");
  7. webSocketClient.close();
  8. webSocketClient = null;
  9. }
  10. }

以上两步就是websocket建立打开及关闭的方式。


3.最重要的一步,通信(客户端与服务器端的通信),通信数据格式通过byte传递,一般要与服务器端进行约定数据格式

传输数据格式如下:

1) client     ----->  server数据转化,

先将protobuf 对象进行toByteArray()转化成byte数组;

将byte数组长度int类型转化成byte数组  A;

定义的protobuf消息类型的id,int类型转化成byte数组  B

最后使用ByteArrayOutputStream,依次写入 B ,A ,及msg,然后传给服务器处理。参考如下代码

  1. /**
  2. * 发送protobuf 格式转换
  3. *
  4. * @param cmdid
  5. * @param msg
  6. */
  7. private void sendMessage(Songid.enSongCmdId cmdid, byte[] msg){
  8. if(webSocketClient != null && webSocketClient.getConnection().isOpen()){
  9. try {
  10. //消息id
  11. short id = (short) cmdid.getNumber();
  12. //消息id short转byte数组
  13. byte[] ba_cmdid = DataConvertUtils.shortToByteArray(id);
  14. //转成byte后数组的长度
  15. int cid_len = ba_cmdid.length;
  16. //消息内容protobuf 数据结构转byte数组后的长度
  17. int msg_len = msg.length;
  18. //消息长度转byte数组
  19. byte[] ba_msglen = DataConvertUtils.intToBytes(msg_len);
  20. //转成byte数组后的长度
  21. int len2 = ba_msglen.length;
  22. //依次写入bytearray中
  23. ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  24. outputStream.write(ba_cmdid);
  25. outputStream.write(ba_msglen);
  26. outputStream.write(msg);
  27. beginSendTime = System.currentTimeMillis();
  28. webSocketClient.send(outputStream.toByteArray());
  29. outputStream.close();
  30. }catch (Exception ex){
  31. ex.printStackTrace();
  32. }
  33. }
  34. }

2)接收到服务器端数据,同样需要转换成客户端需要的数据格式,即:

先取出前面N位内容byte数组A转化成int类型值,该值用于判断消息类型,用于区分是哪个消息;

取出N+1位到M位byte数组B转化成int类型,该值用于设置消息包含的数据内容长度;

最后取出后续内容的byte数据,通过id找到对应的实体类型,在根据对应的parseFrom方法,解析byte数组数据。

参考代码如下:

  1. /**
  2. * 接收消息 bytebuffer格式
  3. *
  4. * @param bytes
  5. */
  6. private void receiveMessage(ByteBuffer bytes){
  7. //取出消息类型id
  8. byte[] cmdbyte = new byte[2];
  9. bytes.get(cmdbyte,0,cmdbyte.length);
  10. short cmdid = DataConvertUtils.lBytesToShort(cmdbyte);
  11. //取出消息内容的长度
  12. byte[] msglenbyte = new byte[4];
  13. bytes.get(msglenbyte,0,msglenbyte.length);
  14. int msgLen = DataConvertUtils.lBytesToInt(msglenbyte);
  15. //取出消息内容
  16. byte[] msgbyte = new byte[msgLen];
  17. bytes.get(msgbyte,0,msgbyte.length);
  18. // AppLogUtil.logLocal(" cmdid ---> " + cmdid + ",msgLen ---> " + msgLen);
  19. //消息返回
  20. switch (cmdid){
  21. case (short)Songid.enSongCmdId.enReqResponse_VALUE:
  22. //如果是服务器请求延迟的心跳包,则直接返回给服务器
  23. if(webSocketClient != null){
  24. webSocketClient.send(bytes.array());
  25. }
  26. break;
  27. case (short)Songid.enSongCmdId.enRetLogin_VALUE: //登录
  28. notifyRetLogin(cmdid,msgbyte);
  29. break;
  30. }
  31. }

在数据转化过程中需要将int 和short类型进行转换成byte[] 及逆向转换,转换代码如下:

  1. /**
  2. * 将基本数据类型转换为byte数组,以及反向转换的方法
  3. *
  4. * Created on 2017/6/8.
  5. */
  6. public class DataConvertUtils {
  7. /**
  8. * 将16位的short转换成byte数组
  9. *
  10. * @param s
  11. * short
  12. * @return byte[] 长度为2
  13. * */
  14. public static byte[] shortToByteArray(short s) {
  15. byte[] b = new byte[2];
  16. b[0] = (byte) (s & 0xff);
  17. b[1] = (byte) (s >> 8 & 0xff);
  18. return b;
  19. }
  20. /**
  21. * 低字节数组到short的转换
  22. * @param b byte[]
  23. * @return short
  24. */
  25. public static short lBytesToShort(byte[] b) {
  26. int s = 0;
  27. if (b[1] >= 0) {
  28. s = s + b[1];
  29. } else {
  30. s = s + 256 + b[1];
  31. }
  32. s = s * 256;
  33. if (b[0] >= 0) {
  34. s = s + b[0];
  35. } else {
  36. s = s + 256 + b[0];
  37. }
  38. short result = (short)s;
  39. return result;
  40. }
  41. /**
  42. * 将int类型的数据转换为byte数组
  43. * 原理:将int数据中的四个byte取出,分别存储
  44. * @param n int数据
  45. * @return 生成的byte数组
  46. */
  47. public static byte[] intToBytes(int n){
  48. byte[] b = new byte[4];
  49. b[0] = (byte) (n & 0xff);
  50. b[1] = (byte) (n >> 8 & 0xff);
  51. b[2] = (byte) (n >> 16 & 0xff);
  52. b[3] = (byte) (n >> 24 & 0xff);
  53. return b;
  54. }
  55. /**
  56. * 将低字节数组转换为int
  57. * @param b byte[]
  58. * @return int
  59. */
  60. public static int lBytesToInt(byte[] b) {
  61. int s = 0;
  62. for (int i = 0; i < 3; i++) {
  63. if (b[3-i] >= 0) {
  64. s = s + b[3-i];
  65. } else {
  66. s = s + 256 + b[3-i];
  67. }
  68. s = s * 256;
  69. }
  70. if (b[0] >= 0) {
  71. s = s + b[0];
  72. } else {
  73. s = s + 256 + b[0];
  74. }
  75. return s;
  76. }
  77. }


注:在转换中,有些时候可能高字节转换的,请注意区分!!!


另外,如需进行websocket断开重连,建议断开后延迟1秒在进行websocket重连,否则可能会导致,websocket断开操作未结束,新的websocket已建立,从而导致异常。


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/1021592
推荐阅读
相关标签
  

闽ICP备14008679号