刘 雍,孙 冰,马玉春
(海南热带海洋学院 a.海洋信息工程学院; b.海南省嵌入式系统重点实验室,海南 三亚 572022)
基于消息驱动的Android TCP服务器类的设计
刘 雍a,孙 冰a,马玉春b
(海南热带海洋学院 a.海洋信息工程学院; b.海南省嵌入式系统重点实验室,海南 三亚 572022)
网络应用日趋广泛,本文所提出的Android TCP服务器模型及实现的TcpServer类通过多线程类来处理远程客户机的连接请求和数据接收,设置了多种消息用来通知用户连接是否成功并提交接收到的数据,以及数据收发过程中是否出现错误等.该TcpServer类在工程项目中得到了应用,运行可靠,提高了开发效率.
安卓(Android); 传输控制协议; 服务器; 多线程
安卓(Android)作为移动设备的开放源码操作环境与平台,在移动互联网发展迅速的今天,不断扩展网络应用是其开发的主要方向.目前,Android网络编程能够实现信息实时交互、在线存储,还可以实现电子商务、实时监控与移动办公等.文献[1]给出了各种网络编程的初步方案,特别是传输控制协议(TCP:Transmission Control Protocol)的Socket编程.文献[2]实现了微软.NET框架下的基于事件驱动的TcpServer类,侦听远程客户机的连接与数据接收都通过多线程在后台进行,连接成功或收到数据均通过事件提交用户,节约了系统资源,同时加强了数据接收和发送的处理功能.本文在文献[1]的基础之上,结合文献[2]的原理,设计并实现一个Android平台下的TcpServer类,使其支持消息驱动和便捷的数据接收与发送等功能.
Android中引入Handler作为消息传递机制,Handler可以充当子线程与主线程的中介,主要接收子线程发来的数据,并以此数据配合主线程更新界面(UI)控件显示的内容.当App启动时,Android首先会开启一个主线程(即UI线程),主线程为管理界面中的UI控件,对各控件进行事件分发.如果需要执行一个耗时的操作,如:读写文件和实时数据传输等.如果将这些操作放在主线程中,就会导致App崩溃,因而,这些操作应该放在子线程中.但是,子线程中的数据又需要到主线程的UI中进行更新,Android运行机制不允许在子线程中直接更新主线程中的数据.本文考虑借助Handler消息传递机制来解决这个问题.由于Handler对象运行在主线程(UI),同其子线程之间可以通过Message对象来传递数据,这样就可以比较流畅地更新主线程中的数据[3].
在实时监控系统中,可以在初始化Handler对象时定义回调方法,用来接收和处理子线程发送的消息,也可使用Handler对象的postDelayed方法执行定时查询功能,以便及时做出处理[4].本文所设计的TcpServer类通过主线程的Handler对象向主线程发送连接成功和关闭等事件消息和接收到的数据消息.
TcpServer的内部工作原理模型如图1所示.首先通过构造函数进行初始化,主线程向构造函数传送 Handler对象和侦听接口等参数,然后在多线程中创建ServerSocket对象,调用该对象的accept方法侦听端口.连接失败,则发送连接失败消息;连接成功,则启动新的线程读取数据,如果收到数据,则发送数据接收到的消息,供主线程调用.TcpServer初始化并与远程客户机连接,成功之后还可以直接供主线程调用,用来发送数据[5].
图1 TcpServer 内部工作原理模型
端口侦听通过多线程嵌入类Th_Accept实现,其中run方法中的关键代码为
try{
_ss = new ServerSocket(_nPort);
_connection = _ss.accept();
_bis = new BufferedInputStream(_connection.getInputStream());
_bos = new BufferedOutputStream(_connection.getOutputStream());
_bConnected = true; //缺省为false
} catch (UnknownHostException e) {
} catch (IOException e) {}
if (_bConnected){
_handler.obtainMessage(MESSAGE_Connected, 0, 0,null).sendToTarget();
th_ReadData = new Th_ReadData();
th_ReadData.start();
}
else
_handler.obtainMessage(MESSAGE_ConnectError, 0, 0,null).sendToTarget();
传入端口号生成ServerSocket对象_ss,调用_ss对象的accept方法等候远程客户机的连接请求,此时多线程处于阻塞状态.连接成功后,生成BufferedInputStream对象_bis,用于后续的数据读取(接收),再生成BufferedOutputStream对象_bos,用于后续的数据发送.如果以上各项都取得成功,则将连接状态参数_bConnected修改为true(否则,保持false不变).
如果连接成功,则Handler对象_handler通过obtainMessage方法获得连接成功消息,并通过消息的sendToTarget方法发送到主线程,随后启动读取数据的多线程Th_ReadData对象;如果连接失败,则向主线程发送连接失败的消息.
Th_ReadData也是一个多线程嵌入类,用于在后台读取数据,run方法中为一个读取数据的死循环,其关键代码为:
while(true){
if(_bConnected != true) return;
if(_bStopConnect) return;
try {
if(_bis.available() > 0){
ByteBuffer byteBuf = readByteBuffer();
if(_bConnected != true) return; //be necessary!!
_handler.obtainMessage(MESSAGE_DataArrived, 0, 0, byteBuf).sendToTarget();
}
} catch (IOException e) {}
}
如果连接状态_bConnected不为true,或者主线程主动终止连接导致_bStopConnect为true,则退出死循环.否则,尝试接收数据,假如_bis对象的可用数据大于0,则读取数据存入byteBuf对象,并向主线程发送数据到达消息,同时携带所接收的数据(存于byteBuf).如果在读取数据的时候发生错误,也可在catch代码模块中添加发送读取数据错误消息.
上一节Th_ReadData类中调用的readByteBuffer方法是TcpServer类中的私有方法,其定义如下所示.首先分配一个1024个字节的缓冲区,用ByteBuffer对象buf来保存数据,然后记录当前时间lStart,通过while循环将延迟在_nDelay(一般定义为50毫秒)内的数据进行合并,最后作为一个数据包返回.如果在读取数据包的过程中发生错误,还可以在catch代码模块中添加读取数据错误消息.
private ByteBuffer readByteBuffer(){
ByteBuffer buf = ByteBuffer.allocate(1024);
long lStart = System.currentTimeMillis();
while(System.currentTimeMillis() - lStart < _nDelay){
try{
while(_bis.available()>0){
byte[]bIn = new byte[_bis.available()];
_bis.read(bIn, 0, _bis.available());
buf.put(bIn);
}
}catch (IOException e) {}
}
return buf;
}
发送数据通过sendBytes方法直接调用,因而为公有方法,参数即为需要发送的字节数组,通过BufferedOutputStream对象_bos的write方法进行发送,第一个参数为所需要发送的字节数组,第二个参数为偏移量,这里指从第0个字节开始发送,第三个参数为需要发送的字节数.同理,如果在发送数据包的过程中发生错误,还可以在catch代码模块中添加发送数据错误消息.
public void sendBytes(byte[]bOut){
try {
_bos.write(bOut, 0, bOut.length);
_bos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
TcpServer类中还包括直接发送与接收文本字符串的方法,可以很方便地处理POP3协议之类的纯文本协议[4].处理文本字符串的方法与处理字节流的方法类似,这里不再赘述.
文献[2]中的I-7013D温度采集模块采用RS-485接口,可以监视1路温度量的变化.为了让学生进行扩展学习,实现了一个Visual Basic 2010 版的I-7013D模块仿真软件.利用本文的TcpServer类,进一步实现了Android版的支持TCP协议的I-7013D模块仿真软件,方法是在UI主线程中定义一个嵌入类IncomingHandlerCallback,实现处理消息的接口方法handleMessage,其代码如下所示.
class IncomingHandlerCallback implements Handler.Callback{
public boolean handleMessage(Message msg) {
processMessage(msg);
return true;
}
}
processMessage方法用来处理各种消息,一般使用switch语句对消息进行分类处理,为了方便,这里仅用条件语句来处理数据到达消息,判断数据到达,则将消息中的数据对象进行强制转换,然后即可进行随后的处理.
private void processMessage(Message msg){
if(msg.what == TcpServer.MESSAGE_DataArrived){
ByteBuffer byteBuf = (ByteBuffer)msg.obj;
// 进一步处理数据
}
}
在UI主线程的onCreate方法中,对Handler和TcpServer进行初始化,即可等待远程客户机的连接,连接成功后响应其查询命令.
mHandler = new Handler(new IncomingHandlerCallback());
server = new TcpServer(mHandler, nPort);
图2所示为Android版I-7013D主控软件运行效果,能完整地实时取得运行TcpServer对象的I-7013D仿真模块中的数据,分别通过文本框(当前温度31.79度)和绘图方式进行展示.
图2 I-7013D主控机软件运行
Android网络应用广泛.本文所提出的Android TCP服务器模型及实现的TcpServer原型类可以在后台处理远程客户机的连接请求和接收数据,设置了多种消息用来通知用户连接是否成功,提交接收到的数据,以及数据收发过程中是否出现错误等.由于通过多线程处理耗时工作,TcpServer类不会引起App崩溃,且占用资源少.利用TcpServer类研发的支持Tcp协议的Android版I-7013D模块仿真软件,可以与对应的主控机软件无缝连接,运行可靠[6].
[1]陈文,郭依正.深入理解Android网络编程[M].北京:机械工业出版社, 2013.
[2]马玉春.计算机监控系统的仿真开发[M].北京:国防工业出版社,2015.
[3]陈伟.Android Handler消息传递机制在人机界面软件设计中的应用[J].科技信息,2014, 13 (6):90-92.
[4]刘雍.基于ARM9 的嵌入式视频监控系统的设计与实现[J].琼州学院学报,2015, 22 (2):35-38.
[5]杨宗德,吕光宏,刘雍.Linux高级程序设计 [M].北京:人民邮电出版社,2012.
[6]文松,王敏,胡春阳, 等.一种用于移动设备的Web 服务远程证明方法[EB/OL].(2016-11-29)[2016-11-29].http://www.cnki.net/kcms/detail/46.1071.G4.20161129.1713.005.html.
(编校:曾福庚)
Design of Message Driven TCP Server on Android Platform
LIU Yonga, SUN Binga,MA Yu-chunb
(a.School of Ocean Information Engineering; b.Hainan Key Laboratory of Embedded Systems,Hainan Tropic Ocean University, Sanya Hainan 572022, China)
Android network application is becoming more and more extensive.In this paper, on Android platform a message driven TCP server model is established and implemented, in which the connection request from remote client and data receiving is handled by embedded Thread class, with connection establishment and data receiving functioning as messages to main Thread running UI.Prototype class TCP Server has been applied in real project and running reliably.
Android; transmission control protocol; server; thread
格式:刘雍,孙冰,马玉春.基于消息驱动的Android TCP服务器类的设计[J].海南热带海洋学院学报,2017,24(2):59-63.
2017-02-16
刘雍(1979 - ),女,四川阆中人, 海南热带海洋学院海洋信息工程学院副教授,硕士,研究方向为移动应用与嵌入式系统.
马玉春(1969-),男,江苏南京人, 海南热带海洋学院海洋信息工程学院教授,博士,研究方向为移动应用与计算机监控技术.
TP311.52
A
2096-3122(2017) 02-0059-05
10.13307/j.issn.2096-3122.2017.02.12