emqx私有tcp协议服务器开发---emqx_tcp_protocol模块(1)

-module(emqx_tcp_protocol).

-export([logger_header/0]).

-include("emqx_tcp/include/emqx_tcp.hrl").
-include("emqx/include/emqx.hrl").
-include("/emqx/include/logger.hrl").
-import(proplists, [get_value/2]).
-import(emqx_misc, [maybe_apply/2]).

-export([init/2,received/2,
         deliver/2,terminate/2,maybe_gc_and_check_oom/2]).
-export([info/1, client_id/1, stats/1]).

-record(pstate,
        {peername,
         sockname,
         peercert,
         client_id,
         username,
         keepalive,
         sendfun,  %% 发送函数
         conn_pid, %% 连接进程ID
         connected = false,
         up_topic, %% 上行主题
         dn_topic, %% 下行主题
         proto_ver,%% 协议版本
         conn_mod, %% 连接模块
         connected_at, %% 连接时间
         gc_state,     %% gc状态
         recv_stats,   %% 接收统计
         send_stats}). %% 发送统计

-vsn("4.2.1").
%% 初始化
init(SocketOpts = #{sockname := Sockname,peername := Peername, peercert :=         
    Peercert,sendfun := SendFun},Options) ->
    #pstate{sockname = Sockname, peername = Peername,
            peercert = Peercert,
            up_topic = get_value(up_topic, Options),
            dn_topic = get_value(dn_topic, Options),
            conn_pid = self(), sendfun = SendFun,
            gc_state = init_gc_state(),
            conn_mod = maps:get(conn_mod, SocketOpts, undefined),
            recv_stats = #{msg => 0, pkt => 0},
            send_stats = #{msg => 0, pkt => 0}}.

%% gc状态初始化
init_gc_state() ->
    %% 获取gc配置策略
    GcPolicy = application:get_env(emqx_tcp,force_gc_policy,undefined),                          
    maybe_apply(fun emqx_gc:init/1, GcPolicy).

%% 接收数据
received(Packet = #tcp_packet_conn{},PState = #pstate{connected = false}) ->
    process(Packet, PState#pstate{connected = true});
%% 已经连接    
received(#tcp_packet_conn{},PState = #pstate{connected = true}) -> 
    {error, protocol_has_connected, PState};
%% 未连接
received(_Packet,PState = #pstate{connected = false}) ->
    {error, protocol_not_connected, PState};     
    
received(Packet, PState) -> process(Packet, PState).
%% 处理连接数据包
process(#tcp_packet_conn{client_id = ClientId,keepalive = Keepalive, username =     
                        Username, password = Password},PState) ->
    
    PState1 = prepare_adapter_topic(PState#pstate{client_id = ClientId,username =             
                                     Username, keepalive = Keepalive,
                                     connected_at =erlang:system_time(millisecond)}),                                          
    %% 客户端连接信息
    ClientInfo = clientinfo(PState1),
    %% 
    _ = run_hooks('client.connect', [conninfo(PState1)], undefined),
    %% 
    connack(case emqx_access_control:authenticate(ClientInfo#{password =>Password}) of
                {ok, _ClientInfo0} ->
                    emqx_logger:set_metadata_clientid(ClientId),                                             
					ok =             
                    emqx_cm:register_channel(ClientId,info(PState1),stats(PState1)),                    
                    autosubcribe(PState1),
                    start_keepalive(Keepalive, PState1),
                    {0, PState1};
                {error, Reason} ->
                    begin
                        logger:log(warning,
                                   #{},
                                   #{report_cb =>
                                         fun (_) ->
                                                 {'$logger_header'() ++
                                                      "TCP Client ~s (Username: '~s') login "
                                                      "failed for ~p",
                                                  [ClientId, Username, Reason]}
                                         end,
                                     mfa => {emqx_tcp_protocol, process, 2},
                                     line => 113})
                    end,
                    {1, <<"Authentication Failure">>, PState1}
            end);
%% 处理业务数据包
process(Packet = #tcp_packet_datatrans{}, PState) ->do_publish(Packet, PState);   
%% 处理ping,pong
process(#tcp_packet_ping{}, PState) ->deliver(pong, PState);  
%% 处理断开连接数据包
process(#tcp_packet_disconn{}, PState) ->{stop, normal, PState};
%% 处理非法数据包
process(Packet, PState) ->{error, {unknown_packet, Packet}, PState}.
terminate(_Reason, #pstate{client_id = undefined}) -> ok;
terminate(_Reason, #pstate{connected = false}) -> ok;
terminate(Reason, PState) ->
    begin
        logger:log(info,
                   #{},
                   #{report_cb =>
                         fun (_) ->
                                 {'$logger_header'() ++ "Shutdown for ~p",
                                  [Reason]}
                         end,
                     mfa => {emqx_tcp_protocol, terminate, 2}, line => 134})
    end,
    ConnInfo = conninfo(PState),
    ConnInfo1 = maps:put(disconnected_at,erlang:system_time(millisecond), ConnInfo),
    ok = emqx_hooks:run('client.disconnected',[clientinfo(PState), Reason, ConnInfo1]). 
                    

猜你喜欢

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