I have TCP server that listen Ip:Port.
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.
Task: when one client connected on Ip:Port (for example telnet Ip Port
), another client trying connection must be dropped. In other words, exclusive usage of Ip:Port.
Questions:
P.S. I am new in erlang.
First, you can't recv()
like that when you specify {packet, 0}
. Read this answer about gen_tcp.
The server could:
Pid = spawn(?MODULE, loop, [Sock])
Monitor the process in #1:
Ref = monitor(process, Pid)
But to prevent a race condition, you should perform #1 and #2 in one step:
{Pid, Ref} = spawn_monitor(?MODULE, loop [Sock])
After gen_tcp:accept(ListenSock)
executes, do:
gen_tcp:close(ListenSock)
Detect when the client terminates and therefore it's time to start listening for a new client:
receive {'DOWN', Ref, process, Pid, _Reason} ->
listen(Ip, Port)
Or, if the client will not terminate after it is done sending data, then you can detect when the client closes the socket in 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 socket option (e.g. {backlog, 0}
):
The backlog option sets an OS socket configuration parameter. From 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.
And, a good read is this thread at Perl Monks: TCP server: How to reject connections when busy? Some snippets about the backlog configuration:
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.