3.Socket

来源:互联网 发布:阿沁的淘宝店叫什么 编辑:程序博客网 时间:2024/05/17 05:11

  说到socket就会想到tcp、udp、http协议,先来分析一下erlang项目中如何使用tcp socket。

  RabbitMQ项目中使用了prim_inet:async_accept/2、prim_inet:async_recv/3等方法,这些是相对比较底层的api,在erlang官方文档中并没有介绍,不过我们可以通过源码看到,最终会进到erlang:port_control/3函数,与端口进行交互。然后我们进到gen_tcp模块,可以看到无论是调用了inet_tcp还是inet6_tcp,最终还是会回到prim_inet模块。因此gen_tcp是对prim_inet进行包装,是更高层的socket调用模块,用gen_tcp写socket层,代码也会更清理明了。
  使用gen_tcp模块收到内核协议栈过来完整的封包的时候,可以通过init:setopts/2设置接收消息的方式。官方文档:http://www.erlang.org/doc/man/inet.html
  {active, true | false | once | N}:true时,所有收到的信息都会被传递到接收进程;false时,只能接收通过gen_tcp:recv/2,3, gen_udp:recv/2,3 or gen_sctp:recv/1,2等函数接收到信息;once时,每次收到信息后需要手动重置,以待下次接收。这里讨论一下N,N可以理解为once的扩展,once是每收到一条信息都需要重置,而N则是收到N条信息后重置一次。参数N是OTP 17.0以后的版本才能使用。下面有个简单的例子:
-module(sockettcp).  -export([start/0]).  start() ->    {ok, LSock} = gen_tcp:listen(1234, [binary, {packet, 0},{active, false}]),    io:format("listen(~p) on ~p~n",[LSock, 1234]),    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("(~p)recv:~p~n",[Socket, Data]);      {tcp_passive,Socket} ->          inet:setopts(ASock, [{active, 3}]),         io:format("tcp_passive:~p~n",[Socket]);    Err ->       io:format("error:~p~n",[Err])  end,  do_loop(ASock).
  再开一个shell作为客户端输入:
{ok,S} = gen_tcp:connect({127,0,0,1},1234,[{packet,0}]).gen_tcp:send(S, integer_to_binary(1)).gen_tcp:send(S, integer_to_binary(1)).gen_tcp:send(S, integer_to_binary(1)).gen_tcp:send(S, integer_to_binary(1)).
  可以看到每间隔3个包,需要重置一次参数。
0 0