erlang

Broadcast message to all joined clients when a new client joins the same room in Erlang


I have been developing a chatroom application using message passing. I want to broadcast a message to all joined clients when a new client joins the same room. The codes I'm using as follows:

client.erl

{join, Roomname} ->
            
           {?SERVER, ?SERVER_NODE} ! {self(), {join, Roomname}},
        
           receive
                            
            {joined, Roomname, RoomPid, Messages} -> 
                    ...... Do something     
                     loop(UpdatedState);
           {failed, Why} ->
                  io:format("~p ~n", [Why]),
                  loop(S)
           after ?TIMEOUT ->
                io:format("No response from server or chatroom ~n", [])
          end                   
            

chatroom.erl

{join, ClientPid, ClientName} ->
            
                 
            UpdatedClients = orddict:store(ClientPid, 
            ClientName,S#state.clients),
            Roomname = S#state.name,            
            Messages = S#state.messages,            
            ClientPid ! {joined, Roomname, self(), Messages},
            loop(S#state{clients=UpdatedClients});

server.erl

{Client, {join, Roomname}} -> 
            
            
            Rooms = S#state.rooms,
            RoomExist = orddict:is_key(Roomname, Rooms),
            
            if RoomExist ->                 
                RoomPid = orddict:fetch(Roomname, Rooms),
                Clientname = orddict:fetch(Client, S#state.clients),

                RoomPid ! {join, Client, Clientname},
                io:format("~p joined room ~p ~n", [Clientname, Roomname]);
            not RoomExist -> 
                Client ! {failed, room_does_not_exist}
            end,
            loop(S);

The codes above work fine. However, I tried to broadcast a message to all joined clients when a new client join the same room by altering the client.erl and chatrroom.erl to:

client.erl

{join, Roomname} ->
            
           {?SERVER, ?SERVER_NODE} ! {self(), {join, Roomname}},
        
            receive
                            
            {joined, Roomname, RoomPid, Messages} -> 
                    ...... Do something     
                     loop(UpdatedState);
            {broadcast,Who,Roomname} ->
               io:format("~p Joined room ~p ~n", [Who,Roomname]);

           {failed, Why} ->
                  io:format("~p ~n", [Why]),
                  loop(S)
           after ?TIMEOUT ->
                io:format("No response from server or chatroom ~n", [])
          end   

chatroom.erl

{join, ClientPid, ClientName} ->
            
            Clients = S#state.clients,
            ClientPids = orddict:fetch_keys(Clients),
            UpdatedClients = orddict:store(ClientPid, ClientName,S#state.clients),
            Roomname = S#state.name,            
            Messages = S#state.messages,            
            ClientPid ! {joined, Roomname, self(), Messages},
           [Pid ! {broadcast,ClientName,Roomname} || Pid <- ClientPids],
           loop(S#state{clients=UpdatedClients});

the new client join still working fine, but I'm getting "Unknown message" at the joined clients processes like:

Unknown message: {broadcast,the new client name,the room name}

It means, the first message ClientPid ! {joined, Roomname, self(), Messages}is recognized, while, the second one [Pid ! {broadcast,ClientName,Roomname} || Pid <- ClientPids] not!

What the wrong I am doing here?


Solution

  • You should check if that error message occurs on the 2nd Pid. If so, that's probably because you missed the loop(XXX) on the receive clause.

    In order to trace what happens inside the receive block, you can follow this approach, by restructuring the code and print the message / i.e. 'Other' value.

    ...
    receive
        Msg -> 
            handle_message(Msg)
       after ?TIMEOUT ->
            io:format("No response from server or chatroom ~n", [])
    ...
    ...
    
    handle_message({joined, Roomname, RoomPid, Messages}) -> 
        ...... Do something     
        loop(UpdatedState);
    handle_message({broadcast, Who, Roomname}) -> 
        io:format("~p Joined room ~p~n", [Who, Roomname]),
        loop(S);
    handle_message({failed, Why}) -> 
        io:format("~p~n", [Why]),
        loop(S);
    handle_message(Other) -> 
        io:format("Other case apparently: ~p~n", [Other]),
        loop(S).