一、基本功能介绍
1、esockd是erlang异步非阻塞TCP/SSL Socket服务器框架
2、 支持Acceptor池与异步Accept
3、支持UDP/DTLS 服务器
4、支持最大连接数管理
5、支持动态对指定的ip进行否定和允许
6、客户端限速支持
7、ip6支持
二、架构图
三、从esockd.erl 文件开始
该模块包含了核心API ,管理API,工具函数定义,类型定义等功能
esockd源码如下:
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(esockd).
-include("esockd.hrl").
-export([start/0]).
%% Core API
-export([open/4, open_udp/4, open_dtls/4, close/2, close/1]).
-export([reopen/1, reopen/2]).
-export([child_spec/4, udp_child_spec/4, dtls_child_spec/4]).
%% Management API
-export([listeners/0, listener/1]).
-export([get_stats/1, get_options/1, get_acceptors/1]).
-export([get_max_connections/1, set_max_connections/2, get_current_connections/1]).
-export([get_shutdown_count/1]).
%% Allow, Deny API
-export([get_access_rules/1, allow/2, deny/2]).
%% Utility functions
-export([parse_opt/1, ulimit/0, fixaddr/1, to_string/1]).
-type(proto() :: atom()).
%% transport()定义为module类型
-type(transport() :: module()).
%%
-type(udp_transport() :: {udp | dtls, pid(), inet:socket()}).
%% sock() 定义为 esockd_transport:sock()
-type(sock() :: esockd_transport:sock()).
-type(mfargs() :: atom() | {atom(), atom()} | {module(), atom(), [term()]}).
-type(sock_fun() :: fun((esockd_transport:sock()) -> {ok, esockd_transport:sock()} | {error, term()})).
-type(option() :: {acceptors, pos_integer()}|{max_connections, pos_integer()}
| {max_conn_rate, pos_integer() | {pos_integer(), pos_integer()}}
| {access_rules, [esockd_access:rule()]}|{shutdown, brutal_kill | infinity | pos_integer()}
| tune_buffer | {tune_buffer, boolean()}| proxy_protocol | {proxy_protocol, boolean()}
| {proxy_protocol_timeout, timeout()}| {ssl_options, [ssl:ssl_option()]}
| {tcp_options, [gen_tcp:listen_option()]}| {udp_options, [gen_udp:option()]}
| {dtls_options, [gen_udp:option() | ssl:ssl_option()]}).
%% 定义host类型,可能类型是inet:ip_address() 或者 string()
-type(host() :: inet:ip_address() | string()).
%% 定义listen_on() 可能类型是inet:port_number(),或者是ip和port的元组
-type(listen_on() :: inet:port_number() | {host(), inet:port_number()}).
%% 导出接口
-export_type([proto/0, transport/0, udp_transport/0, sock/0, sock_fun/0, mfargs/0, option/0, listen_on/0]).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%% @doc Start esockd application.
-spec(start() -> ok).
start() ->
{ok, _} = application:ensure_all_started(esockd), ok.
%% @doc Open a TCP or SSL listener
%% Proto: 该参数是一个TCP服务的名称,
%% listen_on(): 是一个类型定义: -type(listen_on() :: inet:port_number() | {host(), inet:port_number()}). 服务的port或者服务器的ip和port
%% [option()]:这是一个数组参数,option()是一个类型定义:是关于TCP服务器的一些配置
%% mfargs类型定义:-type(mfargs() :: atom() | {atom(), atom()} | {module(), atom(), [term()]}).
%% 函数返回值:{ok,pid()} 或者{error,term()}
-spec(open(atom(), listen_on(), [option()], mfargs()) -> {ok, pid()} | {error, term()}).
%% 判断Proto是不是原子,Port是不是一个整数
open(Proto, Port, Opts, MFA) when is_atom(Proto), is_integer(Port) ->
%% 调用 esockd_sup 模块的start_listener 方法,比如:
%% Opts = [{acceptors, 1}, {max_connections, 1024}, {tcp_options, [binary, {reuseaddr, true}]}].
%% MFA = {echo_server, start_link, []},
%% esockd:open(echo, 5000, Options, MFArgs).
esockd_sup:start_listener(Proto, Port, Opts, MFA);
%% 该方法跟上面的方法唯一区别就是第一个方法传递一个端口port就可以,该方法传递了一个元组{Host,Port}
open(Proto, {Host, Port}, Opts, MFA) when is_atom(Proto), is_integer(Port) ->
%% 判断host和port,返回ip和port
{IPAddr, _Port} = fixaddr({Host, Port}),
%% 通过ip获取和tcp配置的参数
case proplists:get_value(ip, tcp_options(Opts)) of
%% 如果没有定义,返回ok
undefined -> ok;
%% 如果有定义,返回ok
IPAddr -> ok;
%% 如果没有,就返回匹配错误
Other -> error({badmatch, Other})
end,
%% 调用esockd_sup模块start_listener方法
esockd_sup:start_listener(Proto, {IPAddr, Port}, Opts, MFA).
tcp_options(Opts) ->
proplists:get_value(tcp_options, Opts, []).
%% 打开udp服务
%% Proto:属性名 Port:端口 Opts:配置 MFA:模块,方法,参数元组
open_udp(Proto, Port, Opts, MFA) ->
esockd_sup:start_child(udp_child_spec(Proto, Port, Opts, MFA)).
udp_child_spec(Proto, Port, Opts, MFA) ->
esockd_sup:udp_child_spec(Proto, fixaddr(Port), udp_options(Opts), MFA).
%% udp option参数获取
udp_options(Opts) ->
proplists:get_value(udp_options, Opts, []).
%% 开启udp tls服务
open_dtls(Proto, ListenOn, Opts, MFA) ->
esockd_sup:start_child(dtls_child_spec(Proto, ListenOn, Opts, MFA)).
%% udp tsl 子进程规范
dtls_child_spec(Proto, ListenOn, Opts, MFA) ->
esockd_sup:dtls_child_spec(Proto, fixaddr(ListenOn), Opts, MFA).
%% @doc Child spec for a listener
-spec(child_spec(atom(), listen_on(), [option()], mfargs()) -> supervisor:child_spec()).
child_spec(Proto, ListenOn, Opts, MFA) when is_atom(Proto) ->
esockd_sup:child_spec(Proto, fixaddr(ListenOn), Opts, MFA).
%%关闭监听
-spec(close({atom(), listen_on()}) -> ok | {error, term()}).
close({Proto, ListenOn}) when is_atom(Proto) ->
close(Proto, ListenOn).
-spec(close(atom(), listen_on()) -> ok | {error, term()}).
close(Proto, ListenOn) when is_atom(Proto) ->
esockd_sup:stop_listener(Proto, fixaddr(ListenOn)).
%% @doc Reopen the listener 从新打开监听
-spec(reopen({atom(), listen_on()}) -> {ok, pid()} | {error, term()}).
reopen({Proto, ListenOn}) when is_atom(Proto) ->
reopen(Proto, ListenOn).
-spec(reopen(atom(), listen_on()) -> {ok, pid()} | {error, term()}).
reopen(Proto, ListenOn) when is_atom(Proto) ->
esockd_sup:restart_listener(Proto, fixaddr(ListenOn)).
%% @doc Get listeners.
-spec(listeners() -> [{{atom(), listen_on()}, pid()}]).
listeners() -> esockd_sup:listeners().
%% @doc Get one listener.
-spec(listener({atom(), listen_on()}) -> pid() | undefined).
listener({Proto, ListenOn}) when is_atom(Proto) ->
esockd_sup:listener({Proto, fixaddr(ListenOn)}).
%% @doc Get stats
-spec(get_stats({atom(), listen_on()}) -> [{atom(), non_neg_integer()}]).
get_stats({Proto, ListenOn}) when is_atom(Proto) ->
esockd_server:get_stats({Proto, fixaddr(ListenOn)}).
%% @doc Get options
-spec(get_options({atom(), listen_on()}) -> undefined | pos_integer()).
get_options({Proto, ListenOn}) when is_atom(Proto) ->
with_listener({Proto, ListenOn}, fun get_options/1);
get_options(LSup) when is_pid(LSup) ->
esockd_listener:options(esockd_listener_sup:listener(LSup)).
%% 获取socket接收者数量
-spec(get_acceptors({atom(), listen_on()}) -> undefined | pos_integer()).
get_acceptors({Proto, ListenOn}) ->
with_listener({Proto, ListenOn}, fun get_acceptors/1);
get_acceptors(LSup) when is_pid(LSup) ->
AcceptorSup = esockd_listener_sup:acceptor_sup(LSup),
esockd_acceptor_sup:count_acceptors(AcceptorSup).
%% 得到最大的连接数
-spec(get_max_connections({atom(), listen_on()} | pid()) -> undefined | pos_integer()).
get_max_connections({Proto, ListenOn}) when is_atom(Proto) ->
with_listener({Proto, ListenOn}, fun get_max_connections/1);
get_max_connections(LSup) when is_pid(LSup) ->
ConnSup = esockd_listener_sup:connection_sup(LSup),
esockd_connection_sup:get_max_connections(ConnSup).
%% 调用接口设置应用最大的连接数
-spec(set_max_connections({atom(), listen_on()} | pid(), pos_integer()) -> undefined | pos_integer()).
set_max_connections({Proto, ListenOn}, MaxConns) when is_atom(Proto) ->
with_listener({Proto, ListenOn}, fun set_max_connections/2, [MaxConns]);
set_max_connections(LSup, MaxConns) when is_pid(LSup) ->
ConnSup = esockd_listener_sup:connection_sup(LSup),
esockd_connection_sup:set_max_connections(ConnSup, MaxConns).
%% 获取当前系统的连接数
-spec(get_current_connections({atom(), listen_on()}) -> undefined | non_neg_integer()).
get_current_connections({Proto, ListenOn}) when is_atom(Proto) ->
with_listener({Proto, ListenOn}, fun get_current_connections/1);
get_current_connections(LSup) when is_pid(LSup) ->
ConnSup = esockd_listener_sup:connection_sup(LSup),
esockd_connection_sup:count_connections(ConnSup).
%% @doc Get shutdown count
-spec(get_shutdown_count({atom(), listen_on()}) -> undefined | pos_integer()).
get_shutdown_count({Proto, ListenOn}) when is_atom(Proto) ->
with_listener({Proto, ListenOn}, fun get_shutdown_count/1);
get_shutdown_count(LSup) when is_pid(LSup) ->
ConnSup = esockd_listener_sup:connection_sup(LSup),
esockd_connection_sup:get_shutdown_count(ConnSup).
%% @doc Get access rules
-spec(get_access_rules({atom(), listen_on()}) -> [esockd_access:rule()] | undefined).
get_access_rules({Proto, ListenOn}) when is_atom(Proto) ->
with_listener({Proto, ListenOn}, fun get_access_rules/1);
get_access_rules(LSup) when is_pid(LSup) ->
ConnSup = esockd_listener_sup:connection_sup(LSup),
esockd_connection_sup:access_rules(ConnSup).
%% @doc Allow access address
-spec(allow({atom(), listen_on()}, all | esockd_cidr:cidr_string()) -> ok | {error, term()}).
allow({Proto, ListenOn}, CIDR) when is_atom(Proto) ->
LSup = listener({Proto, ListenOn}),
ConnSup = esockd_listener_sup:connection_sup(LSup),
esockd_connection_sup:allow(ConnSup, CIDR).
%% @doc Deny access address
-spec(deny({atom(), listen_on()}, all | esockd_cidr:cidr_string()) -> ok | {error, term()}).
deny({Proto, ListenOn}, CIDR) when is_atom(Proto) ->
LSup = listener({Proto, ListenOn}),
ConnSup = esockd_listener_sup:connection_sup(LSup),
esockd_connection_sup:deny(ConnSup, CIDR).
%% @doc Parse option.
parse_opt(Options) ->
parse_opt(Options, []).
parse_opt([], Acc) ->
lists:reverse(Acc);
parse_opt([{acceptors, I}|Opts], Acc) when is_integer(I) ->
parse_opt(Opts, [{acceptors, I}|Acc]);
parse_opt([{max_connections, I}|Opts], Acc) when is_integer(I) ->
parse_opt(Opts, [{max_connections, I}|Acc]);
parse_opt([{max_conn_rate, Limit}|Opts], Acc) when Limit > 0 ->
parse_opt(Opts, [{max_conn_rate, {Limit, 1}}|Acc]);
parse_opt([{max_conn_rate, {Limit, Period}}|Opts], Acc) when Limit > 0, Period >0 ->
parse_opt(Opts, [{max_conn_rate, {Limit, Period}}|Acc]);
parse_opt([{access_rules, Rules}|Opts], Acc) ->
parse_opt(Opts, [{access_rules, Rules}|Acc]);
parse_opt([{shutdown, I}|Opts], Acc) when I == brutal_kill; I == infinity; is_integer(I) ->
parse_opt(Opts, [{shutdown, I}|Acc]);
parse_opt([tune_buffer|Opts], Acc) ->
parse_opt(Opts, [{tune_buffer, true}|Acc]);
parse_opt([{tune_buffer, I}|Opts], Acc) when is_boolean(I) ->
parse_opt(Opts, [{tune_buffer, I}|Acc]);
parse_opt([proxy_protocol|Opts], Acc) ->
parse_opt(Opts, [{proxy_protocol, true}|Acc]);
parse_opt([{proxy_protocol, I}|Opts], Acc) when is_boolean(I) ->
parse_opt(Opts, [{proxy_protocol, I}|Acc]);
parse_opt([{proxy_protocol_timeout, Timeout}|Opts], Acc) when is_integer(Timeout) ->
parse_opt(Opts, [{proxy_protocol_timeout, Timeout}|Acc]);
parse_opt([{ssl_options, L}|Opts], Acc) when is_list(L) ->
parse_opt(Opts, [{ssl_options, L}|Acc]);
parse_opt([{tcp_options, L}|Opts], Acc) when is_list(L) ->
parse_opt(Opts, [{tcp_options, L}|Acc]);
parse_opt([{udp_options, L}|Opts], Acc) when is_list(L) ->
parse_opt(Opts, [{udp_options, L}|Acc]);
parse_opt([{dtls_options, L}|Opts], Acc) when is_list(L) ->
parse_opt(Opts, [{dtls_options, L}|Acc]);
parse_opt([_|Opts], Acc) ->
parse_opt(Opts, Acc).
%% @doc System 'ulimit -n'
-spec(ulimit() -> pos_integer()).
ulimit() ->
proplists:get_value(max_fds, erlang:system_info(check_io)).
with_listener({Proto, ListenOn}, Fun) ->
with_listener({Proto, ListenOn}, Fun, []).
with_listener({Proto, ListenOn}, Fun, Args) ->
LSup = listener({Proto, ListenOn}),
with_listener(LSup, Fun, Args);
with_listener(undefined, _Fun, _Args) ->
undefined;
with_listener(LSup, Fun, Args) when is_pid(LSup) ->
erlang:apply(Fun, [LSup | Args]).
-spec(to_string(listen_on()) -> string()).
%% 端口port integer 转 string
to_string(Port) when is_integer(Port) ->
integer_to_list(Port);
%% 端口port和 ip地址合成一个ip:port的string
to_string({Addr, Port}) ->
{IPAddr, Port} = fixaddr({Addr, Port}),
inet:ntoa(IPAddr) ++ ":" ++ integer_to_list(Port).
%% 检查Port 是不是整数
fixaddr(Port) when is_integer(Port) ->
Port;
%% 检查Addr是不是一个list,Port是不是整数
fixaddr({Addr, Port}) when is_list(Addr), is_integer(Port) ->
%% 解析地址,然后返回一个ip
{ok, IPAddr} = inet:parse_address(Addr),
%% 返回元组{ip,port}
{IPAddr, Port};
fixaddr({Addr, Port}) when is_tuple(Addr), is_integer(Port) ->
case esockd_cidr:is_ipv6(Addr) or esockd_cidr:is_ipv4(Addr) of
true -> {Addr, Port};
false -> error(invalid_ipaddr)
end.
当在shell里执行ok=esockd:start().的时候就会调用该模块的方法:
%% @doc Start esockd application.
-spec(start() -> ok).
start() ->
{ok, _} = application:ensure_all_started(esockd), ok.
然后接着执行esockd_app.erl 模块里方法:
start(_StartType, _StartArgs) ->
io:format(" esockd_app start ~n"),
esockd_sup:start_link().
下一篇介绍 esockd_sup模块,该模块作为应用的root 监听者。