我有监听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。我是二郎的新人。
答案 0 :(得分:3)
首先,当您指定recv()
时,您不会像{packet, 0}
那样。阅读此answer about gen_tcp。
服务器可以:
Pid = spawn(?MODULE, loop, [Sock])
监视#1中的进程:
Ref = monitor(process, Pid)
但是为了防止出现竞争状况,您应该一步一步执行#1和#2:
{Pid, Ref} = spawn_monitor(?MODULE, loop [Sock])
执行gen_tcp:accept(ListenSock)
后,请执行以下操作:
gen_tcp:close(ListenSock)
在客户端终止时检测,因此是时候开始侦听新客户端了:
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 数据包,从而触发其重新发送。结果,对等体没有 表示您的服务器正忙。它只是一直在等待 建立连接。