There are a few wonky things in there. The easiest way to talk through them is probably to just show a different version:
-module(biu_server).
-export([start_server/0,start/0]).
start() ->
spawn(fun() -> start_server() end).
start_server() ->
Options = [list, {reuseaddr, true}],
{ok, Listen} = gen_tcp:listen(1337, Options),
par_connect(Listen).
par_connect(Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
% The line below is a whole different universe of confusion for now.
% Get sequential stuff figured out first.
% spawn(fun() -> par_connect(Listen) end),
loop(Socket).
loop(Socket) ->
ok = inet:setopts(Socket, [{active, once}]),
receive
{tcp, Socket, Request} ->
ok = io:format("Received TCP message: ~tp~n", [Request]),
Response = io_lib:format("You said: ~tp\r\n", [Request]),
gen_tcp:send(Socket, Response),
loop(Socket);
{tcp_closed, Socket} ->
io:format("Server socket closed~n");
quit ->
ok = io:format("Fine! I QUIT!~n");
Unexpected ->
ok = io:format("Unexpected message: ~tp~n", [Unexpected]),
loop(Socket)
end.
Note that above I commented out the call to spawn a new process to handle the connection. One of the problems the original version had was that the remaining listener had no way to be terminated because it was always blocking on TCP accept -- forever.
There are also some socket control issues there -- the easiest way around that is to have each listener spawn the next listener and move on to handle its newly acquired connection. Another way is to do what you've done, but there are some details that need to be managed to make it work smoothly. (Example (don't worry, still easy): https://github.com/zxq9/erlmud/blob/master/erlmud-0.1/tcplistener.erl).
I also added two new clauses to the receive in your main loop: one that lets us tell the process to kill itself from within Erlang, and another that handles unexpected messages from within the system. The "Unexpected" clause is important for two reasons: it tells us what is going on (you shouldn't normally be receiving mysterious messages, but it can happen and its not always your fault), and it prevents the process mailbox from stacking up with unmatched-but-unmanageable messages.
Just sticking with the version above, here is what happens during my first session...
In the Erlang shell:
1> c(biu_server).
{ok,biu_server}
2> {Pid, Ref} = spawn_monitor(biu_server, start_server, []).
{<0.40.0>,#Ref<0.0.2.89>}
Received TCP message: "foo\r\n"
Received TCP message: "bar\r\n"
Received TCP message: "Yay! It works!\r\n"
Server socket closed
3> flush().
Shell got {'DOWN',#Ref<0.0.2.89>,process,<0.40.0>,normal}
ok
4> f().
ok
And in a telnet session:
ceverett@changa:~/Code/erlang$ telnet localhost 1337
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
foo
You said: "foo\r\n"
bar
You said: "bar\r\n"
Yay! It works!
You said: "Yay! It works!\r\n"
^]
telnet> quit
Connection closed.
As we can see, the closed connection from the client side terminated as expected. The shell's process was monitoring the TCP server process, so when I flush()
at the end we see the 'DOWN'
monitor message as expected -- with a normal exit.
Now let's do a similar session, but we'll use the Erlang-side quit
message:
5> {Pid, Ref} = spawn_monitor(biu_server, start_server, []).
{<0.43.0>,#Ref<0.0.2.103>}
Received TCP message: "Still works.\r\n"
6> Pid ! "Launch the missiles!".
Unexpected message: "Launch the missiles!"
"Launch the missiles!"
7> Pid ! quit.
Fine! I QUIT!
quit
8> flush().
Shell got {'DOWN',#Ref<0.0.2.103>,process,<0.43.0>,normal}
ok
And on the telnet side:
ceverett@changa:~/Code/erlang$ telnet localhost 1337
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Still works.
You said: "Still works.\r\n"
Connection closed by foreign host.
You notice the "\r\n" in there. That is coming from the client's original message -- all telnet messages are terminated with "\r\n" (so this is what you split the stream on usually -- which is a topic we aren't addressing yet; this code actually is pretending that TCP works like UDP datagrams, but that's not true...). The "\r\n" that are in the server's return message are being interpreted correctly by the telnet client and breaking to the next line.
Writing telnet (or datagram) games is a very pleasant way to explore the Erlang networking modules, by the way.