From 0f15f1da5796b2b10bc8b5fe8c101c1469cdb53a Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Wed, 11 Feb 2026 23:22:41 +0800 Subject: [PATCH] init quic server --- apps/sdlan/priv/cert.pem | 21 +++++ apps/sdlan/priv/key.pem | 28 ++++++ apps/sdlan/src/quic/sdlan_quic_conn.erl | 108 ++++++++++++++++++++++ apps/sdlan/src/quic/sdlan_quic_server.erl | 49 ++++++++++ apps/sdlan/src/sdlan_sup.erl | 8 ++ config/sys-dev.config | 5 + 6 files changed, 219 insertions(+) create mode 100644 apps/sdlan/priv/cert.pem create mode 100644 apps/sdlan/priv/key.pem create mode 100644 apps/sdlan/src/quic/sdlan_quic_conn.erl create mode 100644 apps/sdlan/src/quic/sdlan_quic_server.erl diff --git a/apps/sdlan/priv/cert.pem b/apps/sdlan/priv/cert.pem new file mode 100644 index 0000000..0efb341 --- /dev/null +++ b/apps/sdlan/priv/cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUEhYvMYhAARHRpKd1EOTR7HiRIVkwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNjAyMTEwOTMyMjlaFw0yNzAy +MTEwOTMyMjlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDWKHFkwDMH/XjY3joBwT3zzgh5WkqG7dKuj7YV7WdX +JjOc46iqNsvyieHTfuJBuPirLDAX2hGzU0OheeQBg/a9pFQxxyRBN2UZUFwTHPTG +TBBvdQGcC2vMQ3HnJwUoPzJRCYPBXQZ3JSmlq+y1uJzpLQfYeiRtowfGrRrd0jHJ +Xt5amitoN7m9VshG7KCg2K8AVriP/X5oiyNJ0s8kVdMFclUNekvWbuxik98VLWbF +dcB+kTaFUQkMj7y2ks6b6gWhw1wxPU4kWDEBaQMIICM2nZ+sTSUisKPBqpqlvC8N +NFFUhA+QW5SQuTa8t4iUxWsfIeuvPSrb1E1QzPwvld8PAgMBAAGjUzBRMB0GA1Ud +DgQWBBRKr5Ulk3xjskqncFCS22VxK2tCmTAfBgNVHSMEGDAWgBRKr5Ulk3xjskqn +cFCS22VxK2tCmTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAc +p/59bzORWJCKGJJ/GFrBIKKB9F1T1GoKy5rajxZeUCbCL22FaZ6VDmi8uUb4dYBU +5bwKOea76+J/cM1/Irt7n8c0d5nhIXm/ZdqERYjtv3F/MPi/X8Q4TP8uY6bA+dJr +enDaATGg+Jy2Iq6A6EGhYSmyxabgDkN2MNPtIwyekXoUMrA8D4jBGCQznjS3f1OW +9DMpVyb1qoz7WyAftyZkhIcwrBoTZ+C7e7ys9L8Q9uu7dKvdsYoJ73JQn4Rsgk6Y +jMGbxn6Td+z9m7aI6oPtY6UlxeA4scepX5Cx610cP0xnGJhf/o13e7jCxnr2/pTD +TTtahnT+G0FeQmlUswb9 +-----END CERTIFICATE----- diff --git a/apps/sdlan/priv/key.pem b/apps/sdlan/priv/key.pem new file mode 100644 index 0000000..5e3c9c9 --- /dev/null +++ b/apps/sdlan/priv/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDWKHFkwDMH/XjY +3joBwT3zzgh5WkqG7dKuj7YV7WdXJjOc46iqNsvyieHTfuJBuPirLDAX2hGzU0Oh +eeQBg/a9pFQxxyRBN2UZUFwTHPTGTBBvdQGcC2vMQ3HnJwUoPzJRCYPBXQZ3JSml +q+y1uJzpLQfYeiRtowfGrRrd0jHJXt5amitoN7m9VshG7KCg2K8AVriP/X5oiyNJ +0s8kVdMFclUNekvWbuxik98VLWbFdcB+kTaFUQkMj7y2ks6b6gWhw1wxPU4kWDEB +aQMIICM2nZ+sTSUisKPBqpqlvC8NNFFUhA+QW5SQuTa8t4iUxWsfIeuvPSrb1E1Q +zPwvld8PAgMBAAECggEAQ5DB6cP7ta8iI+XEzk3t4lAj+0lhzv0UZa+Ahp1+Z4/t +Y7etvHoKUUxwG35iGtMlXTfVOok54WZJJZZjuZitTXqdd5D5Hrw/4MMqMXuGvFM+ +MjPrnJQ739d9hayZY2/Ay7FhSK21PvzSDWtXBKQomSZ0Xbd3a8GuT9/IZKiOvZVl +Caf9IpWsfBCTJQCO0IpNrktTzxEHPNHZLKBd7HmpRmb7SNdPPaDM5Sl9B5N8pV1j +VafAsJjOcv2L0kTEc4Kq/VWO4fc625JTb7Zn3Q4VmQGdq/o+3Or1ojLME3GNz1YF +j+5cKjJf+ezAdvXYLCYqRaWS8UuxhxR++S0B8tGI/QKBgQD84U+YRKB+bURUjKz3 +6OsSY6q78OeGiQjLZBBLoA/724xIPElstpy4j3wylmhV+AW696Dp8+PMMqSpdhS8 +ts9QAtvrqFchu5aIhs5ZltYrNioinRri7F/FrFeDpdmt+YcNnY5p2QNI+5Tt01oG +i67qtxuVE+UOlUxDSaW+XpG2KwKBgQDYzNVKSX+IgB3aQV5SyRZJmEUsB8bxAnQ8 +K4vFBXtmAtOZibaJvokHe1Rp48NZHE4xlDKm4nfKx46XBW1EEPtaoyzL4gxwlSQN +LL80PhZM0wQBhL6Ya6TZPYPLSqv+KMuIGL9OFmavrlDroQMJbiUy9UJS+xFaouHt +EoaRq5tMrQKBgDErgjWCSo6qolmqTMubf3HA7WbDzdDr/kjF+SErS1BWfS1ig4he +7ZQ5WhXgBwOISVz0X1Z+NLH0uu20Zw3WofLVy3tD7UVC219Kjv7+hEA8tO6sC5lK +Csk93HpdmjjoxujP1Owh5TCgsnGX4e5Z5LYAyp0vFB/EyeJfhJnCe6SnAoGAfU5O +zSQUAVpDZt23XdP5/Ml02ZEZLD3F3u0wWMzlWL2zfZ+6EH0/CEMBND6/ruaMT12f +tRNaN6sFwEYTtG64SNfdUW4y0HNzJeZCETj9fKPOQe5ulvxIiINkhICBTmJX2S6s +i76o1UvEW5xxe+bcu0pEbl/M1P0l5fd6LgHovfUCgYADQHISMiW1JqCnM/Mu0WhD +i++BWMnUCb9BqFk/IPnKJUZUPLNKQ3izuvrNKpvd5HwSm7278h6SX2LaU+1D+Pcw +vvQGMhK7FDhO0E7qOu/pNDRtaNnKIeG76GoYAkGdm2dc3kY8mxlLIgegL/ateuf0 +AXBDlbmJJZgDYNioytFKnw== +-----END PRIVATE KEY----- diff --git a/apps/sdlan/src/quic/sdlan_quic_conn.erl b/apps/sdlan/src/quic/sdlan_quic_conn.erl new file mode 100644 index 0000000..eb78381 --- /dev/null +++ b/apps/sdlan/src/quic/sdlan_quic_conn.erl @@ -0,0 +1,108 @@ +%%%------------------------------------------------------------------- +%%% @author anlicheng +%%% @copyright (C) 2026, +%%% @doc +%%% +%%% @end +%%% Created : 11. 2月 2026 23:00 +%%%------------------------------------------------------------------- +-module(sdlan_quic_conn). +-author("anlicheng"). + +-behaviour(gen_statem). + +%% API +-export([start_link/1]). + +%% gen_statem callbacks +-export([init/1, handle_event/4, terminate/3, code_change/4, callback_mode/0]). + +-record(state, { + conn :: quicer:connection_handle(), + stream_handle :: undefined | quicer:stream_handle() +}). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc Creates a gen_statem process which calls Module:init/1 to +%% initialize. To ensure a synchronized start-up procedure, this +%% function does not return until Module:init/1 has returned. +start_link(Conn) -> + gen_statem:start_link(?MODULE, [Conn], []). + +%%%=================================================================== +%%% gen_statem callbacks +%%%=================================================================== + +%% @private +%% @doc Whenever a gen_statem is started using gen_statem:start/[3,4] or +%% gen_statem:start_link/[3,4], this function is called by the new +%% process to initialize. +init([Conn]) -> + {ok, initializing, #state{conn = Conn}, [{next_event, internal, do_init}]}. + +%% @private +%% @doc This function is called by a gen_statem when it needs to find out +%% the callback mode of the callback module. +callback_mode() -> + handle_event_function. + +%% @private +%% @doc If callback_mode is handle_event_function, then whenever a +%% gen_statem receives an event from call/2, cast/2, or as a normal +%% process message, this function is called. + +handle_event(internal, do_init, initializing, State = #state{conn = Conn}) -> + case quicer:accept_stream(Conn, []) of + {ok, Stream} -> + {next_state, initialized, State#state{stream_handle = Stream}}; + {error, closed} -> + {stop, connection_closed, State}; + {error, Reason} -> + logger:error("accept stream failed: ~p", [Reason]), + {stop, Reason, State} + end; + +%% 处理quicer相关的信息 +handle_event(info, {quic, Msg, Stream, _Props}, _StateName, State = #state{stream_handle = Stream}) -> + logger:debug("[sdlan_quic_stream] get message: ~p", [Msg]), + {keep_state, State}; + +handle_event(info, {quic_closed, Stream, _Props}, _StateName, State = #state{conn = Conn, stream_handle = Stream}) -> + quicer:close_connection(Conn), + {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}. + +%% @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, + quicer:close_connection(Conn), + logger:warning("[sdlan_quic_conn] terminate closed with reason: ~p", [Reason]), + ok. + +%% @private +%% @doc Convert process state when code is changed +code_change(_OldVsn, StateName, State = #state{}, _Extra) -> + {ok, StateName, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== diff --git a/apps/sdlan/src/quic/sdlan_quic_server.erl b/apps/sdlan/src/quic/sdlan_quic_server.erl new file mode 100644 index 0000000..d59f60e --- /dev/null +++ b/apps/sdlan/src/quic/sdlan_quic_server.erl @@ -0,0 +1,49 @@ +%%%------------------------------------------------------------------- +%%% @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() -> + {ok, Props} = application:get_env(sdlan, quic_server), + Port = proplists:get_value(port, Props), + Alpn = proplists:get_value(alpn, Props), + + Path = code:priv_dir(sdlan), + + LOptions = [ + {certfile, Path ++ "/cert.pem"}, + {keyfile, Path ++ "/key.pem"}, + {alpn, Alpn}, + {peer_bidi_stream_count, 1} + ], + {ok, L} = quicer:listen(Port, LOptions), + loop_accept(L). + +loop_accept(L) -> + case quicer:accept(L, [], infinity) of + {ok, Conn} -> + case quicer:handshake(Conn) of + ok -> + {ok, WorkerPid} = sdlan_quic_conn:start_link(Conn), + quicer:controlling_process(Conn, WorkerPid), + loop_accept(L); + {error, _} -> + quicer:close_connection(Conn), + loop_accept(L) + end; + {error, Reason} -> + logger:debug("[sdlan_quic_server] accept failed: ~p", [Reason]), + loop_accept(L) + end. \ No newline at end of file diff --git a/apps/sdlan/src/sdlan_sup.erl b/apps/sdlan/src/sdlan_sup.erl index 8c4b3b9..38b37dc 100644 --- a/apps/sdlan/src/sdlan_sup.erl +++ b/apps/sdlan/src/sdlan_sup.erl @@ -61,6 +61,14 @@ init([]) -> shutdown => 2000, type => supervisor, modules => ['sdlan_stun_sup'] + }, + #{ + id => sdlan_quic_server, + start => {sdlan_quic_server, start_link, []}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => ['sdlan_quic_server'] } ], diff --git a/config/sys-dev.config b/config/sys-dev.config index 9c78729..332d811 100644 --- a/config/sys-dev.config +++ b/config/sys-dev.config @@ -15,6 +15,11 @@ {backlog, 10240} ]}, + {quic_server, [ + {port, 1365}, + {alpn, ["punchnet/1.0"]} + ]}, + %% 网络带宽, 单位为: kb {band_width, 2048},