2

I can't get multiple clients to communicate with my concurrent echo server. Here are my results:

Server Terminal window:

1> c(s).
{ok,s}

2> s:init(15555).
Server started on port: 15555

Client Terminal window:

1> c(cl).
{ok,cl}

2> cl:test().
[<0.64.0>,<0.65.0>,<0.66.0>,<0.67.0>,<0.68.0>]

3> 
=ERROR REPORT==== 21-May-2017::21:21:39 ===
Error in process <0.68.0> with exit value:
{{badmatch,{error,econnrefused}},[{cl,client,2,[{file,"cl.erl"},{line,5}]}]}

=ERROR REPORT==== 21-May-2017::21:21:39 ===
Error in process <0.67.0> with exit value:
{{badmatch,{error,econnrefused}},[{cl,client,2,[{file,"cl.erl"},{line,5}]}]}
...
...

In the shell, if I start my server, then connect one client, my client and server can communicate back and forth without error:

Server Terminal window:

1> c(s).
{ok,s}

2> s:init(15555).
Server started on port: 15555

Client Terminal window:

1> c(cl).  
{ok,cl}

2> cl:client(1, 15555).
Client1: sent -->hello
Client1: received <--hello
ok

In my failing concurrent test, I do this:

test() ->
    Port = 1555,
    [spawn(?MODULE, client, [Id, Port]) || Id <- lists:seq(1, 5)].

Here's my echo server:

-module(s).
-compile(export_all).

init(Port) ->
    {ok, ServerSocket} = gen_tcp:listen(Port, [binary, {active,true},
                                               {packet,4}, {reuseaddr,true}] ),
    io:format("Server started on port: ~w~n", [Port]),
    server(ServerSocket).

server(ServerSocket) ->
    {ok, ClientSocket} = gen_tcp:accept(ServerSocket),
    spawn(?MODULE, server, [ServerSocket]),

    timer:sleep(rand:uniform(3) * 1000),
    loop(ClientSocket).


loop(ClientSocket) ->
    receive
        {tcp, ClientSocket, CompleteMsg} ->
            io:format("Server: received ~s~n", [CompleteMsg]),
            gen_tcp:send(ClientSocket, CompleteMsg);
        {tcp_closed, ClientSocket} ->
            io:format("Server: client closed socket.~n")
    end,
    loop(ClientSocket).

Here's my client:

-module(cl).
-compile(export_all).

client(Id, Port) ->
    {ok, Socket} = gen_tcp:connect(localhost, Port, [binary, {active,true}, 
                                                      {packet,4},
                                                      {reuseaddr, true}] ),
    Msg = "hello",
    gen_tcp:send(Socket, Msg),
    io:format("Client~w: sent -->~s~n", [Id, Msg]),

    receive 
        {tcp, Socket, CompleteMsg} ->
            io:format("Client~w: received <--~s~n", [Id, CompleteMsg]);
        {tcp_closed, Socket} ->
            io:format("Server closed the socket.~n")
    end,
    gen_tcp:close(Socket).

test() ->
    Port = 1555,
    [spawn(?MODULE, client, [Id, Port]) || Id <- lists:seq(1, 5)].

It's my understanding that a TCP socket is nothing but a tuple containing four numbers:

{senderPort, senderAddress, destinationPort, destinationAddress}

I think that each of my clients must have the same senderPort and senderAddress, therefore they are all trying to use the same TCP socket. But I would think that one of my clients would be able to successfully establish a TCP connection while the rest got a connection refused error, yet all of my concurrent clients get a connection refused error.

It seems to me that gen_tcp:connect() somehow picks a senderPort and retrieves the senderAddress, which along with the ServerAddress and ServerPort arguments, defines the socket. How can I make each of my clients use a different senderPort so that each client creates a unique socket?

Is there a way to programmatically test a concurrent server with multiple clients on localhost?

7stud
  • 46,922
  • 14
  • 101
  • 127
  • 4
    Typo in the port number in `cl:test/0`? – Dogbert May 22 '17 at 05:11
  • 1
    @Dogbert, Yep. !@#$!@#$. What a waste of time. I guess I need to choose a port that is harder to mistype. So `connect()` must pick an *open* port to use for the SenderPort term in the 4-tuple that comprises a socket. Can you post your comment as an answer? – 7stud May 22 '17 at 21:37
  • 1
    I think it'd be better to close as a "simple typographical error" as it was just a typo, nothing more. :) – Dogbert May 23 '17 at 06:53
  • 1
    @Dogbert, 1) There are all kinds of reasons why code doesn't work. You took the time to look through my code, which I greatly appreciate, and you deserve to be rewarded for your erlang debugging skills. 2) Will my question have any future value? I searched high and low for the topic in my title, and I couldn't find anything. If someone had previously asked my question, and you had answered it, I could have saved myself hours of digging by checking that my port numbers were consistent. In addition there just aren't that many gen_tcp examples online--maybe someone will benefit from it. – 7stud May 23 '17 at 07:44

0 Answers0