erlang R17新socket选项{active,N}

erlang R17带来了新的socket选项{active,N} ,与{active,once}一起为应用层提供流量控制。为什么要多了这个选项,{active,once}不是可以有效抑制大量socket消息吗?

我们知道,{active,once}在每次接收到包都要重新设置active选项,才能继续接收erlang的消息通知。实际上,每次设定{active,once}都意味着调用一次epoll_ctl, 如果请求过于频繁,就会有大量的epoll_ctl调用。erlang目前只有一个线程会收割epoll_wait事件,epoll_wait要轮询已经就绪的ctl队列,如果大量的ctl事件将会阻塞了epoll_wait的操作,造成网络处理能力的下降。

那么,我们能不能设定接收N个的socket消息后再执行一次epoll_ctl,这样可以有效减少epoll_ctl的调用,{active,N}就是这样出现的。

下面来看一下{active,N}的说明

Add the {active,N} socket option for TCP, UDP, and SCTP,where N is an integer in the range -32768..32767, to allow a caller to specify the number of data messages to be delivered to the controlling process. Once the
socket‘s delivered message count either reaches 0 or is explicitly set to 0 with inet:setopts/2 or by including {active,0} as an option when the socket is created, the socket transitions to passive({active, false}) mode and the socket‘s controlling process receives
a message to inform it of the transition. TCP sockets receive {tcp_passive,Socket}, UDP sockets receive {udp_passive,Socket} and SCTP sockets receive {sctp_passive,Socket}.

The socket‘s delivered message counter defaults to 0, but it can be set using {active,N} via any gen_tcp, gen_udp, or gen_sctp function that takes socket options as arguments, or via inet:setopts/2. New N values are added to the socket‘s current counter value,
and negative numbers can be used to reduce the counter value. Specifying a number that would cause the socket‘s counter value to go above 32767 causes an einval error. If a negative number is specified such that the counter value would become negative, the
socket‘s counter value is set to 0 and the socket transitions to passive mode. If the counter value is already 0 and inet:setopts(Socket, [{active,0}]) is specified, the counter value remains at 0 but the appropriate passive mode transition message is generated
for the socket.

设定了{active,N}选项后,进程在接收了N个包后,会收到{tcp_passive, Socket}消息,意味着这个Socket进入被动模式,需要重新设置active选项。

接下来,在实际的例子中测试这个参数:

-module(server).

-export([start/0]).
-export([continue/1]).
-define( PORT, 8888).

start() ->
  {ok, LSock} = gen_tcp:listen(?PORT, [binary, {packet, 0},{active, false}]),
  io:format("socket listen: ~p on ~p ~n",[LSock, ?PORT]),
  accept(LSock).

accept(LSock) ->
  {ok, ASock} = gen_tcp:accept(LSock),
  Pid = spawn(fun() -> do_loop(ASock) end),
  gen_tcp:controlling_process(ASock, Pid),
  inet:setopts(ASock, [{active, 3}]),
  accept(LSock).

do_loop(ASock) ->
  receive
    {tcp, Socket, Data} ->
       io:format("socket ~p recv: ~p ~n",[Socket, Data]);
    {tcp_closed, Socket} ->
       io:format("socket ~p close ~n",[Socket]);
    {tcp_passive,Socket} ->
       io:format("socket ~p is passive, please call continue/1 ~p ~n",[Socket, self()]);
    release_passive ->
       inet:setopts(ASock, [{active, 3}]);
    Err ->
       io:format("socket may error: ~p ~n",[Err])
  end,
  do_loop(ASock).

continue(Pid) ->
  Pid ! release_passive,
  ok.

编译启动这个模块后,我们创建一个客户端来请求这个服务端:

