偶然得了一个谷米的车载GPS设备(gt02d),做为程序员的我,开始躁动了:想着做一个服务器程序,记录GPS设备上传的坐标,然后在地图上绘制每天的轨迹。。。想想还是挺有意思的(其实前两年还有一个失败的经历,自己还弄了一个"TA在哪儿"的Android版本的程序,就是登录后,每1分钟通过Http上传坐标,这样你的好友就可以看到你在哪儿,还专门让老婆坐公交,我骑车测试,结果反应太慢了。后来,没有了,再后来,好些软件就有位置共享的功能了,哈哈。。。),只不过,轨迹大部分时间还是三点一线(宿舍,公司,球场)。再加上,前段时间我们的订餐系统使用了superwebsocket框架做为服务器和APP通信的媒介,但是不是特别稳定(也有可能是我们的程序有问题),经常出现无法链接的问题,可能主要的原因还是superwebsocket以IIS为宿主,而应用程序池会回收,并且这个回收很多时间不可控,这时服务器和APP通信被中断了,回收后,程序池启动时,会重新建立“通信通道”,但从我们的实践中,会有经常出现无法建立的情况,并且不知道什么时间会出现建立失败的情况,这个很可怕。于是,三天两头接到电话说APP登录不了,后来我只能索性让客户自己重起服务器,有时每天要重起好几次,所以,他们也烦了!于是,我的日子就不好过了!也正好借些机会了解下一些通信的内容。

只有.jar?jd-gui来开路

  前几年,我们为杭州一个外卖网,开发订单调度系统时,当时,他们就是给每个配送员的电瓶车,安装了一个GPS设备,系统中配送员对应一个GPS设备的imei,并提供了一个服务器程序(不过只有发布后的gar文件,直接通过命令运行),这样就可以根据配送员的位置,调度订单给他们了(如图1)。

                                                   (图1)

  前几年,智能手机在配送员中还不是特别普及,这确实是一个不错的方案,虽然现在多数都用智能手机了,我们的客户后来基本也都是直接用手机上传坐标了,但是手机用电就消耗得快很多了。原本,以为把当年的程序拿过来部署下就可以了,结果呢?一直不上传坐标,发送短信指定,设备也是正常回复,定位也成功了,端口也是被“占用”,直接:netstat -ano|findstr "8889",显示如图2,说明正常。最后,来回问了他们好几个客服和技术,才了解到,他们的设备只支持TCP,不支持UDP,几年前测试时,就是用的UDP协议上传坐标的,当时还因为他们提供的文档说,UDP暂不支持,结果当时只能用UDP,所以印象十分深刻,现在怎么突然不支持了。没办法,只能看能不能反编译,修改下代码,鄙人在学校是学了2年java,但是上班后,只是偶尔客串下Android的开发,心里还是十分没底。

                        (图2)

  乘着这股躁动,说干就干,先是下载了jd-gui,打开gar文件,一看代码,代码不多,也是很文明的,心中也踏实了许多!然后,Save All Sources,随后,用eclipse新建一个项目,导入源码,只有几个地方有点小错误,修改下,直接编译通过了,这个又是朝着“胜利之门”前进了一大步。唯一不好的地方就是,反编译的代码,每行前面总有一些注释,虽不影响生成,但是看着还是纠心,主要鄙人对代码的格式很在意,一一删除了几行,才想起可以用正则表达式替换,正则表达式真是个好东西,谁用谁知道!下面是替换前后的对比,两个正则表达式为:  /\* [\s]* \*/  (替换中间为空的行),/\* [0-9]* \*/(替换中间是数字的)。

          

                            (替换前)                                                                                                     (替换后)

程序未动,协议文档先行

  向设备技术要了协议文档(他们客服很拽,一听说要自己开发平台,就基本不理人了,说是他们的利益都来自至说平台,你自己做平台了,影响他们利益了,还好硬件技术还是很好说话,要不怎么说程序员都是好人呢,时间都用在技术上了,哪还有时间使坏心眼哦),细读了文档,基本就是3个交互,其他包我没用上,也就没写了:

  1,登录,设备与服务器建立链接后,发送登录包,服务器必须根据协议回复相应数据即可,登录包中含设备编号,8个字节,这样后面的定位包中就不需要传设备编号了,原来的设备是每个定位包中都含设备编号,也许是太耗流量了,才修改成现在的模式了;

  2,心跳,设备登录后,会间隔几分钟,发送心跳包,确认链接正常,服务器必须正确响应数据(登录后,第一个包会是心跳包);

  3,定位包,登录,心跳包正常后,设备开始发送位置信息包。

数据流程图如下,流程还是比较清晰的。

  

                                              (数据流程图)

  

