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

偶然得了一个谷米的车载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了解不多,可能好些地方说的不对,或者不好的地方,也请大家指出,也希望能对某些需要的人提供一些帮助,共同进步!

  

   成为一名优秀的程序员!

时间: 2024-10-17 14:40:29

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

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

1.MINA框架简介 MINA(Multipurpose Infrastructure for Network Applications)是用于开发高性能和高可用性的网络应用程序的基础框架.通过使用MINA框架可以可以省下处理底层I/O和线程并发等复杂工作,开发人员能够把更多的精力投入到业务设计和开发当中.MINA框架的应用比较广泛,应用的开源项目有Apache Directory.AsyncWeb.Apache Qpid.QuickFIX/J.Openfire.SubEthaSTMP.red5

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

1.Nginx是什么? Nginx就是反向代理服务器. 首先我们先来看看什么是代理服务器,代理服务器一般是指局域网内部的机器通过代理服务发送请求到互联网上的服务器,代理服务器一般作用于客户端.比如GoAgent,翻墙神器. 一个完整的代理请求过程为:客户端首先与代理服务器创建连接,然后根据代理服务器所使用的代理协议,请求对目标服务器创建连接.或则获得目标服务器的指定资源.Web代理服务器是网络的中间实体.代理位于Web客户端和Web服务器之间,扮演"中间人"的角色. HTTP的代理服务

mui APP与服务器之间的交互原理

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <title></title

Apache MINA框架整合到Spring框架中

毕业设计用到了Apache公司的MINA框架作为服务端与安卓客户端的通信框架. 问题:服务端分为两个部分,一个是基于javaEE平台的后台管理系统,另一个是基于MINA框架的服务,整个项目中需求是当tomcat服务器启动的时候,将MINA服务也一起启动起来,相当于服务端程序运行起来后,开启两个服务. 分析:服务端的后台管理系统是采用Spring+Hibernate框架整合搭建的,而当tomcat服务器启动后,会加载spring的配置文件,而spring配置文件中可以自定义监听器,将启动MINA服

Mina框架研究

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架. 这个框架的优点: – 异步 – 无阻塞 – 事件驱动 – 支持TCP, UDP, APR, 串口- – 通过 过滤器(Filters)实现扩展性 – 同时提供协议框架 总体框架 之前的一个项目用到了MINA,最近想再系统的整理一下,主要参考MINA 2.0 User

基于mina的的android即时通信app

前段时间研究mina框架,发现很适合做即时通信后台,经过几个月的研发本人开发了一套基于mina框架的android即时通信app,暂命名为E聊.鉴于本人能力有限还存在不少bug,希望大家原谅并指出,bug和部分功能还在不断完善中,过段时间部分代码会进行开源,敬请关注. 主要功能包括: 1.私聊,群聊 2.支持文字.语音.图片.文件.小视频等 3.位置共享(开发中) 4.朋友圈(图文发布.评论等,小视频功能正在开发中) 5.上线通知,断线重连 6.接收离线消息 7.更多功能正在筹划中... 演示图

MINA框架

MINA 框架简介 Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(然,也可以提供JAVA 对象的序列化服务.虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能.高扩展性的网络通信应用,Mina 提供了事件驱动.异步(Mina 的异步IO 默认使用的是JAVA NIO 作为底层支持)操作的编程模型.Mina 主要有1.x 和2.x 两个分支,这里我们讲解最新版本2.0,如果你使用的是Mina 1.x,那么

长连接神器Mina框架的使用

前段时间学习了mina框架的使用.它是基于Socket进行通信,所以说在项目中要是需要长连接的使用,那mina框架是一个不错的选择. 下面简单介绍一下mina框架的使用,学习mina框架不长时间,现在写下来即时为了记录一下自己的学习经历,又希望可以帮助其他初学者朋友,若有不足与错误之处,还请大神指教. 在使用mina框架之前需要下载所需的jar包.可以在我的网盘中下载,Android端也可以用的.地址:http://pan.baidu.com/s/1skP8YT3 ,提取码:inyl. 所需的j

Android Mina框架的学习笔记

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架.当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发.串口通讯程序(只在最新的预览版中提供),MINA 所支持的功能也在进一步的扩展中.目前正在使用 MINA 的软件包括有:Apache Directory Project.Asyn