diff --git a/apps/sdlan/src/quic/sdlan_quic_channel.erl b/apps/sdlan/src/quic/sdlan_quic_channel.erl index 1b4ed9f..97e647b 100644 --- a/apps/sdlan/src/quic/sdlan_quic_channel.erl +++ b/apps/sdlan/src/quic/sdlan_quic_channel.erl @@ -20,18 +20,10 @@ %% 注册失败的的错误码 -%% token不存在 --define(NAK_INVALID_TOKEN, 1). -%% 节点被禁用 --define(NAK_NODE_DISABLE, 2). -%% 没有IP地址可以用 --define(NAK_NO_IP, 3). %% 网络错误 -define(NAK_NETWORK_FAULT, 4). %% 内部错误 -define(NAK_INTERNAL_FAULT, 5). -%% hostname被占用 --define(NAK_HOSTNAME_USED, 6). %% API -export([start_link/1]). @@ -42,32 +34,32 @@ -record(state, { conn :: quicer:connection_handle(), - stream_handle :: undefined | quicer:stream_handle(), + stream :: undefined | quicer:stream_handle(), %% 累积器,用于处理协议framing的解析 buf = <<>>, client_id :: undefined | binary(), + network_id = 0 :: integer(), %% 网络相关信息id network_pid :: undefined | pid(), %% mac地址 mac :: undefined | binary(), ip = 0 :: integer(), - ping_counter = 0, - - %% 标记是否已经注册 - is_registered = false + ping_counter = 0 }). %%%=================================================================== %%% API %%%=================================================================== -send_event(Pid, EventType, Event) -> +-spec send_event(Pid :: pid(), EventType :: integer(), Event :: binary()) -> no_return(). +send_event(Pid, EventType, Event) when is_pid(Pid), is_integer(EventType), is_binary(Event) -> gen_statem:cast(Pid, {send_event, EventType, Event}). -stop(Pid, Reason) -> - ok. +-spec stop(Pid :: pid(), Reason :: term()) -> ok. +stop(Pid, Reason) when is_pid(Pid) -> + gen_statem:stop(Pid, Reason, 2000). %% @doc Creates a gen_statem process which calls Module:init/1 to %% initialize. To ensure a synchronized start-up procedure, this @@ -98,21 +90,16 @@ callback_mode() -> %% process message, this function is called. handle_event(internal, do_init, initializing, State=#state{conn = Conn}) -> - logger:debug("[sdlan_quic_channel] conn: ~p, do_init", [Conn]), case quicer:accept_stream(Conn, #{active => true}) of {ok, Stream} -> logger:debug("[sdlan_quic_channel] get stream: ~p", [Stream]), - {next_state, initialized, State#state{stream_handle = Stream}}; + {next_state, initialized, State#state{stream = Stream}}; {error, Reason} -> - logger:error("accept stream failed: ~p", [Reason]), + logger:error("[sdlan_quic_channel] accept stream failed: ~p", [Reason]), {stop, Reason, State} end; -handle_event(info, {frame, Frame}, _StateName, State=#state{}) -> - logger:debug("[sdlan_quic_channel] get frame: ~p", [Frame]), - {keep_state, State}; - -handle_event(info, {frame, <>}, registered, State=#state{stream_handle = Stream}) -> +handle_event(info, {frame, <>}, initialized, State=#state{stream = Stream}) -> #sdl_register_super{ client_id = ClientId, network_id = NetworkId, mac = Mac, ip = Ip, mask_len = MaskLen, hostname = HostName, pub_key = PubKey, access_token = AccessToken} = sdlan_pb:decode_msg(Body, sdl_register_super), @@ -142,23 +129,18 @@ handle_event(info, {frame, <>} case sdlan_network:get_pid(NetworkId) of NetworkPid when is_pid(NetworkPid) -> {ok, AesKey, SessionToken} = sdlan_network:attach(NetworkPid, self(), ClientId, Mac, Ip, HostName), - RsaPubKey = sdlan_cipher:rsa_pem_decode(PubKey), - EncodedAesKey = rsa_encode(AesKey, RsaPubKey), - RegisterSuperAck = sdlan_pb:encode_msg(#sdl_register_super_ack { - aes_key = EncodedAesKey, + aes_key = rsa_encode(AesKey, RsaPubKey), session_token = SessionToken }), %% 发送确认信息 - Reply = <>, - - {ok, _} = quicer:send(Stream, <>), + {ok, _} = quicer:send(Stream, <>), %% 设置节点的在线状态 Result = sdlan_api:node_online(ClientId, NetworkId, sdlan_ipaddr:int_to_ipv4(Ip)), logger:debug("[sdlan_register_worker] client_id: ~p, set none online result is: ~p", [ClientId, Result]), - {next_state, registered, State#state{network_pid = NetworkPid, client_id = ClientId, mac = Mac, ip = Ip}}; + {next_state, registered, State#state{network_id = NetworkId, network_pid = NetworkPid, client_id = ClientId, mac = Mac, ip = Ip}}; undefined -> logger:warning("[sdlan_register_worker] client_id: ~p, register get error: network not found", [ClientId]), {ok, _} = quicer:send(Stream, register_nak_reply(PacketId, ?NAK_INTERNAL_FAULT, <<"Internal Error">>)), @@ -174,14 +156,16 @@ handle_event(info, {frame, <>} {stop, normal, State} end; -handle_event(info, {frame, <>}, registered, State=#state{stream_handle = Stream, network_pid = NetworkPid, mac = SrcMac, is_registered = true}) when is_pid(NetworkPid) -> +handle_event(info, {frame, <>}, registered, #state{stream = Stream, network_pid = NetworkPid, mac = SrcMac}) when is_pid(NetworkPid) -> #sdl_query_info{dst_mac = DstMac} = sdlan_pb:decode_msg(Body, sdl_query_info), case sdlan_network:peer_info(NetworkPid, SrcMac, DstMac) of error -> logger:debug("[sdlan_channel] query_info src_mac is: ~p, dst_mac: ~p, nat_peer not found", [sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac)]), - {ok, _} = quicer:send(Stream, <>), - {keep_state, State}; + + EmptyResponse = sdlan_pb:encode_msg(#sdl_empty{}), + {ok, _} = quicer:send(Stream, <>), + keep_state_and_data; {ok, {NatPeer = {{Ip0, Ip1, Ip2, Ip3}, NatPort}, NatType}, V6Info} -> logger:debug("[sdlan_channel] query_info src_mac is: ~p, dst_mac: ~p, nat_peer: ~p", [sdlan_util:format_mac(SrcMac), sdlan_util:format_mac(DstMac), NatPeer]), @@ -196,11 +180,10 @@ handle_event(info, {frame, <>}, re v6_info = V6Info }), {ok, _} = quicer:send(Stream, <>), - {keep_state, State} + keep_state_and_data end; -handle_event(info, {frame, <<0:32, ?PACKET_PING>>}, registered, State = #state{stream_handle = Stream, ping_counter = PingCounter}) -> - %logger:debug("[sdlan_channel] client_id: ~p, get ping", [ClientId]), +handle_event(info, {frame, <<0:32, ?PACKET_PING>>}, registered, State = #state{stream = Stream, ping_counter = PingCounter}) -> {ok, _} = quicer:send(Stream, <<0:32, ?PACKET_PONG>>), {keep_state, State#state{ping_counter = PingCounter + 1}}; @@ -216,19 +199,22 @@ handle_event(info, {timeout, _, ping_ticker}, _, State = #state{client_id = Clie end; %% 发送指令信息 -handle_event(info, {send_event, EventType, Event}, _, State = #state{stream_handle = Stream, client_id = ClientId, is_registered = true}) -> +handle_event(info, {send_event, EventType, Event}, registered, #state{stream = Stream, client_id = ClientId}) -> logger:debug("[sdlan_channel] client_id: ~p, will send eventType: ~p, event: ~p", [ClientId, EventType, Event]), {ok, _} = quicer:send(Stream, <<0:32, ?PACKET_EVENT, EventType, Event/binary>>), - {noreply, State}; + keep_state_and_data; %% 取消注册 -handle_event(info, {frame, <<0:32, ?PACKET_UNREGISTER>>}, registered, State = #state{client_id = ClientId, network_pid = NetworkPid, is_registered = true}) when is_pid(NetworkPid) -> +handle_event(info, {frame, <<0:32, ?PACKET_UNREGISTER>>}, registered, State=#state{client_id = ClientId, mac = Mac, network_pid = NetworkPid}) when is_pid(NetworkPid) -> logger:warning("[sdlan_channel] unregister client_id: ~p", [ClientId]), - % sdlan_network:unregister(NetworkPid, ClientId), + sdlan_network:unregister(NetworkPid, ClientId, Mac), {stop, normal, State}; +handle_event(info, {quic, send_shutdown_complete, Stream, _Props}, _StateName, State = #state{stream = Stream}) -> + {stop, connection_shutdown, State}; + %% 处理quicer相关的信息, 需要转换成内部能够识别的frame消息 -handle_event(info, {quic, Data, Stream, _Props}, _StateName, State = #state{stream_handle = Stream, buf = Buf}) -> +handle_event(info, {quic, Data, Stream, _Props}, _StateName, State = #state{stream = Stream, buf = Buf}) -> logger:debug("[sdlan_quic_channel] get message: ~p", [Data]), case decode_frames(<>) of {error, Reason} -> @@ -238,30 +224,19 @@ handle_event(info, {quic, Data, Stream, _Props}, _StateName, State = #state{stre {keep_state, State#state{buf = NBuf}, Actions} end; -handle_event(info, {quic_closed, Stream, _Props}, _StateName, State = #state{conn = Conn, stream_handle = Stream}) -> - quicer:close_connection(Conn), +handle_event(info, {quic_closed, Stream, _Props}, _StateName, State = #state{stream = Stream}) -> {stop, connection_closed, State}; -handle_event(info, {'EXIT', _, _}, _StateName, State = #state{conn = Conn}) -> - quicer:close_connection(Conn), - {stop, connection_closed, State}; - -handle_event(_EventType, _EventContent, _StateName, State = #state{}) -> - NextStateName = the_next_state_name, - {next_state, NextStateName, State}. +handle_event(info, {'EXIT', _, _}, _StateName, State) -> + {stop, connection_closed, State}. %% @private %% @doc This function is called by a gen_statem when it is about to %% terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_statem terminates with %% Reason. The return value is ignored. -terminate(Reason, _StateName, _State = #state{conn = Conn, stream_handle = Stream}) -> - case Stream /= undefined of - true -> - quicer:close_stream(Stream); - false -> - ok - end, +terminate(Reason, _StateName, _State = #state{conn = Conn, stream = Stream}) -> + Stream /= undefined andalso quicer:close_stream(Stream), quicer:close_connection(Conn), logger:warning("[sdlan_quic_conn] terminate closed with reason: ~p", [Reason]), ok. diff --git a/apps/sdlan/src/quic/sdlan_quic_channel_sup.erl b/apps/sdlan/src/quic/sdlan_quic_channel_sup.erl new file mode 100644 index 0000000..b93c4cd --- /dev/null +++ b/apps/sdlan/src/quic/sdlan_quic_channel_sup.erl @@ -0,0 +1,65 @@ +%%%------------------------------------------------------------------- +%%% @author anlicheng +%%% @copyright (C) 2026, +%%% @doc +%%% +%%% @end +%%% Created : 13. 2月 2026 18:00 +%%%------------------------------------------------------------------- +-module(sdlan_quic_channel_sup). +-author("anlicheng"). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). +-export([start_channel/1]). + +%% Supervisor callbacks +-export([init/1]). + +-define(SERVER, ?MODULE). + +%%%=================================================================== +%%% API functions +%%%=================================================================== + +%% @doc Starts the supervisor +-spec(start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}). +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%%%=================================================================== +%%% Supervisor callbacks +%%%=================================================================== + +%% @private +%% @doc Whenever a supervisor is started using supervisor:start_link/[2,3], +%% this function is called by the new process to find out about +%% restart strategy, maximum restart frequency and child +%% specifications. +-spec(init(Args :: term()) -> + {ok, {SupFlags :: {RestartStrategy :: supervisor:strategy(), + MaxR :: non_neg_integer(), MaxT :: non_neg_integer()}, + [ChildSpec :: supervisor:child_spec()]}} + | ignore | {error, Reason :: term()}). +init([]) -> + SupFlags = #{strategy => simple_one_for_one, intensity => 0, period => 1}, + + AChild = #{ + id => sdlan_quic_channel, + start => {'sdlan_quic_channel', start_link, []}, + restart => temporary, + shutdown => 2000, + type => worker, + modules => ['sdlan_quic_channel'] + }, + {ok, {SupFlags, [AChild]}}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +-spec start_channel(NConn :: quicer:connection_handle()) -> supervisor:startchild_ret(). +start_channel(NConn) -> + supervisor:start_child(?MODULE, [NConn]). \ No newline at end of file diff --git a/apps/sdlan/src/quic/sdlan_quic_server.erl b/apps/sdlan/src/quic/sdlan_quic_server.erl index b94ed14..78fe1d2 100644 --- a/apps/sdlan/src/quic/sdlan_quic_server.erl +++ b/apps/sdlan/src/quic/sdlan_quic_server.erl @@ -45,8 +45,13 @@ loop_accept(L) -> case quicer:handshake(Conn) of {ok, NConn} -> logger:debug("[sdlan_quic_server] conn: ~p, handshake success", [NConn]), - {ok, ChannelPid} = sdlan_quic_channel:start_link(NConn), - quicer:controlling_process(NConn, ChannelPid), + case sdlan_quic_channel_sup:start_channel(NConn) of + {ok, ChannelPid} -> + quicer:controlling_process(NConn, ChannelPid); + Error -> + quicer:close_connection(NConn), + logger:notice("[sdlan_quic_server] start channel get error: ~p", [Error]) + end, loop_accept(L); {error, _} -> quicer:close_connection(Conn), diff --git a/apps/sdlan/src/sdlan_sup.erl b/apps/sdlan/src/sdlan_sup.erl index 38b37dc..6e2e18f 100644 --- a/apps/sdlan/src/sdlan_sup.erl +++ b/apps/sdlan/src/sdlan_sup.erl @@ -62,6 +62,16 @@ init([]) -> type => supervisor, modules => ['sdlan_stun_sup'] }, + + #{ + id => sdlan_quic_channel_sup, + start => {sdlan_quic_channel_sup, start_link, []}, + restart => permanent, + shutdown => 2000, + type => supervisor, + modules => ['sdlan_quic_channel_sup'] + }, + #{ id => sdlan_quic_server, start => {sdlan_quic_server, start_link, []}, diff --git a/config/sys-dev.config b/config/sys-dev.config index 48e230e..116fd15 100644 --- a/config/sys-dev.config +++ b/config/sys-dev.config @@ -3,7 +3,7 @@ {http_server, [ {port, 18082}, - {acceptors, 500}, + {acceptors, 1}, {max_connections, 10240}, {backlog, 10240} ]},