分析代码,学习mina

  打开main.java,看到引用 mina-core-2.0.4.jar.XXX。再百度mina:

   Apache MINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可靠性的网络应用程序。它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的事件驱动的异步API。

原来是基于此框架建立的通信,再看代码中 NioDatagramAcceptor acceptor = new NioDatagramAcceptor(); 才知道这个表示建立了UDP协议的通信。于是,再查了些资料,把代码修改成建立TCP协议的通信,运行后,再查看端口占用情况,已经是TCP类型了,如下图,再发短信指定设置设备,用IPAnalyse抓包,看到已经能正常上传数据包了,于是,再一次的前进了一大步。

  

  调试程序,第一步肯定是把日志功能调通,在学校时,写java程序时,日志也是用的log4j,当时,只是一句代码 PropertyConfigurator.configure("log4j.properties") 就ok了,还好一下子找到了在学校时写的代码(6,7年了,真不容易呀),把log4j.properties放到特定目录测试,一下子就ok了。当然,这个东西网上一搜,肯定是一箩筐,但是现在好些都是:天下文章一大抄,粘贴复制加剪刀。抄没有问题,但是至少得验证是否正确吧。

  走到这里时,躁动开始让人异常兴奋,失去了程序员应有的冷静。于是,胡乱的调试,胡乱的输入日志,陪上一个周末,10天的“晚自习”,依然是毫无近展,在几近放弃的时候,当然,这时躁动也基本变成了平静,才知道要冷静,回头再仔细分析下MINA的消息流程,原来MINA使用的是异步机制,而程序中也是用了一个线程来处理消息,所以之前通过单步跟踪,或者输入日志的方式自己确认的消息流程错误的,当时以为是:设备->encoder->decoder->设备,所以一直调试不通,后来查了相关资料才了解正确的消息流程,如下图,其实最重要的部分,也是之前一直没有重视的部分就是在 messageReceived 方法中,把 request 转成 respose,当然,request 有很多,如登录、心跳、定位包,所以不能强制转化。

  

  

  理清流程后,真有点“拨开云雾见月明”的感觉,再按开发文档 Decoder 登录包,转成回复包,Encoder 回复包 ,一切都变得顺理成章了。登录包,及回复协议如下图     

 

  下面,附上基本流程的代码,没有什么技术含量的,代码可能也比较丑(在对一个数组赋值时,没注意,全是下标为0的元素赋值,结果错误校验位一直不对,浪费好多时间呀),请务见怪哈。

  

 1 public class Decoder extends CumulativeProtocolDecoder
 2  {
 3      private static final Logger log = Logger.getLogger(Decoder.class);
 4     public boolean doDecode(IoSession session, IoBuffer buffer,
 5             ProtocolDecoderOutput output) {
 6         try {
 7
 8             log.info("Decoder:" + buffer.toString());
 9
10             byte[] head = new byte[2];
11             buffer.get(head, 0, 2);
12             if (head.length < 2 || head[0] != 120) {
13                 return false;
14             }
15
16             //長度和協議號
17             byte[] lenbyte = new byte[2];
18             buffer.get(lenbyte,0,2);
19
20             byte proto =  lenbyte[1];
21
22             log.info("Decoder.body:" + buffer.toString());
23
24             System.out.println("proto:" +proto);
25
26             Message msg;
27             switch (proto) {
28             case 1://登录包
29                 {
30                     byte[] body = new byte[18];
31                     buffer.get(body);
32
33                     msg =new Login();
34                     byte[] crc = new byte[4];
35                     byte[] loginbody = new byte[8];
36                     loginbody[0] = crc[0] = 0x05;
37                     loginbody[1] = crc[1]= 0x01;
38                     loginbody[2] = crc[2]= body[12];
39                     loginbody[3] = crc[3]= body[13];
40                     //协议体中从“包长度”到“信息序列号”(包括“包长度”、“信息序列号”)这部分数据的 CRC-ITU 值。
41                     CRC16Util crc16 = new CRC16Util();
42
43                     crc16.reset();
44                     crc16.update(crc);
45
46                     byte[] crcresult = Byte2Hex.short2bytes((short)crc16.getCrcValue());
47                     loginbody[4] = crcresult[0];
48                     loginbody[5] = crcresult[1];
49                     //停止位(2位) 0x0D 0x0A
50                     loginbody[6] = 0x0D;
51                     loginbody[7] = 0x0A;
52
53                     msg.setHeadBuf(head);
54                     msg.setBodyBuf(loginbody);
55
56                     msg.fromHead(head);
57                     msg.fromBody(loginbody);
58                     //获取终端编号
59                     String termid = Byte2Hex.Bytes2HexString(body);
60                     termid = termid.substring(1,16);
61
62                     System.out.println("termid:" +termid);
63                     session.setAttribute("termid", termid);
64
65                     msg.setSession(session);
66                     output.write(msg);
67                     return true;
68                 }
69             default:
70                 return false;
71             }
72             return true;
73
74         } catch (Exception e) {
75             System.out.println("decode error:" + e.toString());
76         }
77         return false;
78     }
79  }

