emqx tcp私有协议开发---emqx_tcp_app

-module(emqx_tcp_app).

-behaviour(application).

%% 插件协议
-emqx_plugin(protocol).

%% 导出启动和停止方法
-export([start/2, stop/1]).
%% 导出socket监听和停止方法
-export([start_listener/1,start_listener/3,stop_listener/1,stop_listener/3]).
%% 引入mqtt头文件
-include("emqx_mqtt.hrl").
%% 定义mqtt报文的头部信息
-record(mqtt_packet_header, {type = 0, dup = false, qos = 0, retain = false}).
%% 连接报文定义
-record(mqtt_packet_connect,
        {
         proto_name = <<"MQTT">>, 
         proto_ver = 4,is_bridge = false,
         clean_start = true,will_flag = false,
         will_qos = 0,will_retain = false,
         keepalive = 0,
         properties = #{},
         clientid = <<>>,
         will_props = #{},
         will_topic = undefined,
         will_payload = undefined,
         username = undefined,
         password = undefined}).

%% 连接回复报文
-record(mqtt_packet_connack,{ack_flags, reason_code, properties = #{}}).

%% 发布消息
-record(mqtt_packet_publish,{topic_name, packet_id, properties = #{}}).
%% 发布回复报文
-record(mqtt_packet_puback,{packet_id, reason_code, properties = #{}}).

%% 订阅报文
-record(mqtt_packet_subscribe,{packet_id, properties = #{}, topic_filters})
## 订阅回复报文
-record(mqtt_packet_suback,{packet_id, properties = #{}, reason_codes}).

%% 取消订阅报文
-record(mqtt_packet_unsubscribe,{packet_id, properties = #{}, topic_filters})
%% 取消订阅回复报文
-record(mqtt_packet_unsuback,{packet_id, properties = #{}, reason_codes}).

%% 断开连接报文
-record(mqtt_packet_disconnect,{reason_code, properties = #{}}).
%% 授权报文
-record(mqtt_packet_auth,{reason_code, properties = #{}}).

%% mqtt 数据包定义{头部报文,命令字,消息质量,消息负载}
-record(mqtt_packet,
        {header :: #mqtt_packet_header{},
         variable ::
             #mqtt_packet_connect{} |
             #mqtt_packet_connack{} |
             #mqtt_packet_publish{} |
             #mqtt_packet_puback{} |
             #mqtt_packet_subscribe{} |
             #mqtt_packet_suback{} |
             #mqtt_packet_unsubscribe{} |
             #mqtt_packet_unsuback{} |
             #mqtt_packet_disconnect{} |
             #mqtt_packet_auth{} |
             pos_integer() |
             undefined,
         payload :: binary() | undefined}).

%% mqtt 消息定义
-record(mqtt_msg,{
         qos = 0,  %% 消息质量
         retain = false,%% 保留最新消息
         dup = false, %%
         packet_id, %% 消息id
         topic, %% 消息主题
         props, %%
         payload %% 消息负载
}).
%% 引入模块的头文件
-include("emqx_tcp.hrl").
%% 定义监听类型{协议,简单方法,监听配置}
-type listener() :: {esockd:proto(), esockd:listen_on(),[esockd:option()]}.

%% 启动application模块
start(_Type, _Args) ->
    start_listener(),
    emqx_tcp_sup:start_link().

%% 停止
stop(_State) -> stop_listener().

%% 循环启动多个监听
start_listener() ->
    lists:foreach(fun start_listener/1, listeners_confs()).

%% 循环停止多个监听
stop_listener() ->
    lists:foreach(fun stop_listener/1, listeners_confs()).

%% 定义启动监听方法,返回进程id或者错误原子信息
-spec start_listener(listener()) -> {ok, pid()} |{error, term()}.
start_listener({Proto, ListenOn, Options}) ->
    %% 调用启动方法,反悔ok或者error
    case start_listener(Proto, ListenOn, Options) of
        {ok, _} ->
            io:format("Start emqx-tcp:~s listener on[Proto, format(ListenOn)]);
        {error, Reason} ->
            io:format(standard_error,
          "Failed to start emqx-tcp:~s listener "
          "on ~s - ~p~n!",
          [Proto, format(ListenOn), Reason])
    end.

%% 启动监听
-spec start_listener(esockd:proto(), esockd:listen_on(),
                     [esockd:option()]) -> {ok, pid()} | {error, term()}.
start_listener(tcp, ListenOn, Options) ->
    start_tcp_listener('emqx-tcp:tcp', ListenOn, Options);
start_listener(ssl, ListenOn, Options) ->
    start_tcp_listener('emqx-tcp:ssl', ListenOn, Options).

start_tcp_listener(Name, ListenOn, Options) ->
    SockOpts = esockd:parse_opt(Options),
    esockd:open(Name,ListenOn,merge_default(SockOpts),
                {emqx_tcp_connection,start_link,[Options -- SockOpts]}).
%% 合并配置项
merge_default(Options) ->
    case lists:keytake(tcp_options, 1, Options) of
        {value, {tcp_options, TcpOpts}, Options1} ->
            [{tcp_options,
              emqx_misc:merge_opts([binary,{packet, raw},{reuseaddr, true}{backlog, 512},{nodelay, true}],TcpOpts)}| Options1];
        false ->
            [{tcp_options,{packet, raw},{reuseaddr, true},{backlog, 512},
               {nodelay, true}]}| Options]
    end.

%% 监听配置
listeners_confs() ->
    [{Proto, ListenOn, wrap_proto_options(Options)} || {Proto, ListenOn, Options} <- env(listeners, [])].

wrap_proto_options(Opts) ->
    ProtoOpts = [{K, env(K)} || K <- protokeys(), env(K) =/= undefined],
    ProtoOpts ++ Opts.

protokeys() ->
    [idle_timeout,
     up_topic, %% 上行主题
     dn_topic, %% 下行主题
     max_packet_size,%% 数据包大小
     enable_stats,%% 是否开启统计
     force_gc_policy,%% 虚拟机垃圾回收策略
     force_shutdown_policy].%% 强制杀死策略

%% 停止监听
-spec stop_listener(listener()) -> ok | {error, term()}.
stop_listener({Proto, ListenOn, Opts}) ->
    case stop_listener(Proto, ListenOn, Opts) of
        ok ->
            io:format("Stop emqx-tcp:~s listener on ~s successfully.~n",
             [Proto, format(ListenOn)]);
        {error, Reason} ->
            io:format(standard_error,
                      "Failed to stop emqx-tcp:~s listener "
                      "on ~s - ~p~n.",
                      [Proto, format(ListenOn), Reason])
    end.

%% 停止监听
-spec stop_listener(esockd:proto(), esockd:listen_on(),
[esockd:option()]) -> ok | {error, term()}.
stop_listener(tcp, ListenOn, _Opts) ->
    esockd:close('emqx-tcp:tcp', ListenOn);
stop_listener(Proto, ListenOn, _Opts)
    when Proto == ssl; Proto == tls ->
    esockd:close('emqx-tcp:ssl', ListenOn).

%% 端口
format(Port) when is_integer(Port) ->
    io_lib:format("0.0.0.0:~w", [Port]);
%% ip地址和端口
format({Addr, Port}) when is_list(Addr) ->
    io_lib:format("~s:~w", [Addr, Port]);

format({Addr, Port}) when is_tuple(Addr) ->
    io_lib:format("~s:~w", [inet:ntoa(Addr), Port]).

env(Key) -> env(Key, undefined).

env(Key, Default) ->
    application:get_env(emqx_tcp, Key, Default).

上述代码是对emqx 私有tcp协议 建立tcp连接,主要是基于esockd这个socket连接池来构建 里面牵涉到几个文件,下面介绍emqx_tcp.

源代码地址:https://github.com/tlchun/emqx-tcp.git

猜你喜欢

转载自blog.csdn.net/qq513036862/article/details/110268063