%%%------------------------------------------------------------------- %%% @author anlicheng %%% @copyright (C) 2026, %%% @doc %%% %%% @end %%% Created : 11. 2月 2026 21:26 %%%------------------------------------------------------------------- -module(sdlan_quic_server). -author("anlicheng"). %% API -export([start_link/0, init/0]). start_link() -> {ok, spawn_link(?MODULE, init, [])}. init() -> process_flag(trap_exit, true), {ok, Props} = application:get_env(sdlan, quic_server), Port = proplists:get_value(port, Props), Alpn = proplists:get_value(alpn, Props), Limits = proplists:get_value(limits, Props), CertFile = proplists:get_value(certfile, Props), KeyFile = proplists:get_value(keyfile, Props), AcceptorCount = proplists:get_value(acceptors, Props, 10), %% 获取环境变量 Path = os:getenv("QUIC_CERT_PATH", code:priv_dir(sdlan)), LOptions = #{ addr_family => inet, % 必选:QUIC/TLS证书配置 certfile => Path ++ "/quic/" ++ CertFile, keyfile => Path ++ "/quic/" ++ KeyFile, alpn => Alpn, peer_bidi_stream_count => 1, conn_acceptors => AcceptorCount }, ListenAddr = "0.0.0.0:" ++ integer_to_list(Port), case quicer:listen(ListenAddr, LOptions) of {ok, L} -> logger:notice("[sdlan_quic_server] listen on ~s, acceptors: ~p", [ListenAddr, AcceptorCount]), Pids = [spawn_link(fun() -> loop_accept(L, Limits, I) end) || I <- lists:seq(1, AcceptorCount)], wait_acceptors(L, Pids); Error -> exit(Error) end. wait_acceptors(L, Pids) -> receive {'EXIT', Pid, Reason} -> logger:warning("[sdlan_quic_server] acceptor ~p exit with reason: ~p", [Pid, Reason]), quicer:close_listener(L), exit({acceptor_exit, Pid, Reason, Pids}); stop -> quicer:close_listener(L), ok; Info -> logger:notice("[sdlan_quic_server] get unexpected info: ~p", [Info]), wait_acceptors(L, Pids) end. loop_accept(L, Limits, AcceptorId) -> case quicer:accept(L, #{}, infinity) of {ok, Conn} -> logger:debug("[sdlan_quic_server] acceptor: ~p, accept a new connection: ~p", [AcceptorId, Conn]), case quicer:handshake(Conn) of {ok, NConn} -> case sdlan_quic_channel_sup:start_channel(NConn, Limits) of {ok, ChannelPid} -> logger:debug("[sdlan_quic_server] conn: ~p, handshake success, channel pid: ~p", [NConn, ChannelPid]), case quicer:controlling_process(NConn, ChannelPid) of ok -> sdlan_quic_transport:accept_stream(ChannelPid); {error, Reason} -> logger:warning("[sdlan_quic_server] conn: ~p, controlling_process failed: ~p", [NConn, Reason]), sdlan_quic_transport:stop(ChannelPid, {controlling_process_failed, Reason}), quicer:close_connection(NConn) end; Error -> quicer:close_connection(NConn), logger:notice("[sdlan_quic_server] start channel get error: ~p", [Error]) end, loop_accept(L, Limits, AcceptorId); {error, Reason} -> logger:debug("[sdlan_quic_server] acceptor: ~p, handshake failed: ~p", [AcceptorId, Reason]), quicer:close_connection(Conn), loop_accept(L, Limits, AcceptorId) end; {error, Reason} -> logger:debug("[sdlan_quic_server] acceptor: ~p, accept failed: ~p", [AcceptorId, Reason]), loop_accept(L, Limits, AcceptorId) end.