Starting from:
$35

$29

Erlang TinyChat Assignment Solution

The goal of this assignment is to create a simple chat application in Erlang which we dub TChat. This document is structured as follows:

    • Section 1 presents the assignment policies.

    • Section 2 describes the actors involved in the TChat application.

    • Section 3 describes the message protocol associated to each operation that is to be supported by the actors of TChat.

    • Section 4 presents submission instructions.

    • Assignment Policies

Collaboration Policy. This homework may be done individually or in pairs. Use of the Internet is allowed, but should not include searching for existing solutions.

Under absolutely no circumstances can code be exchanged between students. Excerpts of code presented in class can be used.

Assignments from previous o erings of the course must not be reused. Violations will be penalized appropriately.

Late Policy. Late submissions are allowed with a penalty of 2 points per hour past the deadline.

    • A High-Level Overview of TChat

TChat consists of 4 primary actors or process types and their corresponding Erlang modules:


∗Based on http://www.cse.chalmers.se/edu/year/2018/course/TDA384_LP1/cchat/. Note, however, that this assignment is di erent both in its actors and messaging protocols.

1


    • GUIs (gui.erl): code provided for you.

    • Clients (client.erl): must complete stub provided.

    • A server (server.erl): must complete stub provided.

    • Chatrooms (chatroom.erl): must complete stub provided.

There is also a main.erl module to start the program, as well as some additional auxiliary les that will be described below (all of which are provided).

The good news is that you are given the entirety of gui.erl, and part of client.erl! The even better news is that you will still have plenty of opportunity to practice message passing throughout the rest of the modules!

But how does this all work?

In addition to the process types mentioned above, there is of course the human user \process". Its role is important because the functionality of this program will be described in terms of the user’s commands. The user can enter commands into the graphical user interface or GUI. Here is an image of two GUI processes, pictured side-by-side, corresponding to two di erent clients:













Each GUI process has a system tab named \System". It also has zero or more chatroom tabs, corresponding to each of the chatrooms that the human user is participating in. Chatroom names always start with a hashtag. The current active/open tab is indicated by writing its name in boldface. Each GUI process also has a command line entry pane, located at the bottom. In order to interact with the TChat system, users may enter any of the following commands in the command line entry pane:













2


/join #chatroom

name
join chatroom with name \#chatroom

name".
















/leave
leave chatroom who’s tab is currently active/open.










/leave #chatroom

name
leave chatroom with name \#chatroom

name".






/whoami
what is current nickname?






/nick new

nickname
change   nickname   from   current   nickname   to











\new

nickname". Must start with lowercase


some string of text
send a message \some string of text" to chatroom







who’s tab is currently active/open.


/quit
quit. Just that user’s GUI and client exit, while server and







chatrooms stay running.















NOTE: For some reason, the GUI will not always exit properly after entering /quit and running the proper exit protocol. This is a bug, and should not a ect the running of your program. Additionally, the close window button on the GUI is not attached to anything. This means that we are only expecting the user to exit the program by entering /quit through the GUI. Any other way of closing the program is considered unde ned behavior.


2.1    Processes and their Local State

Each process has a local state. For example, the local state of a client includes its nickname and the chatrooms that it is connected to. The local state of a process is modeled as a parameter, of some appropriate record type, of the main loop function of the process (typically called loop). This section describes the local state, and the corresponding record type that holds it, for each process in TChat that you will have to implement, namely client, server and chatroom. All record types introduced below are declared in the header le defs.hrl.

2.1.1    Client (client.erl)

The client has the following properties: a nickname that is unique across the running of this program, a number of connected chatrooms, and the name of the gui process with which it has an exclusive connection. These properties make up the client’s local state. It is should always be kept track of in a record of type cl st. Its declaration is given below:


-record(cl_st, {gui, nick, con_ch}).


