前言
目前市場上存在多種免費的雲推送服務,比如:個推、JPush等,但從技術上講這畢竟是別人的東西,主要面向通用場景,特定場景下還是得自已來實現推送服務。
本文主要介紹的是基於MQTT實現一個簡單的Android消息推送系統。更多推送技術資料請見:http://www.52im.net/forum.php?mod=collection&action=view&ctid=11
通信協議比較
按照慣例,總是在跟類似的通信協議對比一下,以下內容搜集自網絡,僅供參考。
方案1:使用GCM服務(Google Cloud Messaging)
簡介:Google推出的雲消息服務,即第二代的C2DM。
優點:Google提供的服務、原生、簡單,無需實現和部署服務端。
缺點:Android版本限制(必須大於2.2版本),該服務在國內不夠穩定、需要用戶綁定Google帳號,受限於Google。
方案2:使用XMPP協議(Openfire + Spark + Smack)
簡介:基於XML協議的通訊協議,前身是Jabber,目前已由IETF國際標准化組織完成了標准化工作。
優點:協議成熟、強大、可擴展性強、目前主要應用於許多聊天系統中,且已有開源的Java版的開發實例androidpn。
缺點:協議較復雜、冗余(基於XML)、費流量、費電,部署硬件成本高。
方案3:使用MQTT協議(更多信息見:http://mqtt.org/)
簡介:輕量級的、基於代理的“發布/訂閱”模式的消息傳輸協議。
優點:協議簡潔、小巧、可擴展性強、省流量、省電,目前已經應用到企業領域(參考:mqtt.org/software),且已有C++版的服務端組件rsmb。
缺點:不夠成熟、實現較復雜、服務端組件rsmb不開源,部署硬件成本較高。
方案4:使用HTTP輪循方式
簡介:定時向HTTP服務端接口(Web Service API)獲取最新消息。
優點:實現簡單、可控性強,部署硬件成本低。
缺點:實時性差。
對各個方案的優缺點的研究和對比,推薦使用MQTT協議的方案進行實現,主要原因是:MQTT最快速,也最省流量(固定頭長度僅為2字節),且極易擴展,適合二次開發。接下來,我們就來分析使用MQTT方案進行Android消息的原理和方法,並架設自己的推送服務。
(即時通訊網注:作為通信協議,實際上Google的Protobuf也是個非常不錯的選擇,請見《強列建議將Protobuf作為你的即時通訊應用數據傳輸格式》,更多通信協議的選擇和對比文章請見:http://www.52im.net/forum.php?mod=collection&action=view&ctid=18)
基於MQTT推送系統的技術原理
<ignore_js_op>
實際上,其他推送系統(包括GCM、XMPP方案)的原理都與此類似。
推送服務端准備
- 下載&解壓rsmb安裝包(下載地址:請見文末附件)
- 進入對應的目錄,比如32位的Linux系統則應該進入linux_ia32目錄。
- 編輯配置文件broker_1883.cfg。
broker_1883.cfg的配置如下:
|
1
2
3
|
port 1883
max_inflight_messages 10
max_queued_messages 1000
|
運行./broker broker_1883.cfg,顯示如下:
|
01
02
03
04
05
06
07
08
09
10
|
20120823 110454.039 CWNAN9999I Really Small Message Broker
20120823 110454.039 CWNAN9997I Licensed Materials - Property of IBM
20120823 110454.039 CWNAN9996I Copyright IBM Corp. 2007, 2010 All Rights Reserved
20120823 110454.039 CWNAN9995I US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
20120823 110454.039 CWNAN0049I Configuration
file
name is broker_1883.cfg
20120823 110454.040 CWNAN0053I Version 1.2.0, Aug 18 2010 17:03:35
20120823 110454.040 CWNAN0054I Features included: bridge
20120823 110454.040 CWNAN9993I Author: Ian Craggs ([url=mailto:icraggs@uk.ibm.com]icraggs@uk.ibm.com[
/url
])
20120823 110454.040 CWNAN0014I MQTT protocol starting, listening on port 1883
... ...
|
這樣,推送服務的服務端就已經准備好了,監聽1883端口。
推送客戶端准備
- 下載&解壓AndroidPushNotificationsDemo項目(下載地址:請見文末附件)
- 將該項目導入Eclipse中(File -> Export -> Existing Projects into Workspace)
- 修改PushService.java中的MQTT_HOST常量為推送服務端的IP地址。
- 啟動Android模擬器,並安裝該項目。
注意:在新版本的Android SDK中可能會遇到以下錯誤:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
.. ...
08-23 02:28:44.184: W
/dalvikvm
(282): VFY: unable to
find
class referenced
in
signature (Lcom
/ibm/mqtt/MqttPersistence
;)
08-23 02:28:44.194: I
/dalvikvm
(282): Failed resolving Lcom
/tokudu/demo/PushService
$MQTTConnection; interface 35
'Lcom/ibm/mqtt/MqttSimpleCallback;'
08-23 02:28:44.194: W
/dalvikvm
(282): Link of class
'Lcom/tokudu/demo/PushService$MQTTConnection;'
failed
08-23 02:28:44.194: E
/dalvikvm
(282): Could not
find
class
'com.tokudu.demo.PushService$MQTTConnection'
, referenced from method com.tokudu.demo.PushService.connect
08-23 02:28:44.194: W
/dalvikvm
(282): VFY: unable to resolve new-instance 42 (Lcom
/tokudu/demo/PushService
$MQTTConnection;)
in
Lcom
/tokudu/demo/PushService
;
... ...
08-23 02:28:44.404: E
/AndroidRuntime
(282): java.lang.VerifyError: com.tokudu.demo.PushService
08-23 02:28:44.404: E
/AndroidRuntime
(282): at com.tokudu.demo.PushActivity$1.onClick(PushActivity.java:32)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at android.view.View.performClick(View.java:2408)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at android.view.View$PerformClick.run(View.java:8816)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at android.os.Handler.handleCallback(Handler.java:587)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at android.os.Handler.dispatchMessage(Handler.java:92)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at android.os.Looper.loop(Looper.java:123)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at android.app.ActivityThread.main(ActivityThread.java:4627)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at java.lang.reflect.Method.invokeNative(Native Method)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at java.lang.reflect.Method.invoke(Method.java:521)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
08-23 02:28:44.404: E
/AndroidRuntime
(282): at dalvik.system.NativeStart.main(Native Method)
... ...
|
原因是發布的時候沒有加入wmqtt.jar包,解決辦法如下:
- 在項目根目錄下創建libs目錄,並把wmqtt.jar包移入該目錄。
- 重新配置項目的Java Build Path(右鍵菜單中的Properties選項中)。
- 重新打包發布即可。
運行效果如下:
<ignore_js_op>
點擊“Start Push Service”按鈕即可開啟推送服務,這時我們可以看到rsmb的服務日志中打出以下提示:
|
1
|
20120823 113742.297 CWNAN0033I Connection attempt to listener 1883 received from client tokudu
/9774d56d682e549c
on address 192.168.28.39:3345
|
其中的“9774d56d682e549c”就是對應的客戶端ID號。
發送服務准備
- 下載&解壓PHP版的發送服務端代碼send_mqtt.zip(下載地址:請見文末附件)
- 修改etc/config.php中推送服務端的IP地址和端口號,即MQTT_SERVER_HOST和MQTT_SERVER_POST常量。
- 打開對應的URL地址,就可以看到發送服務的界面,實際上就是向對應的推送客戶端推送消息。
<ignore_js_op>
接着,我們在該界面中填入客戶端ID(9774d56d682e549c)和推送消息(test)並點擊“Send Push Message”按鈕,服務端就可以向客戶端推送消息了。我們看到,客戶端上立馬就可以收到剛剛推送的消息,如下圖:
<ignore_js_op>
結語
當然,以上方案還存在許多的不足,比如,如果客戶端沒有保持連接,發送的消息就會被丟棄。不過,我們可以利用MQTT協議開發出更強大的服務端來替代rsmb,更可以加入隊列、緩存等功能進行優化,有興趣的朋友不妨試試。可參考開源項目Mosquitto(http://mosquitto.org/)。
