emqx私有tcp协议服务器开发---emqx_tcp_frame模块

-module(emqx_tcp_frame).
-include("/emqx_tcp/include/emqx_tcp.hrl").
-export([initial_parse_state/1, parse/2, serialize/2]).
-export([format/1]).

%% 初始化解析状态
initial_parse_state(Options) when is_map(Options) ->{none, merge_opts(Options)}.
%% 合并解析配置
merge_opts(Options) ->maps:merge(#{max_size => 65535, version => 1}, Options).
%% 解析空数据
parse(<<>>, {none, Options}) ->
    {ok,{more, fun (Bin) -> parse(Bin, {none, Options}) end}};
%% 读取数据包的类型和标志     
parse(<<Type:4, Flags:4, Rest/binary>>, {none, Options}) ->
      parse_frame_type(Type, Flags, Rest, Options);
parse(Bin, {more, Cont})when is_binary(Bin), is_function(Cont) -> Cont(Bin).
     
%% 解析类型为Type = 1(FRAME_TYPE_CONN 连接类型),Flags = 1的数据包
parse_frame_type(1, 1, Rest, Options) ->
    case run_read_funs([fun read_length_binary/1], Rest) of
        {ok, Rest1, [ConnPayload]} ->
            %% 解析连接负载
            case parse_conn_payload(ConnPayload) of
                {error, Reason} -> {error, Reason};
                %% 获取用户名和密码
                {Keepalive, ClientId, Username, Password} ->
                    Pkt = #tcp_packet_conn{client_id = ClientId,
                                           keepalive = Keepalive,
                                           username = Username,
                                           password = Password, version = 1},
                    {ok, Pkt, Rest1, {none, Options#{version => 1}}}
            end;
        {more, Rest} ->
            {ok,
             {more,fun (Bin) ->
                  parse_frame_type(1,1,<<Rest/binary, Bin/binary>>,Options)
                  end}}                         
    end;
parse_frame_type(1, Version, _Rest, _Options) ->
    {error, {not_supported_version, Version}};

%% FRAME_TYPE_DATATRANS 业务数据报文
parse_frame_type(3, Flags, Rest,Options = #{max_size := MaxSize}) ->
                 
    case run_read_funs([fun read_length_binary/1], Rest) of
        {ok, Rest1, [Data]} ->
            %% 计算数据报文
            case byte_size(Data) of
                %% 数据长度大于最大长度
                Len when Len > MaxSize ->
                    {error, {max_size_limit, Len, MaxSize}};
                Len ->
                    %% 构造数据报文
                    Pkt = #tcp_packet_datatrans{length = Len, data = Data},
                    %% 返回ok
                    {ok, Pkt, Rest1, {none, Options}}
            end;
        %% 有更多数据包仍需处理
        {more, Rest} ->
            {ok,
             {more,
              fun (Bin) ->
                      parse_frame_type(3,Flags,<<Rest/binary, Bin/binary>>,Options)                
              end}}
    end;
%% 解析报文类型为4的数据包
parse_frame_type(4, _Flags, Rest, Options) ->
    {ok, #tcp_packet_ping{}, Rest, {none, Options}};
%% 解析报文类型为6的数据包
parse_frame_type(6, _Flags, Rest, Options) ->
    {ok, #tcp_packet_disconn{}, Rest, {none, Options}};

%% 非法报文
parse_frame_type(Type, Flags, Rest, Options) ->
    {error, {illegal_frame, {Type, Flags, Rest, Options}}}.
%% 解析连接报文
parse_conn_payload(<<K:8, L1:16, ClientId:L1/binary>>) ->
      {K, ClientId, undefined, undefined};              
parse_conn_payload(<<K:8, L1:16, ClientId:L1/binary, L2:16, Username:L2/binary>>) ->
      {K, ClientId, Username, undefined};             
parse_conn_payload(<<K:8, L1:16, ClientId:L1/binary,L2:16, Username:L2/binary, L3:16,
                      Password:L3/binary>>) ->
      {K, ClientId, Username, Password};
parse_conn_payload(_) ->{error, invalid_conn_payload_format}.
    
%% 读取二进制里面的数据
-spec run_read_funs(list(), binary()) -> {ok,binary(),
                                          ReadResult :: list()} |{more, binary()}.
run_read_funs(Funs, Bin) when is_list(Funs) and is_binary(Bin) ->
    
    case run_read_funs(Funs, Bin, []) of
        {ok, Remaining, Results} -> {ok, Remaining, Results};
        {pause, _, _} -> {more, Bin}
    end.

run_read_funs([], Bin, Acc) ->{ok, Bin, lists:reverse(Acc)};
run_read_funs([Fun | RFuns], Bin, Acc) ->
    case Fun(Bin) of
        {more, Bin} -> {pause, Bin, lists:reverse(Acc)};
        {Content, RestBin} -> run_read_funs(RFuns, RestBin, [Content | Acc])
           
    end.
%% 读数据长度
-spec read_length_binary(binary()) -> {more, binary()} |
                                      {Content :: binary(), Rest :: binary()}.
%% 小于2,说明数据包未读完
read_length_binary(Bin) when byte_size(Bin) < 2 ->
    {more, Bin};
%% 匹配两个字节数据,取出长度值
read_length_binary(<<Len:16, Rest/binary>> = Bin) ->
    %% 如果数据包Rest的长度大于提取的长度,说明还有更多数据
    case byte_size(Rest) >= Len of
        false -> {more, Bin};
        true ->
            %% 提取Rest里面的数据{数据长度,数据内容}
            <<Content:Len/binary, Rest1/binary>> = Rest,
            %% 返回内容和剩余的数据Rest1
            {Content, Rest1}
    end.

%% 序列化连接数据包
serialize(#tcp_packet_conn{client_id = ClientId,keepalive = Keepalive, 
                           username = Username,
                           password = Password, version = Version}, _Opts) ->
    %% 负载数据二进制化     
    Payload = <<Keepalive:8, (lbin(ClientId))/binary,
                (encode_username_and_passowrd(Username, Password))/binary>>,
    %% 计算负载部分数据长度                                         
    LenOfPaylaod = byte_size(Payload),
    %% 组成数据包返回
    <<1:4, Version:4, LenOfPaylaod:16, Payload/binary>>;
%% 连接确认数据包序列化
serialize(#tcp_packet_connack{code = Code, msg = Msg},_Opts) ->
          <<2:4, Code:4, (lbin(Msg))/binary>>;
%% 业务数据包    
serialize(#tcp_packet_datatrans{data = Data}, _Opts) ->
    <<3:4, 0:4, (lbin(Data))/binary>>;
serialize(#tcp_packet_ping{}, _Opts) -> <<4:4, 0:4>>;
serialize(#tcp_packet_pong{}, _Opts) -> <<5:4, 0:4>>;
serialize(#tcp_packet_disconn{}, _Opts) -> <<6:4, 0:4>>.

%% 用户和密码内容编码
encode_username_and_passowrd(undefined, undefined) -><<>>;
encode_username_and_passowrd(Username, undefined) -><<(lbin(Username))/binary>>;
encode_username_and_passowrd(Username, Password)when is_binary(Username), 
    is_binary(Password) ->
    <<(lbin(Username))/binary, (lbin(Password))/binary>>;
encode_username_and_passowrd(Username, Password) ->
    error({not_supported_username_password,Username,Password}).
           
lbin(B) when is_binary(B) -><<(byte_size(B)):16, B/binary>>.
format(#tcp_packet_conn{client_id = ClientId,username = Username}) ->                
    io_lib:format("CONNECT(client_id=~s, username=~p)",[ClientId, Username]);
                  
format(#tcp_packet_connack{code = Code, msg = Msg}) ->
    io_lib:format("CONNACK(code=~p, msg=~s)", [Code, Msg]);
format(#tcp_packet_datatrans{length = Len, data = Data}) ->              
    io_lib:format("DATATRANS(length=~p, data=~p)",[Len, Data]);
                  
format(#tcp_packet_ping{}) -> io_lib:format("PING", []);
format(#tcp_packet_pong{}) -> io_lib:format("PONG", []);
format(#tcp_packet_disconn{}) ->io_lib:format("DISCONN", []).
    

猜你喜欢

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