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

How handle only one client simultaneously in erlang 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.

问题:

P.S。我是 erlang 的新手。

首先,当您指定 {packet, 0} 时,您不能 recv() 那样。阅读此

服务器可以:

  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:

The backlog parameter defines the maximum length for the queue of pending connections. If a connection request arrives with the queue full, the client may receive an error with an indication of ECONNREFUSED. Alternatively, if the underlying protocol supports retransmission, the request may be ignored so that retries may succeed.

而且,Perl Monks 上的这个线程是一个很好的读物:TCP server: How to reject connections when busy?一些关于 backlog 配置的片段:

So, it looks like connection request are just ignored (as TCP does support retransmission)

...when the queue is full, then the system simply stops answering SYN packets, which triggers their resending. As result, the peer gets no indication that your server is busy. It simply keeps waiting for connection to be established.