MQTT協議專一於網絡、資源受限環境,創建之初未曾考慮WEB環境,倒也正常。雖然如此,但不表明它不適合HTML5環境。html
HTML5 Websocket是創建在TCP基礎上的雙通道通訊,和TCP通訊方式很相似,適用於WEB瀏覽器環境。雖然MQTT基因層面選擇了TCP做爲通訊通道,但咱們添加個編解碼方式,MQTT Over Websocket也能夠的。java
這樣作的好處,MQTT的使用範疇被擴展到HTML五、桌面端瀏覽器、移動端WebApp、Hybrid等,多了一些想像空間。這樣看來,不管是移動端,仍是WEB端,MQTT都會有本身的使用空間。git
話說,現代化瀏覽器都已經支持Websocket,這裏有一個全部瀏覽器支持列表:github
更詳細列表,請直接訪問:http://caniuse.com/websocketsweb
毫無疑問,火狐和谷歌瀏覽器帶動了現代瀏覽器的發展,對HTML5標準的支持也是如此。支持Websocket的瀏覽器單純從上面數字來說,73.88%的支持率。但實際上還得參考瀏覽器市場佔有率:c#
上圖數據,來源於: 2014年4月份全球主流瀏覽器市場份額排行榜跨域
超過60%用戶機器上瀏覽器的支持Websocket,數據很可觀。瀏覽器
針對不支持websocker的部分歷史瀏覽器,能夠考慮一下Flash socket,雖然使用Flashsocket用以模擬Websocket就很容易理解,但條件以下: - 須要單獨佔用一個端口專用於安全跨域訪問策略 - 須要瀏覽器支持二進制Blob 支持二進制操做的瀏覽器現狀:安全
來源於:http://caniuse.com/xhr2服務器
比較一下支持Websocket和XHR2的桌面瀏覽器,重疊率很高,使用Flash Socket用以模擬Websocket必要性不大,在相似於IE平臺上,不如直接使用Flash版本的
https://github.com/yangboz/as3MQTT/tree/master/MQTTClient_AS3
不是全部瀏覽器都支持Websocket,尤爲是阻礙歷史發展的IE6/IE7/IE8/IE9。MQTT協議爲二進制協議壓根和HTTP純文本不兼容,尤爲瀏覽器端JavaScript處理文本很合適,但二進制就顯得笨手笨角,除非支持XHR2。
這部分後面專門會講到。
現有一些解決方案多是後面爲MQTT Broker,前面是添加一層代理。好比:例如 mod_websocket ,對應在線示範:http://test.mosquitto.org/ws.html
表面上看着很解藕的,實際上模仿的仍是傳統型的短鏈接反向代理架構:Nginx/Apache +Java/PHP/Python/Ruby。
客戶端創建一條鏈接,服務器端須要使用到至少兩個文件句柄,中間多了一層路徑。優雅的解決方案,能夠向socket.io看起。一套服務端程序,同時提供若干種協議供終端選擇。其實,一臺MQTT Broker中間件服務器,能夠綁定多個端口,一個面向純TCP的1883端口,一個面向Websocket的80/8080端口,共享基礎邏輯,面向不一樣協議。
服務器添加對Websocket支持,基本不用作多大改動。對比Tcp的附加到單個Channel的處理器列表:
1234567891011 |
public class TcpChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline
.addLast(
new MqttMessageNewEncoder(),
new MqttMessageNewDecoder(),
new MqttMessageHandler());
}
}
|
Websocket對應單個Channel的處理器列表:
12345678910111213141516171819202122232425262728293031323334 |
import io.mqtt.handler.HttpRequestHandler;
import io.mqtt.handler.MqttMessageHandler;
import io.mqtt.handler.coder.MqttMessageWebSocketFrameDecoder;
import io.mqtt.handler.coder.MqttMessageWebSocketFrameEncoder;
import io.mqtt.handler.http.HttpJsonpTransport;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
public class WebsocketChannelInitializer extends ChannelInitializer<SocketChannel> {
private final static String websocketUri = "/websocket";
private HttpRequestHandler httpRequestHandler = new HttpRequestHandler(
websocketUri);
static {
HttpJsonpTransport httpJsonpTransport = new HttpJsonpTransport();
HttpRequestHandler.registerTransport(httpJsonpTransport);
}
@Override
public void initChannel(final SocketChannel ch) throws Exception {
ch
.pipeline().addLast(
new HttpServerCodec(),
new MqttMessageWebSocketFrameEncoder(),
new HttpObjectAggregator(65536),
httpRequestHandler,
new WebSocketServerProtocolHandler(websocketUri),
new MqttMessageWebSocketFrameDecoder(),
new MqttMessageHandler());
}
}
|
爲了支持Websocket協議,僅僅額外增長了:
1234567891011121314 |
@
Sharable
public class MqttMessageWebSocketFrameEncoder extends
MessageToMessageEncoder<Message> {
@Override
protected void encode(ChannelHandlerContext ctx, Message msg,
List<Object> out) throws Exception {
if (msg == null)
return;
byte[] data = ((Message) msg).toBytes();
out
.add(new BinaryWebSocketFrame(Unpooled.wrappedBuffer(data)));
}
}
|
1234567891011121314151617 |
public class MqttMessageWebSocketFrameDecoder extends
MessageToMessageDecoder<BinaryWebSocketFrame> {
private MqttMessageNewDecoder messageNewDecoder;
public MqttMessageWebSocketFrameDecoder() {
messageNewDecoder
= new MqttMessageNewDecoder();
}
@Override
protected void decode(ChannelHandlerContext ctx,
BinaryWebSocketFrame wsFrame, List<Object> out) throws Exception {
ByteBuf buf = wsFrame.content();
this.messageNewDecoder.decode(ctx, buf, out);
}
}
|
囉囉嗦嗦的講了一大通Websocket,總之對Websocket的支持還算容易。後面有時間寫寫如何使用HTTP協議達到MQTT OVER HTTP的效果。
原文 http://www.blogjava.net/yongboy/archive/2014/05/26/414130.html