1> f(S), {ok,S} = gen_tcp:connect({127,0,0,1},8888,[{packet,0}]).
{ok,#Port<0.526>}
2> gen_tcp:send(S,<<"hello">>).
ok
3> gen_tcp:send(S,<<"hello">>).
ok
4> gen_tcp:send(S,<<"hello">>).
ok
5> gen_tcp:send(S,<<"hello">>).
ok

服务端控制台打印了这样的信息:

D:\tmp>erl -s server
socket listen: #Port<0.422> on 8888
Eshell V6.0  (abort with ^G)
1> socket #Port<0.479> recv: <<"hello">>
1> socket #Port<0.479> recv: <<"hello">>
1> socket #Port<0.479> recv: <<"hello">>
1> socket #Port<0.479> is passive, please call continue/1 <0.33.0>
1> server:continue(pid(0,33,0)).
socket #Port<0.479> recv: <<"hello">>
ok
2>

在上面的例子中,我们设定了{active,3}的选项,在接收到客户端3次数据后,socket进入了passive状态,在重新设置{active,N}后继续接收tcp消息。

那么,如何在实际项目中运用{active,N}选项?

inet:setopts(Socket, [{active, 300}]),
erlang:send_after(30 * 1000, self(), release_passive);

大概思路是,在30秒内最多接收300个包,超过就不接收,等待这30秒完成后继续接收,如此反复。

利用这点还可以加多一个计数器,如果超过10次进入passive状态,说明这个Socket存在问题,有攻击的行为。

参考:

http://blog.csdn.net/mycwq/article/details/24814843

http://www.erlang.org/download/otp_src_17.0.readme

http://blog.yufeng.info/archives/2970

erlang R17新socket选项{active,N}

时间: 2024-10-16 02:47:45

erlang R17新socket选项{active,N}的相关文章

解决erlang R17无法识别中文问题

erlang更新到R17已有一段时间了,公司项目打算从旧版的erlang迁移到R17,却不料有不少的困扰,其中一个问题是中文问题. 这个问题很容易重现:新建一个文件t.erl,保存为utf-8无bom格式 -module(t). -export([test/0]). test() -> ["我", <<"我">>]. 在旧版的erlang中,下面的代码能正常工作,结果就是: Eshell V5.9.1 (abort with ^G) 1

TIME_WAIT状态下,修改socket选项后,bind端口会失败

TIME_WAIT状态下,修改socket选项后,bind端口会失败,即使使用的是SO_REUSEADDR或者SO_REUSEPORT模式 ********************************* tcp        0      0 127.0.0.1:81                127.0.0.1:56850             TIME_WAIT ********************************* tcp        0      0 127.0.

Socket编程基础——Socket选项

有些情况下,我们需要对Socket行为和属性进一步控制,例如修改缓冲区大小,查看Socket状态,这就需要设置/获取Socket选项. 1.获取Socket选项int getsockopt(SOCKET s,int level,int optname,void *optval,int *optlen)s:Socket描述符level:选项级别,包括SOL_SOCKET和IPPROTO_TCPoptname:Socket选项的名字optval:用于接收Socket数值的缓冲区optlen:缓冲区大

《用Java写一个通用的服务器程序》03 处理新socket

在讲监听器时说过处理的新的socket要尽快返回,监听器调用的是ClientFactory的createPhysicalConnection方法,那么就来看这个方法: public boolean createPhysicalConnection(PushClientSocket socket, boolean isObserver, ListenerOptions listenerOptions) { PhysicalConnectionPool thePhysicalConnectionPo

Linux网络编程socket选项之SO_LINGER,SO_REUSEADDR

from http://blog.csdn.net/feiyinzilgd/article/details/5894300 Linux网络编程中,socket的选项很多.其中几个比较重要的选项有:SO_LINGER(仅仅适用于TCP,SCTP), SO_REUSEADDR. SO_LINGER 在默认情况下,当调用close关闭socke的使用,close会立即返回,但是,如果send buffer中还有数据,系统会试着先把send buffer中的数据发送出去,然后close才返回. SO_L

socket选项总结(setsocketopt)

功能描述:        获取或者设置与某个套接字关联的选 项.选项可能存在于多层协议中,它们总会出现在最上面的套接字层.当操作套接字选项时,选项位于的层和选项的名称必须给出.为了操作套接字层的选项,应该 将层的值指定为SOL_SOCKET.为了操作其它层的选项,控制选项的合适协议号必须给出.例如,为了表示一个选项由TCP协议解析,层应该设定为协议 号TCP. 用法:#include #include int getsockopt(int sock, int level, int optname

socket选项自带的TCP异常断开检测

TCP异常断开是指在突然断电,直接拔网线等等情况下,如果通信双方没有进行数据发送通信等处理的时候,无法获知连接已经断开的情况. 在通常的情况下,为了使得socket通信不受操作系统的限制,需要自己在应用层实现心跳包机制,来检查异常断开的情况,一般的方式就是服务器在一段时间没有收到客户端数据包时,定时发包,然后客户端回应,如果已经出现异常断开则服务器接收会返回错误,而客户端在指定时间内没有收到数据包,则主动向服务器发包,得到错误就说明断开.诸如此类的方式就是自己实现的心跳包机制. 但操作系统本身也

socket选项设置

#include <sys/socket.h>int setsockopt( int socket, int level, int option_name, const void *option_value, size_t option_len); 第一个参数socket是套接字描述符.第二个参数level是被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET. option_name指定准备设置的选项,option_name可以有哪些取值,这取决

socket选项

当调用closesocket关闭套接字时,SO_LINGER将决定系统如何处理残存在套接字发送队列中的数据.处理方式无非两种:丢弃或者将数据继续发送至对端,优雅关闭连接 TCP_NODELAY: 表示立即发送数据. SO_RESUSEADDR: 表示是否允许重用Socket 所绑定的本地地址. SO_TIMEOUT: 表示接收数据时的等待超时数据. SO_LINGER: 表示当执行Socket 的 close()方法时, 是否立即关闭底层的Socket. SO_SNFBUF: 表示发送数据的缓冲