如何在erlang gen_tcp中仅同时处理一个客户端?

时间:2019-02-04 19:17:26

标签: tcp erlang tcplistener gen-tcp

我有监听Ip:Port的TCP服务器。

listen(Ip, Port) ->
  Opts = [
    binary,
    {active, false},
    {packet, 0},
    {reuseaddr, true},
    {ip, Ip}
  ],

  case gen_tcp:listen(Port, Opts) of
    {ok, ListenSock} ->
      ?MODULE:loop_accept(ListenSock);
    {error, Reason} ->
      exit(Reason)
  end.

loop_accept(ListenSock) ->
  {ok, Sock} = gen_tcp:accept(ListenSock),
  ?MODULE:loop(Sock),
  ?MODULE:loop_accept(ListenSock).

loop(Sock) ->
  case gen_tcp:recv(Sock, 0) of
    {ok, Data} ->
      gen_tcp:send(Sock, [<<"Response: ">>, Data]),
      ?MODULE:loop(Sock);

    {error, Reason} ->
      ok
  end.

任务:当一个客户端连接到Ip:Port(例如telnet Ip Port)时,必须丢弃另一个尝试连接的客户端。换句话说,是Ip:Port的专有用法。

问题:

  • 如何使用gen_tcp模块在Erlang上实现?
  • 可以通过gen_tcp:listen选项解决吗?
  • 如何通过编程方式放弃Erlang中的尝试连接?

P.S。我是二郎的新人。

1 个答案:

答案 0 :(得分:3)

首先,当您指定recv()时,您不会像{packet, 0}那样。阅读此answer about gen_tcp

服务器可以:

  1. Pid = spawn(?MODULE, loop, [Sock])

  2. 监视#1中的进程:

    Ref = monitor(process, Pid)
    

    但是为了防止出现竞争状况,您应该一步一步执行#1和#2:

    {Pid, Ref} = spawn_monitor(?MODULE, loop [Sock]) 
    
  3. 执行gen_tcp:accept(ListenSock)后,请执行以下操作:

    gen_tcp:close(ListenSock)
    
  4. 在客户端终止时检测,因此是时候开始侦听新客户端了:

    receive {'DOWN', Ref, process, Pid, _Reason} -> 
    listen(Ip, Port)
    

    或者,如果客户端在发送完数据后不会终止,那么您可以在loop()中检测客户端何时关闭套接字:

     case gen_tcp:recv(Sock, 0) of
         {ok, Data} ->
             gen_tcp:send(Sock, [<<"Response: ">>, Data]),
             ?MODULE:loop(Sock);
    
         {error, closed} ->
             listen(Ip, Port);
    
         {error, Reason} ->
              ok
      end
    

=====

积压套接字选项(例如{backlog, 0}):

backlog选项设置OS套接字配置参数。来自man listen

  

backlog参数定义队列的最大长度   待处理的连接。如果连接请求与队列一起到达   客户端 已满,则会收到一条错误消息,指出   ECONNREFUSED。或者,如果基础协议支持   重新传输,该请求可能会被忽略,以便重试   成功。

Perl Monks上的这个线程很好读:TCP server: How to reject connections when busy?有关 backlog 配置的一些摘要:

  

所以,看起来连接请求只是被忽略了(就像TCP那样   支持重新传输)

     

...当队列已满时,系统仅停止回答SYN   数据包,从而触发其重新发送。结果,对等体没有   表示您的服务器正忙。它只是一直在等待   建立连接。