EMQ ---websocket
简介
近年来随着 Web 前端的快速发展,浏览器新特性层出不穷,越来越多的应用可以在浏览器端或通过浏览器渲染引擎实现,Web 应用的即时通信方式 WebSocket 得到了广泛的应用。
WebSocket 是一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 通信协议于2011年被 IETF 定为标准 RFC 6455,并由 RFC 7936 补充规范。WebSocket API 也被 W3C 定为标准。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 —— 摘自 维基百科 WebSocket
MQTT 协议第 6 章详细约定了 MQTT 在 WebSocket [RFC6455] 连接上传输需要满足的条件
我们可以使用emq自带的Dashboard插件,进行websocket调试,打开谷歌浏览器输入网址,其中192.168.83.128是emq所在的IP地址:
http://192.168.83.128:18083/#/websocket
用户名:admin,密码:public
WebSocket URI:ws(s)://192.168.83.128:8083/mqtt
TCP URI:tcp://192.168.83.128:1883
一、测试实践
测试环境:使用MQTT 3.1.1版本协议,Wireshark 2.4.1版本,使用wireshark抓包分析(如果是虚拟机,要抓VMnet8虚拟网卡)。
clientid:861694030142473
username:libaineu2004
password:12345678
cleanSession:true
keepalive:60s
1、测试方案1,使用普通的tcp连接,TCP URI:tcp://192.168.83.128:1883,wireshark过滤条件tcp.port == 1883。
(1)、MQTT Connect Command
鼠标点击wireshark的Frame页面,右键菜单,复制,选中树的所有可见项目
MQ Telemetry Transport Protocol, Connect Command
Header Flags: 0x10 (Connect Command)
Msg Len: 51
Protocol Name Length: 4
Protocol Name: MQTT
Version: 4
Connect Flags: 0xc2
Keep Alive: 60
Client ID Length: 15
Client ID: 861694030142473
User Name Length: 12
User Name: libaineu2004
Password Length: 8
Password: 12345678
右键菜单,复制,字节为HEX + ASCII转储
0000 10 33 00 04 4d 51 54 54 04 c2 00 3c 00 0f 38 36 .3..MQTT...<..86
0010 31 36 39 34 30 33 30 31 34 32 34 37 33 00 0c 6c 1694030142473..l
0020 69 62 61 69 6e 65 75 32 30 30 34 00 08 31 32 33 ibaineu2004..123
0030 34 35 36 37 38 45678
(2)、MQTT Connect Ack
MQ Telemetry Transport Protocol, Connect Ack
Header Flags: 0x20 (Connect Ack)
Msg Len: 2
Acknowledge Flags: 0x00
Return Code: Connection Accepted (0)
0000 20 02 00 00 ...
2、测试方案2,使用websocket连接,WebSocket URI:ws(s)://192.168.83.128:8083/mqtt,wireshark过滤条件tcp.port == 8083。我们可以观察到mqtt连接的过程,websocket协议,完整的数据被拆分了好几帧传输。每一帧报文,都有WebSocket和Data字段。我们仅仅需要关注Data字段即可。我们发现,把所有Data字段组合拼接起来,就是和测试方案1完全相同的数据。MQTT Connect Command和Ack都完全相同。说明了即使使用了Websocket方式来传输,仍然遵循mqtt协议。
从本质上来讲,Websocket也是基于 TCP 协议的,同时借用了HTTP的协议来完成一部分握手。
主要解决 HTTP 协议中一个 request 对应一个 response 的尴尬。(http server 不能主动发送消息给 http client) 。通过 HTTP 完成 websocket 的握手过程,接着按照 websocket 协议进行通讯。
websocket 也有他自己的数据帧格式: http://blog.csdn.net/u010487568/article/details/20569027
(1)MQTT Connect Command
0000 10 .
0000 33 3
0000 00 04 ..
0000 4d 51 54 54 MQTT
0000 04 .
0000 c2 .
0000 00 3c .<
0000 00 0f ..
0000 38 36 31 36 39 34 30 33 30 31 34 32 34 37 33 861694030142473
0000 00 0c ..
0000 6c 69 62 61 69 6e 65 75 32 30 30 34 libaineu2004
0000 00 08 ..
0000 31 32 33 34 35 36 37 38 12345678
(2)MQTT Connect Ack
Data (4 bytes)
Data: 20020000
[Length: 4]
0000 20 02 00 00 ...
二、以下是源码分析,先来看emq v1.1.3版本的源码:
1、-module(emqttd_app).
%%--------------------------------------------------------------------
%% Start Servers
%%--------------------------------------------------------------------
start_servers(Sup) ->
Servers = [
{"emqttd wsclient supervisor", {supervisor, emqttd_ws_client_sup}},
%% Start http listener
start_listener({http, ListenOn, Opts}) ->
mochiweb:start_http(http, ListenOn, Opts, {emqttd_http, handle_request, []});
打开8083端口,开启http服务器。Websocket也是基于 TCP 协议的,同时借用了HTTP的协议来完成一部分握手。
2、-module(emqttd_http).
%%--------------------------------------------------------------------
%% MQTT Over WebSocket
%%--------------------------------------------------------------------
handle_request('GET', "/mqtt", Req) ->
lager:info("WebSocket Connection from: ~s", [Req:get(peer)]),
Upgrade = Req:get_header_value("Upgrade"),
Proto = Req:get_header_value("Sec-WebSocket-Protocol"),
case {is_websocket(Upgrade), Proto} of
{true, "mqtt" ++ _Vsn} ->
emqttd_ws:handle_request(Req);
{false, _} ->
lager:error("Not WebSocket: Upgrade = ~s", [Upgrade]),
Req:respond({400, [], <<"Bad Request">>});
{_, Proto} ->
lager:error("WebSocket with error Protocol: ~s", [Proto]),
Req:respond({400, [], <<"Bad WebSocket Protocol">>})
end;
websocket的入口函数。重点关注emqttd_ws:handle_request(Req);
3、-module(emqttd_ws).
ws_loop(Data, State = #wsocket_state{peer = Peer, client_pid = ClientPid,
parser_fun = ParserFun}, ReplyChannel) ->
?WSLOG(debug, Peer, "RECV ~p", [Data]),
case catch ParserFun(iolist_to_binary(Data)) of
{more, NewParser} ->
State#wsocket_state{parser_fun = NewParser};
{ok, Packet, Rest} ->
gen_server:cast(ClientPid, {received, Packet}),
ws_loop(Rest, reset_parser(State), ReplyChannel);
{error, Error} ->
?WSLOG(error, Peer, "Frame error: ~p", [Error]),
exit({shutdown, Error});
{'EXIT', Reason} ->
?WSLOG(error, Peer, "Frame error: ~p", [Reason]),
?WSLOG(error, Peer, "Error data: ~p", [Data]),
exit({shutdown, parser_error})
end.
gen_server:cast(ClientPid, {received, Packet}), 接收来自客户端的消息。
4、-module(emqttd_ws_client).
handle_cast({received, Packet}, State = #wsclient_state{peer = Peer, proto_state = ProtoState}) ->
case emqttd_protocol:received(Packet, ProtoState) of
{ok, ProtoState1} ->
noreply(State#wsclient_state{proto_state = ProtoState1});
{error, Error} ->
?WSLOG(error, Peer, "Protocol error - ~p", [Error]),
shutdown(Error, State);
{error, Error, ProtoState1} ->
shutdown(Error, State#wsclient_state{proto_state = ProtoState1});
{stop, Reason, ProtoState1} ->
stop(Reason, State#wsclient_state{proto_state = ProtoState1})
end;
emqttd_protocol:received(Packet, ProtoState)。接收处理消息。
5、-module(emqttd_protocol).
process(Packet = ?CONNECT_PACKET(Var), State0) ->
#mqtt_packet_connect{proto_ver = ProtoVer,
proto_name = ProtoName,
username = Username,
password = Password,
clean_sess = CleanSess,
keep_alive = KeepAlive,
client_id = ClientId} = Var,
State1 = State0#proto_state{proto_ver = ProtoVer,
proto_name = ProtoName,
username = Username,
client_id = ClientId,
clean_sess = CleanSess,
keepalive = KeepAlive,
will_msg = willmsg(Var),
connected_at = os:timestamp()},
trace(recv, Packet, State1),
{ReturnCode1, SessPresent, State3} =
case validate_connect(Var, State1) of
validate_connect(Var, State1),校验clientid,username和password的有效性
EMQ ---websocket的更多相关文章
- EMQ、Websocket、MQTT
mqtt.fx的安装和使用 https://blog.csdn.net/nicholaszao/article/details/79211965 EMO 使用说明 http://emqtt.com/d ...
- 使用EMQ搭建MQTT服务器
前言寒假的时候开始搭建mqtt服务器,一开始使用的是RabbitMQ,基于Erlang语言.但是RabbitMQ的本职工作是AMQP,MQTT只是他的一个插件功能,似乎有些大材小用,很多MQTT的功能 ...
- 使用 WebSocket 客户端连接 MQTT 服务器
简介 近年来随着 Web 前端的快速发展,浏览器新特性层出不穷,越来越多的应用可以在浏览器端或通过浏览器渲染引擎实现,Web 应用的即时通信方式 WebSocket 得到了广泛的应用. WebSock ...
- 物联网架构成长之路(6)-EMQ权限控制
1. 前言 EMQTT属于一个比较小众的开源软件,很多资料不全,很麻烦,很多功能都是靠猜测,还有就是看官方提供的那几个插件,了解. 2. 说明 上一小节的插件 emq_plugin_wunaozai ...
- EMQ ---问题集
1)emqttd 使用 SSL遇到的问题:服务器直接布了一份emqttd ,然后什么都没管,端口默认的ws 8083,wss8084,mqtt 1883,mqtt(ssl) 8883. 结果跑起来之后 ...
- centos7(vmware install) 安装EMQ注意事项 ---控制台远程访问
若想远端访问控制台,需打开对于端口 TCP 服务端口占用 EMQ 2.0 消息服务器默认占用的 TCP 端口包括: 1883 MQTT 协议端口 8883 MQTT/SSL 端口 8083 MQTT/ ...
- emq(centos 7) 使用
配置文件: EMQ 配置文件: /etc/emqttd/emq.conf 插件配置文件: /etc/emqttd/plugins/*.conf 日志文件 日志文件目录: /var/log/emqttd ...
- [emqttd] (EMQ)
[emqttd] (EMQ)是采用Erlang语言开发,全面支持MQTT V3.1.1协议,支持集群和大规模连接的开源MQTT消息服务器. [emqttd]致力于发布一个基于Erlang/OTP语言平 ...
- 转:EMQ(emqttd) 2.x 安装和使用(物联网传输控制协议的Broker)
支持下国产开源. MQTT物联网传输控制协议:<MQTT-3.1.1-CN.pdf> 下载:emqttd-centos64-v2.0-rc.2-20161019.zip 安装: $ unz ...
随机推荐
- extjs form textfield的隐藏方法
只需将textfield的hidden和hideLabel配置为true就可以了.只设置hidden:true时会显示出来一个:的标签. this.formpanel = new Ext.Fo ...
- git ssh key配置
原文:https://blog.csdn.net/lqlqlq007/article/details/78983879 git clone支持https和git(即ssh)两种方式下载源码: 当使用 ...
- Git 报错:git - error: RPC failed; curl 18 transfer closed with outstanding read data remaining 解决方案
error: RPC failed; curl 18 transfer closed with outstanding read data remaining because have error w ...
- .NET:异常处理的两条“黄金定律”,求批!
背景 架构之处必须考虑:如何处理异常?如何定义自己的异常体系?本文为了强化这个概念而写. 异常处理的两条“黄金定律” 自己抄袭的两条规律: 异常不能穿过“边界类”. 异常不能在没有恢复的情况下“吞掉” ...
- 电子书mobi的格式详解
https://wiki.mobileread.com/wiki/MOBI#Format Like PalmDOC, the Mobipocket file format is that of a s ...
- 在Ubuntu 13.04下的安装eclipse
来源:http://www.cnblogs.com/lanxuezaipiao/p/3325628.html 一.eclipse安装过程 首先确保在安装eclipse之前已经安装好Java虚拟机 1. ...
- ASP.NET 5 Beta6发布了(翻译)
感觉就好像我们刚刚发布了一个版本,现在我们又要发布一个新的版本.开发团队通过努力工作在Visual Studio2015的正式版上提交使用ASP.NET 5开发工具的上的更新以及库的更新.新的版本为b ...
- Java性能监控工具:VisualVM
VisualVM是JDK自带的一款全能型性能监控和故障分析工具,包括对CPU使用.JVM堆内存消耗.线程.类加载的实时监控,内存dump文件分析,垃圾回收运行情况的可视化分析等,对故障排查和性能调优很 ...
- UVA 10790 (13.08.06)
How Many Points of Intersection? We have two rows. There are a dots on the toprow andb dots on the ...
- [ES6] 05. The leg keyword -- 3. Block Scope
In ES6, IIFE is not necessary: // IIFE写法 (function () { var tmp = ...; ... }()); // 块级作用域写法 { let tm ...