劉 雍,孫 冰,馬玉春
(海南熱帶海洋學(xué)院 a.海洋信息工程學(xué)院; b.海南省嵌入式系統(tǒng)重點(diǎn)實(shí)驗(yàn)室,海南 三亞 572022)
基于消息驅(qū)動的Android TCP服務(wù)器類的設(shè)計(jì)
劉 雍a,孫 冰a,馬玉春b
(海南熱帶海洋學(xué)院 a.海洋信息工程學(xué)院; b.海南省嵌入式系統(tǒng)重點(diǎn)實(shí)驗(yàn)室,海南 三亞 572022)
網(wǎng)絡(luò)應(yīng)用日趨廣泛,本文所提出的Android TCP服務(wù)器模型及實(shí)現(xiàn)的TcpServer類通過多線程類來處理遠(yuǎn)程客戶機(jī)的連接請求和數(shù)據(jù)接收,設(shè)置了多種消息用來通知用戶連接是否成功并提交接收到的數(shù)據(jù),以及數(shù)據(jù)收發(fā)過程中是否出現(xiàn)錯誤等.該TcpServer類在工程項(xiàng)目中得到了應(yīng)用,運(yùn)行可靠,提高了開發(fā)效率.
安卓(Android); 傳輸控制協(xié)議; 服務(wù)器; 多線程
安卓(Android)作為移動設(shè)備的開放源碼操作環(huán)境與平臺,在移動互聯(lián)網(wǎng)發(fā)展迅速的今天,不斷擴(kuò)展網(wǎng)絡(luò)應(yīng)用是其開發(fā)的主要方向.目前,Android網(wǎng)絡(luò)編程能夠?qū)崿F(xiàn)信息實(shí)時交互、在線存儲,還可以實(shí)現(xiàn)電子商務(wù)、實(shí)時監(jiān)控與移動辦公等.文獻(xiàn)[1]給出了各種網(wǎng)絡(luò)編程的初步方案,特別是傳輸控制協(xié)議(TCP:Transmission Control Protocol)的Socket編程.文獻(xiàn)[2]實(shí)現(xiàn)了微軟.NET框架下的基于事件驅(qū)動的TcpServer類,偵聽遠(yuǎn)程客戶機(jī)的連接與數(shù)據(jù)接收都通過多線程在后臺進(jìn)行,連接成功或收到數(shù)據(jù)均通過事件提交用戶,節(jié)約了系統(tǒng)資源,同時加強(qiáng)了數(shù)據(jù)接收和發(fā)送的處理功能.本文在文獻(xiàn)[1]的基礎(chǔ)之上,結(jié)合文獻(xiàn)[2]的原理,設(shè)計(jì)并實(shí)現(xiàn)一個Android平臺下的TcpServer類,使其支持消息驅(qū)動和便捷的數(shù)據(jù)接收與發(fā)送等功能.
Android中引入Handler作為消息傳遞機(jī)制,Handler可以充當(dāng)子線程與主線程的中介,主要接收子線程發(fā)來的數(shù)據(jù),并以此數(shù)據(jù)配合主線程更新界面(UI)控件顯示的內(nèi)容.當(dāng)App啟動時,Android首先會開啟一個主線程(即UI線程),主線程為管理界面中的UI控件,對各控件進(jìn)行事件分發(fā).如果需要執(zhí)行一個耗時的操作,如:讀寫文件和實(shí)時數(shù)據(jù)傳輸?shù)?如果將這些操作放在主線程中,就會導(dǎo)致App崩潰,因而,這些操作應(yīng)該放在子線程中.但是,子線程中的數(shù)據(jù)又需要到主線程的UI中進(jìn)行更新,Android運(yùn)行機(jī)制不允許在子線程中直接更新主線程中的數(shù)據(jù).本文考慮借助Handler消息傳遞機(jī)制來解決這個問題.由于Handler對象運(yùn)行在主線程(UI),同其子線程之間可以通過Message對象來傳遞數(shù)據(jù),這樣就可以比較流暢地更新主線程中的數(shù)據(jù)[3].
在實(shí)時監(jiān)控系統(tǒng)中,可以在初始化Handler對象時定義回調(diào)方法,用來接收和處理子線程發(fā)送的消息,也可使用Handler對象的postDelayed方法執(zhí)行定時查詢功能,以便及時做出處理[4].本文所設(shè)計(jì)的TcpServer類通過主線程的Handler對象向主線程發(fā)送連接成功和關(guān)閉等事件消息和接收到的數(shù)據(jù)消息.
TcpServer的內(nèi)部工作原理模型如圖1所示.首先通過構(gòu)造函數(shù)進(jìn)行初始化,主線程向構(gòu)造函數(shù)傳送 Handler對象和偵聽接口等參數(shù),然后在多線程中創(chuàng)建ServerSocket對象,調(diào)用該對象的accept方法偵聽端口.連接失敗,則發(fā)送連接失敗消息;連接成功,則啟動新的線程讀取數(shù)據(jù),如果收到數(shù)據(jù),則發(fā)送數(shù)據(jù)接收到的消息,供主線程調(diào)用.TcpServer初始化并與遠(yuǎn)程客戶機(jī)連接,成功之后還可以直接供主線程調(diào)用,用來發(fā)送數(shù)據(jù)[5].
圖1 TcpServer 內(nèi)部工作原理模型
端口偵聽通過多線程嵌入類Th_Accept實(shí)現(xiàn),其中run方法中的關(guān)鍵代碼為
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,調(diào)用_ss對象的accept方法等候遠(yuǎn)程客戶機(jī)的連接請求,此時多線程處于阻塞狀態(tài).連接成功后,生成BufferedInputStream對象_bis,用于后續(xù)的數(shù)據(jù)讀取(接收),再生成BufferedOutputStream對象_bos,用于后續(xù)的數(shù)據(jù)發(fā)送.如果以上各項(xiàng)都取得成功,則將連接狀態(tài)參數(shù)_bConnected修改為true(否則,保持false不變).
如果連接成功,則Handler對象_handler通過obtainMessage方法獲得連接成功消息,并通過消息的sendToTarget方法發(fā)送到主線程,隨后啟動讀取數(shù)據(jù)的多線程Th_ReadData對象;如果連接失敗,則向主線程發(fā)送連接失敗的消息.
Th_ReadData也是一個多線程嵌入類,用于在后臺讀取數(shù)據(jù),run方法中為一個讀取數(shù)據(jù)的死循環(huán),其關(guān)鍵代碼為:
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) {}
}
如果連接狀態(tài)_bConnected不為true,或者主線程主動終止連接導(dǎo)致_bStopConnect為true,則退出死循環(huán).否則,嘗試接收數(shù)據(jù),假如_bis對象的可用數(shù)據(jù)大于0,則讀取數(shù)據(jù)存入byteBuf對象,并向主線程發(fā)送數(shù)據(jù)到達(dá)消息,同時攜帶所接收的數(shù)據(jù)(存于byteBuf).如果在讀取數(shù)據(jù)的時候發(fā)生錯誤,也可在catch代碼模塊中添加發(fā)送讀取數(shù)據(jù)錯誤消息.
上一節(jié)Th_ReadData類中調(diào)用的readByteBuffer方法是TcpServer類中的私有方法,其定義如下所示.首先分配一個1024個字節(jié)的緩沖區(qū),用ByteBuffer對象buf來保存數(shù)據(jù),然后記錄當(dāng)前時間lStart,通過while循環(huán)將延遲在_nDelay(一般定義為50毫秒)內(nèi)的數(shù)據(jù)進(jìn)行合并,最后作為一個數(shù)據(jù)包返回.如果在讀取數(shù)據(jù)包的過程中發(fā)生錯誤,還可以在catch代碼模塊中添加讀取數(shù)據(jù)錯誤消息.
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;
}
發(fā)送數(shù)據(jù)通過sendBytes方法直接調(diào)用,因而為公有方法,參數(shù)即為需要發(fā)送的字節(jié)數(shù)組,通過BufferedOutputStream對象_bos的write方法進(jìn)行發(fā)送,第一個參數(shù)為所需要發(fā)送的字節(jié)數(shù)組,第二個參數(shù)為偏移量,這里指從第0個字節(jié)開始發(fā)送,第三個參數(shù)為需要發(fā)送的字節(jié)數(shù).同理,如果在發(fā)送數(shù)據(jù)包的過程中發(fā)生錯誤,還可以在catch代碼模塊中添加發(fā)送數(shù)據(jù)錯誤消息.
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類中還包括直接發(fā)送與接收文本字符串的方法,可以很方便地處理POP3協(xié)議之類的純文本協(xié)議[4].處理文本字符串的方法與處理字節(jié)流的方法類似,這里不再贅述.
文獻(xiàn)[2]中的I-7013D溫度采集模塊采用RS-485接口,可以監(jiān)視1路溫度量的變化.為了讓學(xué)生進(jìn)行擴(kuò)展學(xué)習(xí),實(shí)現(xiàn)了一個Visual Basic 2010 版的I-7013D模塊仿真軟件.利用本文的TcpServer類,進(jìn)一步實(shí)現(xiàn)了Android版的支持TCP協(xié)議的I-7013D模塊仿真軟件,方法是在UI主線程中定義一個嵌入類IncomingHandlerCallback,實(shí)現(xiàn)處理消息的接口方法handleMessage,其代碼如下所示.
class IncomingHandlerCallback implements Handler.Callback{
public boolean handleMessage(Message msg) {
processMessage(msg);
return true;
}
}
processMessage方法用來處理各種消息,一般使用switch語句對消息進(jìn)行分類處理,為了方便,這里僅用條件語句來處理數(shù)據(jù)到達(dá)消息,判斷數(shù)據(jù)到達(dá),則將消息中的數(shù)據(jù)對象進(jìn)行強(qiáng)制轉(zhuǎn)換,然后即可進(jìn)行隨后的處理.
private void processMessage(Message msg){
if(msg.what == TcpServer.MESSAGE_DataArrived){
ByteBuffer byteBuf = (ByteBuffer)msg.obj;
// 進(jìn)一步處理數(shù)據(jù)
}
}
在UI主線程的onCreate方法中,對Handler和TcpServer進(jìn)行初始化,即可等待遠(yuǎn)程客戶機(jī)的連接,連接成功后響應(yīng)其查詢命令.
mHandler = new Handler(new IncomingHandlerCallback());
server = new TcpServer(mHandler, nPort);
圖2所示為Android版I-7013D主控軟件運(yùn)行效果,能完整地實(shí)時取得運(yùn)行TcpServer對象的I-7013D仿真模塊中的數(shù)據(jù),分別通過文本框(當(dāng)前溫度31.79度)和繪圖方式進(jìn)行展示.
圖2 I-7013D主控機(jī)軟件運(yùn)行
Android網(wǎng)絡(luò)應(yīng)用廣泛.本文所提出的Android TCP服務(wù)器模型及實(shí)現(xiàn)的TcpServer原型類可以在后臺處理遠(yuǎn)程客戶機(jī)的連接請求和接收數(shù)據(jù),設(shè)置了多種消息用來通知用戶連接是否成功,提交接收到的數(shù)據(jù),以及數(shù)據(jù)收發(fā)過程中是否出現(xiàn)錯誤等.由于通過多線程處理耗時工作,TcpServer類不會引起App崩潰,且占用資源少.利用TcpServer類研發(fā)的支持Tcp協(xié)議的Android版I-7013D模塊仿真軟件,可以與對應(yīng)的主控機(jī)軟件無縫連接,運(yùn)行可靠[6].
[1]陳文,郭依正.深入理解Android網(wǎng)絡(luò)編程[M].北京:機(jī)械工業(yè)出版社, 2013.
[2]馬玉春.計(jì)算機(jī)監(jiān)控系統(tǒng)的仿真開發(fā)[M].北京:國防工業(yè)出版社,2015.
[3]陳偉.Android Handler消息傳遞機(jī)制在人機(jī)界面軟件設(shè)計(jì)中的應(yīng)用[J].科技信息,2014, 13 (6):90-92.
[4]劉雍.基于ARM9 的嵌入式視頻監(jiān)控系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)[J].瓊州學(xué)院學(xué)報,2015, 22 (2):35-38.
[5]楊宗德,呂光宏,劉雍.Linux高級程序設(shè)計(jì) [M].北京:人民郵電出版社,2012.
[6]文松,王敏,胡春陽, 等.一種用于移動設(shè)備的Web 服務(wù)遠(yuǎn)程證明方法[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
格式:劉雍,孫冰,馬玉春.基于消息驅(qū)動的Android TCP服務(wù)器類的設(shè)計(jì)[J].海南熱帶海洋學(xué)院學(xué)報,2017,24(2):59-63.
2017-02-16
劉雍(1979 - ),女,四川閬中人, 海南熱帶海洋學(xué)院海洋信息工程學(xué)院副教授,碩士,研究方向?yàn)橐苿討?yīng)用與嵌入式系統(tǒng).
馬玉春(1969-),男,江蘇南京人, 海南熱帶海洋學(xué)院海洋信息工程學(xué)院教授,博士,研究方向?yàn)橐苿討?yīng)用與計(jì)算機(jī)監(jiān)控技術(shù).
TP311.52
A
2096-3122(2017) 02-0059-05
10.13307/j.issn.2096-3122.2017.02.12