[erlang-questions] How to write a TCP server in Erlang?

Per Hedeland per@REDACTED
Fri Jun 15 08:43:40 CEST 2007


"Ulf Wiger" <ulf@REDACTED> wrote:
>
>I venture a guess that you send the second message from the
>client and then call gen_tcp:close(Sock). Is that right?
>
>Since you have no flow control between the client and server,
>the client will fire off its messages and then close the socket
>while the server is busy writing the results of the first message
>to the tty.
>
>If you e.g. insert a timer:sleep(500) between the sends on
>the client side, things will likely work. You could of course also
>have the client wait for a reply from the server. (:

Firing off a bunch of messages without intervening sleep and then
closing the socket should work fine per se - this is TCP semantics, and
gen_tcp/inet_drv should preserve it. Doesn't matter if the receiver is
"busy", the messages won't get lost unless the *receiver* closes his
socket before picking them up, which might be what is happening here:

In the posted code, the client connects, sends one message, and closes
the socket. The server accepts, spawns a worker, which does one recv/2
and closes the socket. Obviously nothing more can be sent on that
connection since it is firmly closed, but repeated invocations of
client:start/0 (i.e. sending one message per connection) works fine -
i.e. it's unclear what "try to send another packet" means exactly.

There is a bug in the posted code though, and that is the usage of
{packet, 0} in combination with a single recv/2 - there is no guarantee
that the latter will get the complete binary. Either {packet, 1/2/4}
should be used - the sensible thing to do if multiple messages should be
sent on the same connection - or the receiver must recv() until EOF on
the socket - see the example in the man page.

>How to do these two transmissions correctly? For example, how to write a
>simple TCP server that for each connected client acts as an echo server
>(i.e. it sends back what it received)?

Then your worker process must of course not close the socket after
receiving a single message, but wait for the client close (or some
special "quit" message). Something like this:

worker(Sock) ->
    case gen_tcp:recv(Sock, 0) of
	{ok, Bin} ->
	    gen_tcp:send(Sock, Bin),
	    worker(Sock);
	{error, closed} ->
	    ok
    end.

In this particular case {packet, 0} is OK, since the receiver doesn't
care if he gets an incomplete message (or multiple ones) in the recv/2 -
but if he were to do a binary_to_term/1 on the received binary, some
"packet boundary" method must be used (this can of course be implemented
by the application if none of the "canned" modes of gen_tcp is suitable
- but it's significantly less convenient).

--Per Hedeland



More information about the erlang-questions mailing list