1、主动消息获取(非阻塞)
第一个例子是以主动模式打开socket,然后接受来自socket的数据:
{ok,Listen} = gen_tcp:listen(Port,[…,{active,true}…]),
{ok,Socket} = gen_tcp:accept(Listen), loop(Socket).
loop(Socket) ->
receive
{tcp,Socket,Data} -> … 输出处理 …
{tcp_closed,Socket} -> …
end.
这个过程无法控制发到服务器循环的消息流,如果客户端产生数据的速度大于服务器消费数据的速度,系统就会收到洪水般地消息-消息缓冲区溢出,系统将会crash并表现怪异。
这种类型的服务器叫做非阻塞服务器,因为它无法阻塞客户端。我们仅在信任客户端的情况下才会使用非阻塞服务器。
2 被动消息获取(阻塞)
在这一节,我们写阻塞服务器:服务器以被动模式打开socket,通过 {active,false} 选项。这个服务器不会被危险的客户端洪水袭击。
服务器循环中的代码调用 gen_tcp:recv 来接收数据。客户端在服务器调用 recv 之前会被阻塞。注意OS会对客户端发来的数据做一下缓冲,以允许客户端在服务器调用 recv 之前仍然可以继续发送一小段数据。
{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} -> … 数据处理 … loop(Socket);
{error,closed} …
end.
3 混合消息获取(部分阻塞)
你可能认为把被动模式用到所有服务器上都合适。不幸的是,当我们在被动模式时,我们只能等待来自于一个socket的数据。这对于需要等待多个socket来源数据的服务器则不适用。
幸运的是我们可以用混合方式,既不是阻塞的也不是非阻塞的。我们以一次主动(active once)模式 {active,once} 打开socket。在这个模式中,socket是主动的,但是只能接收一条消息。在控制进程发出一条消息之后,他必须明确的调用 inet:setopts 以便让socket恢复并接收下一条消息。系统在这发生之前会一直阻塞。这是两种世界的最好结合点。如下是代码:
{ok,Listen} = gen_tcp:listen(Port,[…,{active,once}…]),
{ok,Socket} = gen_tcp:accept(Listen), loop(Socket).
loop(Socket) ->
receive
{tcp,Socket,Data} -> … 数据处理 … %%准备好启用下一条消息时
inet:setopts(Socket,[{active,once}]),
loop(Socket);
{tcp_closed,Socket} -> …
end.
使用 {active,once} 选项,用户可以实现高层次的数据流控制(有时叫交通管制),同时又防止了服务器被过多的消息洪水所淹没。