Decoder

 1     public Main() throws IOException {
 2         Config.init();
 3
 4         IoAcceptor acceptor = new NioSocketAcceptor();
 5         acceptor.getFilterChain().addLast("logger", new LoggingFilter());
 6         acceptor.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new CodecFactory()));
 7         acceptor.setHandler(this);
 8         acceptor.getSessionConfig().setReadBufferSize(2048);
 9         acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
10         acceptor.bind(new InetSocketAddress(Config.PORT));
11
12         PropertyConfigurator.configure("D:/javaworkspace/gt02/libs/log4j.properties");
13         //PropertyConfigurator.configure("log4j.properties");
14
15         this.worker = new Worker();
16         this.worker.start();
17
18     }
19
20     public void messageReceived(IoSession session, Object message)
21             throws Exception {
22         if ((message instanceof Message))
23         {
24             this.worker.addRecvMsg((Message) message);
25         }
26
27     }

Main

 1 public void run() {
 2         super.run();
 3         while (true)
 4             try {
 5                 Message msg = waitForProcessRecvMsg();
 6                 Message ret = msg.process();
 7
 8                 IoSession session = msg.getSession();
 9                  if (ret != null)
10                  {
11                      ret.head = msg.head; //设置要回复的内容,写到respost中
12                      ret.body = msg.body;
13                      session.write(ret);
14                 }
15
16             } catch (Exception e) {
17                 System.out.println("Worker Exception:" + e.getMessage());
18             }
19     }

worker

 1 public class Encoder extends ProtocolEncoderAdapter {
 2      private static final Logger log = Logger.getLogger(Encoder.class);
 3
 4     public void encode(IoSession session, Object message,
 5             ProtocolEncoderOutput output) {
 6         try {
 7
 8             Message msg = (Message) message;
 9             msg.toHead();
10             msg.toBody();
11
12             IoBuffer buffer = IoBuffer.allocate(msg.getLength(),false).setAutoExpand(true);
13
14             buffer.put(msg.getHeadBuf());
15             if (msg.getBodyBuf() != null) {
16                 buffer.put(msg.getBodyBuf());
17             }
18
19             buffer.flip();
20             log.info("Encoder.buffer:" + buffer.toString());
21
22             output.write(buffer);
23         } catch (Exception e) {
24             System.out.println("encode error:" + e.toString());
25         }
26     }
27 }

Encoder

  

大功告成

  正确回复登录包后,设备会发送一个心跳包,操作基本同登录包,回复的包只要修改下协议号即可。正确响应心跳包后,开始上传定位数据,这个数据包就包含了,经度、纬度、速度、航向等信息。还好开发文档中提供了解析的代码,虽没有什么难度,就是比较繁琐。解析出信息后,再通过一个Tttp请求加到数据库,这个流程就基本完成了。其实在漫长的摸索过程中,一直在想,完成时我会有多兴奋。但是,当真正看到控制台输出坐标信息时,不是兴奋,而是心中顿觉踏实了,想着晚上终于可以安心的睡一个好觉了。当时,就是看到的就是下图,第一个正常的定位信息,当时还特意截图,主要是为了在媳妇面前邀功,哈哈。

   

结语

  当时,只是一时兴起,然后变成欲罢不能。也许这就是程序员吧。也许这个东西对好些人来说,不值一提,但对我还是有点意义,还是有好些东西值得思考,我还是要感谢我家媳妇的鼓励。下一步,可能就是把这个应用到我们订餐系统与App通信上面了。鄙人对java了解不多,可能好些地方说的不对,或者不好的地方,也请大家指出,也希望能对某些需要的人提供一些帮助,共同进步!

  

   成为一名优秀的程序员!

