Desarrollo de protocolo privado 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).

El código anterior es para establecer una conexión tcp con el protocolo tcp privado emqx. Se basa principalmente en el grupo de conexiones de socket de esockd para construir varios archivos involucrados. El emqx_tcp se presenta a continuación.

Dirección del código fuente: https://github.com/tlchun/emqx-tcp.git

Supongo que te gusta

Origin blog.csdn.net/qq513036862/article/details/110268063
Recomendado
Clasificación