cl st consists of the following  elds:


    • gui: a string representing the name under which the client’s corresponding GUI process is registered. To look up the GUI’s PID, use whereis(list to atom(State#cl st.gui)). The GUI name should be stored as a string.

    • nick: the client’s current nickname. Nickname should be stored as a string.




3


    • con ch: a map from chatroom names (string) to chatroom PIDs; this map models all chatrooms to which the client is registered.


This state should be passed through each call to whatever repeated function you use to run/loop your client.


2.1.2    Server (server.erl)

The server has the following properties: a map of client PIDs to their current nickname (system wide), a map of chatroom names (string) to a list of clients registered to the chatroom (list of PIDs), and a map of chatroom names (string) to the corresponding chatroom PIDs. These properties make up the server’s local state. It is should always be kept track of in a record of type serv st. Its declaration is given below:


-record(serv_st, {nicks, registrations, chatrooms}).


serv st consists of the following  elds:


    • nicks: a map from a client’s PID as the key to the nickname (string) under which the client is currently registered as the value. It should be ensured that no two clients have the same nickname, and a client is not allowed to change its nickname to a nickname currently in use by another client. Look at the maps api in the Erlang docs to complete this. Additionally, nicknames should be stored as strings.

    • registrations: a map from a chatroom’s name (string) as the key to a list of the client processes’ PIDs of clients registered in that chatroom. At a given time, the map may look a bit like this:

f "#soccer" => [<0.0.1>, <0.0.3>, ...], "#swim meet" => [<0.0.2>, <0.0.3>, <0.0.4>, ...], ... g


This map must be kept up to date as clients join and leave chatrooms.

    • chatrooms: a map from a chatroom’s name (string) as the key to the chatroom’s corresponding PID as the value.


The server is responsible for spawning any chatrooms that a client requests to join if they don’t exist, or telling a chatroom the PID of a client that wants to join it if that chatroom already exists. Whether the chatroom is newly spawned or already existent, the chatroom will be responsible, upon receipt of the new client info, of informing the client about itself.

For the purposes of this assignment, it can be expected that a chatroom will not crash (unless there are bugs in your code) and so there is no need for monitoring. It can also be assumed that once a chatroom exists, it is never destroyed for the running duration of the program.



4


2.1.3    Chatroom (chatroom.erl)

The chatroom has the following properties: a name (string), a map from a client’s PID to a client’s nickname (string) which represents clients registered to that chatroom, and a history of the chat since the beginning of that chatroom’s life. These properties make up the chatroom’s local state. It is should always be kept track of in a record of type chat st. Its declaration is given below:


-record(chat_st, {name, registrations, history}).


chat st consists of the following  elds:


    • name: the name (string) of the chatroom.

    • registrations: a map from a client’s PID to a client’s nickname (string). The map represents all clients registered in that chatroom.

    • history: chat history since the beginning of that chatroom. It should be represented as a list of tuples, where each tuple is fClient nickname, Messageg, both of which are strings.

The chatrooms will be responsible for propagating messages sent to it to all clients in the chatroom. The chatroom depends on the server to update it when a client joins or leaves a chatroom, or changes its nickname. Please note though that messages sent between the chatroom and server are sent directly from the client (not through the server), and the chatroom will send messages to the client directly, not through the server. See Figure 1, connection D. This will be discussed in more detail below.


























Figure 1: Layout of processes and connections.



5


    • Message Passing

The following sections will discuss the sequence of messages passed from process to process given certain user input. We will use Figure 1 as a guide. The boxes in Figure 1 represent running processes. For exam-ple, in Figure 1 there are two GUI processes running, namely GUI1 and GUI2. Letters A to D represent communcation connections between processes.

When a process sends a message to another process along a certain connection, the letter denoting the communication connection will refer to the messaging connection letters in Figure 1, and the messaging connection letter will be in [square brackets].

As we will have to test all your code, it is important that you stick to the described message passing protocols, or your code will fail our test cases. Additionally, we will be listening for all the proper processes to be created, and for the correct processes to speak with each other in the speci ed portions of the programs. In short, don’t get too creative and try to stick to the specs.

3.1    New client is initialized

When a new client is created, it automatically sends a message consisting of the tuple fself(), connect, InitialState#cl st.nickg to the server. The server then stores that client’s PID and nickname in its state (see server.erl:30). This is already implemented.


3.2    /join #chat

    1. The GUI will send the message frequest, self(), Ref, fjoin, ChatNamegg to the client [A].


    2. The client checks in its cl st record to see if it is already in the chatroom. If the client is already in the chatroom identi ed by ChatName, then the message fresult, self(), Ref, errg [A] should be sent back to the GUI, and the following steps should be skipped. Otherwise, if the client is not in the chatroom, continue on to step 3.

    3. Since the client is not currently in the chatroom identi ed by ChatName, the client should ask the server to join said chatroom. The client will send the message fself(), Ref, join, ChatNameg [B] to the server.

    4. Once the server receives the message from the client, the server needs to check if the chatroom exists yet. This can be done using the chatrooms element of the serv st record. If the chatroom does not yet exist, the server must spawn the chatroom.

    5. Next, the server should look up the client’s nickname from the server’s serv st record.





6


    6. Once either the existing chatroom PID is found or the new chatroom is spawned, the server must tell the chatroom that the client is joining the chatroom. To achieve this, the server will send the message fself(), Ref, register, ClientPID, ClientNickg [C] to the chatroom.

    7. The server will then update its record of chatroom registrations to include the client in the list of clients registered to that chatroom.

    8. Once the chatroom has received the message from the server, it will update its local record of regis-tered clients. Then it will tell the client about itself by sending the following message to the client: fself(), Ref, connect, State#chat st.historyg [D] where State is the chatroom’s chat st.


    9. The client will receive the message, update its record of connected chatrooms, and send the message fresult, self(), Ref, Historyg [A] back to the GUI, where History is the chatroom history received from the chatroom process.

    10. The GUI code with which you have been provided will then handle that request and create the appropriate tab, populating it with the chat history.


3.3    /leave or /leave #chat name


As far as the client is concerned, both of these commands are executed the same. In the case that the user enters the command /leave, the GUI simply lls in #chat name by analyzing the active tab. Either way, the same message is sent o to the client, and you can treat them as equivalent.


    1. GUI sends the message frequest, self(), Ref, fleave, ChatNamegg [A] to the client.

    2. The client needs to check that it is in the chatroom with the name ChatName. If the chatroom is not found in the client’s list of connected chatrooms, then the client should send the message fresult, self(), Ref, errg [A] to the GUI, and should skip steps 3 and on.

    3. If the chatroom is found, then the client should send the message fself(), Ref, leave, ChatNameg [B] to the server.
    4. The server will lookup the chatroom’s PID from the server’s state serv st.

    5. The server will remove the client from its local record of chatroom registrations.

    6. The server will send the message fself(), Ref, unregister, ClientPIDg [C] to the cha-troom.


        (a) the chatroom will remove the client from its record of registered clients.

    7. The server will then send the message fself(), Ref, ack leaveg [B] to the client.

    8. The client will then remove the chatroom from its list of chatrooms.


7


    9. The client will then send the message fresult, self(), Ref, okg [A] back to the GUI.

    10. The GUI will handle deleting the tab and such.

3.4    /whoami

    1. The GUI sends the message frequest, self (), Ref, whoamig [A] to the client.

    2. The client will then send the message fresult, self(), Ref, Nicknameg [A] back to the GUI, where Nickname is the nickname found in its state cl st.

    3. The GUI will then take care of displaying the nickname in the System tab.


3.5    /nick new nickname


    1. The GUI will send a message frequest, self (), Ref, fnick, Nickgg [A] to the client.

    2. The client should check Nick against its current nickname. If the client nds that Nick is the same as current nickname, then the client should send a message to the GUI fresult, self(), Ref, err sameg [A], and skip steps 3 and on.

    3. If client nds that Nick is not the same as current nickname, then the client will send a message fself(), Ref, nick, Nickg [B] to the server.
    4. The server rst needs to check if the new nickname Nick is already used. If the nickname is already in use by another client, the server will send the message fself(), Ref, err nick usedg [B] back to the client, and the client will send the message fresult, self(), Ref, err nick usedg [A] back to the GUI. If this is the case then skip steps 5 and on.

    5. Since we now know that the new nickname Nick is free to be used, the server must update it’s record of nicknames by pointing that client’s PID to the new nickname instead.

    6. The server must now update all chatrooms to which the client belongs that the nickname of the user has changed, by sending each relevant chatroom the message fself(), Ref, update nick, ClientPID, NewNickg [C] to the chatrooms.

    7. The server will then send the message fself(), Ref, ok nickg [B] to the client.

    8. The client will then send the message fresult, self(), Ref, ok nickg [A] back to the GUI.

    9. The GUI will take care of alerting the user to the change in the System tab.








8


3.6    Send a message

The send-a-message protocol needs to be broken into two contexts: The client who initializes the chat message, and the other clients in the chatroom to which the message is sent. The former of these will be quite similar to what you have seen before. However, pay special attention to the latter half of the protocol as it needs to use an API call that is otherwise not part of this assignment. This is necessary just to simplify the message passing for you, and to simplify the GUI application for us. The rst client (the one who sends the message) will be denoted sending client, and the clients who receive the message initiated by sending client will be denoted as receiving client or receiving clients.




3.6.1    Sending client

Any text entry that the user enters into the graphical interface such that the rst word is not prefaced with a forward-slash (/) is recognized as an outgoing message. This message will be sent to the chatroom identi ed by the active tab. Obviously a message being sent to the System tab is not allowed as this is not a chatroom, but merely a place for all non-chat messages to be put. Fortunately, you do not need to handle this as the message will be denied by the GUI code.

The protocol will be as follows:

    1. The GUI will send the message frequest, self(), Ref, foutgoing msg, ChatName, Messagegg

        [A] to the sending client.

    2. The sending client must look up the PID of the chatroom in its list of connected chats.

    3. The sending client will then send the message fself(), Ref, message, Messageg [D] to the chatroom.

    4. The chatroom will then send back to the sending client the message fself(), Ref, ack msgg

        [D] The chatroom’s behavior will be continued below, as it only relates to the receiving client(s).

    5. The sending client will then send back the message fresult, self(), Ref, fmsg sent, St#cl st.nickgg [A] to the GUI.
    6. The GUI will then take care about posting to the chat history for the appropriate tab.


3.6.2    Receiving client(s)

This bit of the protocol picks up after the chatroom receives the new message from the sending client.

    1. The chatroom will send a message frequest, self(), Ref, fincoming msg, CliNick, State#chat st.name, Messagegg [D] to each receiving client registered to the chatroom except for the sending client! In other words, if the clients Beezle, Bub, and Blitzen are registered


9


to the chatroom, and Bub sent the initial message to the chatroom, the chatroom will send this message to Beezle and Blitzen.

    2. The chatroom will then append the new Message to its own chat history.

    3. When the receiving client receives the message outlined in step 2, it will make the following func-tion call:

gen server:call(list to atom(State#cl st.gui), fmsg to GUI, ChatName, CliNick, Msgg). See explanation below for information on what this is.

    4. The GUI will then receive the message fmsg to GUI, ChatName, CliNick, Msgg through that function call in step 3, and post the message to the appropriate chatroom tab.


Explanation for gen server:call(...) function (if interested)


The gen server [link] is a server that is used in the GUI implementation. Since the GUI doesn’t have any open receive statements listening for incoming messages, we needed to use the gen server as a way to contact the GUI. For further reading, follow the link above, although you will have no need for greater understanding in this assignment.


3.7    /quit

This section perhaps warrants the most detailed attention, as everything must be cleaned up properly. When a user quits, the server and all chatrooms must be properly updated, and the client and GUI must be properly spun down.

NOTE: if the GUI window does not close, BUT when you enter commands into the prompt and hit enter, nothing happens, do not worry. There is some issue we were having with the visual element of the GUI properly exiting, and it was frankly not worth our time to debug this as it does not a ect the students’ end of the assignment. If someone comes up with a x, that’s great and please let us know, but it is not your responsibility to x this.

    1. The GUI will send the message frequest, self(), Ref, quitg [A] to the client.

    2. The client will send the message fself(), Ref, quitg [B] to the server.

    3. The server must clean up a bit.

        (a) Remove client from nicknames.

        (b) Tell each chatroom to which the client is registered that the client is leaving. To do so, send the message fself(), Ref, unregister, ClientPIDg [C] to each chatroom in which the client is registered. Note that this is the same message as when a client asks to leave a chatroom, so this should be handled already.

        (c) Remove client from the server’s copy of all chat registrations.

10


    4. The server must then send the message fself(), Ref, ack quitg [B] to the client.

    5. The client must then send the message fself(), Ref, ack quitg [A] to the GUI.

    6. The client must then cleanly exit. The GUI will exit cleanly upon receiving the message from the client (apart from in the situation discussed above).


3.8    Speci cs of implementation

    1. There is a make le attached. To build the application, typemake run into your terminal in the directory containing your code. This will also launch the Erlang interpreter for you.

    2. The program will be started with main:start(). This will spawn a server and 2 GUIs.

    3. The caller can specify the number N of GUIs to be started initially with main:start(N).

    4. The caller can run additional GUIs with gui:start gui().


The following items should be followed very closely. Failure to do so will cause the program to fail test cases.

    1. GUI code should not be altered to make your code work. We are testing your code as if the GUI code has not been altered.

    2. When a client receives the string of the name of a chatroom, it includes the pound symbol (#) at the beginning of the chatroom name. Leave this in, and store this as part of the chatroom name.

    3. Client’s nicknames should be stored as strings, not converted to atoms.

    4. When the server registers a new client to a chatroom, append the new client to the front of the chatroom’s registration list in the server state.

    5. We will not test the order in which items appear in maps i.e.

ffoo => ‘‘food’’, bar => ‘‘barber’’g will be tested the same as

fbar => ‘‘barber’’, foo => ‘‘food’’g

but you should not go about recreating the maps library. Just use erlang’s library calls.

    6. Do not alter the declaration of the records in defs.h. This includes ordering of its elements, renaming its elements, etc. Leave defs.h alone.



11


    7. Note that you should not create new references with make ref() anywhere in your code. The GUI code will take care of creating a unique reference ID for each sequence, and you should use this reference for the entirety of the messaging sequence. Creating new reference IDs will result in failing test cases.


Above all, ask questions when you are confused! This is a complex assignment with a lot of intricate details.

3.9    Usage trace with expected states

This is an outline of the usage of the program (with some assumptions made about initial client nickname), showing the states of all processes at the end. You can use this as a way to compare that your program does what is expected.

    1. main:start() is called, spawning 2 GUIs (client 1 with nickname \client1", and client 2 with nickname \client2") and 1 server. The GUI for client 1 has name \gui1", and the GUI for client 2 has name \gui2"

    2. client 1 issues command /nick newclient1: client 1 changes nickname to \newclient1"

    3. client 1 issues command /nick newclient1 again:

client 1 tries to change nickname to \newclient1" again (and fails to do so)

    4. client 1 issues command /nick client2:

client 1 tries to change nickname to \client2" (and fails to do so)

    5. client 1 issues command /join #chat1: client 1 joins chatroom 1 \#chat1"

    6. client 1 issues command hello from the #chat1: tab client 1 sends message \hello" to chatroom 1

    7. client 1 issues command /nick newclient1 2: client 1 changes nickname to \newclient1 2"

    8. client 1 issues command /join #chat2: client 1 joins chatroom 2 \#chat2"

    9. client 1 issues command /nick newclient1 3: client 1 changes nickname to \newclient1 3"

    10. client 1 issues command /leave #chat1: client 1 leaves chatroom #chat1




12


    11. client 1 issues command /leave #notachatroom:

client 1 tries to leave some chatroom that does not exist or to which they are not connected (and fails to do so)

    12. client 2 issues command /join #chat1: client 2 joins chatroom 1

    13. client 2 issues command /join #chat2: client 2 joins chatroom 2

    14. client 2 issues command world from the #chat2 tab: client 2 sends message \world" to chatroom 2

    15. client 1 issues command /join #chat1: client 1 rejoins chatroom 1

    16. client 1 issues command /join #chat1:

client 1 tries to join chatroom 1 again (and fails to do so)

The    nal states of all 5 processes will be as follows:

    • Client 1:

fcl st, ‘‘gui1’’,‘‘newclient1 3’’,

#f‘‘#chat1’’ => <chat 1 pid>, ‘‘#chat2’’ => <chat 2 pid>gg

    • Client 2:

fcl st, ‘‘gui2’’, ‘‘client2’’,

#f‘‘#chat1’’ => <chat 1 pid>, ‘‘#chat2’’ => <chat 2 pid>gg

    • Chatroom 1:

fchat st,‘‘#chat1’’,

#f<client 1 pid> => ‘‘newclient1 3’’,

<client 2 pid> => ‘‘client2’’g, [f‘‘newclient1’’,‘‘hello’’g]g

    • Chatroom 2:

fchat st,‘‘#chat2’’,

#f<client 1 pid> => ‘‘newclient1 3’’,

<client 2 pid> => ‘‘client2’’g, [f‘‘newclient2’’,‘‘world’’g]g








13


    • Server:

fserv st, #f<client 1 pid> => ‘‘newclient1 3’’, <client 2 pid> => ‘‘client2’’g,
#f‘‘#chat1’’ => [<client 1 pid>,<client 2 pid>], ‘‘#chat2’’ => [<client 2 pid>,<client 1 pid>]g,
#f‘‘#chat1’’ => <chat 1 pid>, ‘‘#chat2’’ => <chat 2 pid>gg


    • Submission Instructions

Submit a le tchat.zip containing all the les listed below. The list below also indicates which les from the stub you should have left unmodi ed and which ones you should have completed with your own code. One submission per group.

File Name
Do I have to modify this  le?


chatroom.erl
Yes
client.erl
Yes
defs.hrl
No
grm.yrl
No
gui.erl
No
lex.xrl
No
lexgrm.erl
No
main.erl
No
make le
No
server.erl
Yes

























14

More products