基于mina框架的GPS设备与服务器之间的交互相关推荐

  1. Mui --- app与服务器之间的交互原理、mui ajax使用

    1.APP与服务器之间的交互原理app端(客户端)与服务端的交互其实理解起来和容易,客户端想服务器端发送请求,服务器端进行数据运算后返回最终结果.结果可以是多种格式: 1.text 文本格式 2.xm ...

  2. 基于MINA框架快速开发网络应用程序

    1.MINA框架简介  MINA(Multipurpose Infrastructure for Network Applications)是用于开发高性能和高可用性的网络应用程序的基础框架.通过使用 ...

  3. nginx做反向代理和后端web服务器之间的交互

    1.Nginx是什么? Nginx就是反向代理服务器. 首先我们先来看看什么是代理服务器,代理服务器一般是指局域网内部的机器通过代理服务发送请求到互联网上的服务器,代理服务器一般作用于客户端.比如Go ...

  4. 应用终端和服务器之间的交互 简单,基于UM220的北斗数据接收处理及简单应用

    索菲 摘 要:文章以北斗2号和GPS双系统接收模块UM220为硬件平台,利用嵌入式C#二次开发BD2/GPS-OEM板的方法,开发出了能同时运行于PC和Windows CE的跨平台程序,实现了PDA与 ...

  5. 游戏服务器Mina框架开发

    游戏服务器Mina框架开发 作者:老九-技术大黍 社交:知乎 公众号:老九学堂(新人有惊喜) 特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权 前言 如果要使用Java语言来开发游戏 ...

  6. ioGame 网络游戏服务器框架 (java)、java游戏服务器、netty 集群分步式的网络游戏服务器

    ioGame 国内首个基于蚂蚁金服 SOFABolt 的 java 网络游戏服务器框架:无锁异步化.事件驱动的架构设计 通过 ioGame 可以很容易的搭建出一个集群无中心节点.有状态多进程的分步式游 ...

  7. 基于ATmega16单片机和GPS的多用途定位仪设计

    目录 摘要 I Abstract II 第1章绪论 1 1.1 课题研究的背景及意义 1 1.2国内外研究现状 1 1.2.1 外国研究发展现状 1 1.2.2国内研究发展现状 2 1.3课题研究技术 ...

  8. 网络云存储技术Windows server 2012 (项目二十 一 基于Cluster的高可用企业WEB服务器的部署)

    网络云存储技术Windows server 2012 (项目二十一 基于Cluster的高可用企业WEB服务器的部署) 前言 网络存储技术,是以互联网为载体实现数据的传输与存储,它采用面向网络的存储体 ...

  9. go+vue——基于gin框架和gorm的web开发实战

    go+vue--基于gin框架和gorm的web开发实战 gin框架 视频.资料.笔记 安装Go环境, 添加环境变量(可能自动添加好) 下载 Go 环境变量 goland 报错: GOROOT is ...

  10. 基于PHP+小程序(MINA框架)+Mysql数据库的校园维修报修小程序系统设计与实现

    项目背景和意义 目的:本课题主要目标是设计并能够实现一个校园维修报修的微信小程序系统,前台用户使用小程序查找维修人员并提交预约维修,后台管理使用基于PHP+MySql的B/S架构:通过后台添加资讯.录 ...

最新文章

  1. 第二个冲刺期的第七天
  2. ReactiveCocoa
  3. html5怎么实现自动缩放图片,如图,html5开发的手机端web在线客服聊天,如何实现图片点击放大,捏合缩放功能?...
  4. python svm算法smo cifar_使用smo算法编写svm对CIFAR-10数据分类
  5. 图像“颜色选择”怎么用?
  6. mysql半同步复制实现
  7. linux -组管理和权限管理
  8. ICCV 2019 | 北邮提出高阶注意力模型,大幅改进行人重识别SOTA精度
  9. JS学习总结(10)——Windows对象
  10. csdn广告屏蔽方法
  11. python 中文排序(按拼音)
  12. Mac 新手必看:认识 Mac 桌面
  13. 摄像头参数详细介绍[转]
  14. Linux WIFI 驱动
  15. 文本编辑器EditPlus
  16. 静态背景下运动目标检测
  17. 玩转你的开发板-1.4.第1季第4部分-朱有鹏-专题视频课程
  18. jQuery mobile插件基础知识笔记
  19. SQL学习七、聚合函数
  20. android 控制空调,基于Android的多功能空调控制系统设计

热门文章

  1. 计算机用户名取名,重命名电脑用户名
  2. 央掌世界精品赛事《凰金决》全球赛事新闻发布会圆满举行
  3. MATLAB图像分割——检测交通视频中的汽车目标
  4. Feedsky尝试Feed点击付费广告
  5. VScode Latex 拼写检查
  6. 【虚拟机\UBunTu】E45: 已设定选项 ‘readonly‘ (请加 ! 强制执行)
  7. 怎么把foxmail的邮件和服务器同步,foxmail设置ActiveSync同步功能怎么操作?设置ActiveSync同步功能方法介绍...
  8. 计算机系统时间显示不准确的原因,电脑时间总是不对原因 电脑时间总是不对三种解决方案...
  9. 计算机控制系统总线,计算机控制系统功能之现场总线控制-电脑自学网
  10. 关于yaml的基本写法