重点:Socket有归属权,只有创建的进程才有资格处理,除非使用gen_tcp:controlling_process(Socket,Pid).,把该Socket的控制权交给Pid进程。
游戏中主要使用阻塞模式{active, fasle}
1、主动型消息接收(非阻塞):{active,true}
如果客户端发送的数据过快服务器可以处理的速度,那么系统就会被消息淹没,不会阻塞客户端,也叫异步服务器。
start()- >
{ok,Listen}=gen_tcp:listen(Port,[...,{active,true}...]),
{ok,Socket}=gen_tcp:accept(Listen),
loop(Socket).
loop(Socket)->
receive
{tcp,Socket,Data}->
... do something with the data ...
{tcp_closed,Socket}->
...
end.
也叫异步服务器,不会阻塞客户端
2、被动型消息接收(阻塞):{active,false}
不会因为一个过于活跃的客户机通过发送大量数据的攻击而崩溃。调用gen_tcp:recv时会阻塞客户端。需要注意的是,操作系统还会做一些缓存允许客户端继续发送少量的数据,然后才会将其阻塞,此时Erlang还没有调用到recv函数(?,我猜是这些少量数据压在邮箱中)。
start()- >
{ok,Listen}=gen_tcp:listen(port,[...,{active,false}...]),
{ok,Socket}=gen_tcp:accept(listen),
loop(Socket).
loop(Socket)->
case gen_tcp:recv(Socket,N) of
{ok,B}->
...do something with the data...
loop(Socket);
{error,closed}
...
end.
3、混合型模式(半阻塞),{active,once}
既不是阻塞也不是非阻塞。
{active,once}选项打开套接字。这种模式,套接字是主动但是仅仅针对一个消息,在控制进程发过一个消息后,必须显示地调用函数inet:setopts来把它重新激活以便接受下一个消息。在此之前,系统会处于阻塞状态。
start()- >
{ok,Listen}=gen_tcp:listen(Port,[...,{active,once}...]),
{ok,Socket}=gen_tcp:accept(Listen),
loop(Socket).
loop(Socket)->
receive
{tcp,Socket,Data}->
... do something with the data...
%%when you're ready enable the next message
inet:setopts(Sock,[{active,once}]), %%使用inet:setopts来激活,但是消耗资源
loop(Socket);
{tcp_closed,Socket}->
...
end.
4、有关归属权问题
第一种模式中,我改改,黑体是改的部分。这是错误的,Socket的归属权在start()的进程中,接受不了该Socket发送的消息。 第三种模式也是如此。(第二种模式比较特殊,继续看下面)
start()- >
{ok,Listen}=gen_tcp:listen(Port,[...,{active,true}...]),
{ok,Socket}=gen_tcp:accept(Listen),
spawn(fun() -> loop(Socket) end).%%创建接受数据进程
loop(Socket)->
receive
{tcp,Socket,Data}->
... do something with the data ...
{tcp_closed,Socket}->
...
end.
start()- >
{ok,Listen}=gen_tcp:listen(Port,[...,{active,true}...]),
{ok,Socket}=gen_tcp:accept(Listen),
register(Pid,spawn(fun() -> loop(Socket) end)). %%创建接受数据进程,并注册成Pid
gen_tcp:controlling_process(Socket,Pid).
loop(Socket)->
receive
{tcp,Socket,Data}->
... do something with the data ...
{tcp_closed,Socket}->
...
end.
特别的地方,有关第二种模式的Socket归属权的话,这个还是能获取到消息的·,是没问题的。
start()- >
{ok,Listen}=gen_tcp:listen(port,[...,{active,false}...]),
{ok,Socket}=gen_tcp:accept(listen),
spawn(fun() -> loop(Socket) end)
loop(Socket)->
case gen_tcp:recv(Socket,N) of
{ok,B}->
...do something with the data...
loop(Socket);
{error,closed}
...
end.
@@本文参考Erlang程序设计@@