1

我有侦听 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 中的尝试连接?

PS我是erlang的新手。

4

1 回答 1

3

首先,recv()当您指定{packet, 0}. 阅读有关 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 选项设置操作系统套接字配置参数。来自man listen

backlog 参数定义待处理连接队列的最大长度。如果连接请求到达时队列已满,客户端可能会收到带有 ECONNREFUSED 指示的错误。或者,如果底层协议支持重传,则可以忽略请求,以便重试成功。

而且,Perl Monks 上的这个线程很好读:TCP 服务器:忙时如何拒绝连接?关于backlog配置 的一些片段:

所以,看起来连接请求只是被忽略了(因为 TCP 确实支持重传)

...当队列已满时,系统会简单地停止响应 SYN 数据包,这会触发它们的重新发送。结果,对等方没有任何迹象表明您的服务器正忙。它只是一直等待建立连接。

于 2019-02-05T06:11:26.653 回答