Welcome to Erlang!
I teach a course with an assignment almost exactly like this, minus the chat_script
. I have the client code have a function that starts a recursive function that reads lines and sends them to the server, which them sends each client line to all the other clients.
Students often wrestle with gen_server
s. I think a big problem is understanding what code runs where/when — they find the callback model of OTP code a bit challenging. For this reason, I usually have them write a chat server and client manually, and then use gen_server
.
My advice is, as with any large problem, to break it into pieces, and don’t be afraid to experiment or put in temporary bits of code (including debug print statements — I’m old school
).
I’ll make some suggestions, but you can do whatever works for you. Maybe this will get you out of the gate…
Maybe start with the server. For a server, you need to know the server’s state and its message protocol, which encodes what operations the server supports. The operations come in two flavors: server administration (which is usually start
and/or start_link
and stop
) and operations clients will need.
For a chat server, clients should at least be able to join/subscribe to a chat room and send messages to the room. It looks as if your server is just a single room. Maybe there is a way for them to leave the room, too. You can decide whether these operations are calls or casts, i. e., whether a client needs to await a response. If you aren’t sure, pick one. You can change it later.
For the server state, you’ll need to pick a data structure that can keep track of the clients in a way that would allow the server to send a message a client sends to the server to all the other clients in the room.
That should get you started.
In general, the way I suggest building this out is to start with a server that does nothing: basically, the gen_server
out of the box. Then add default handlers for calls and casts that print out “unhandled call/cast: …” for any message. Just define those: initially, all calls and casts will be unhandled. Then I add a call that returns the state, which I can remove later. This will let you start the server in the Erlang shell, ask it for its state any time, and see any unhandled message.
Once that works, I would start adding some real call and/or cast handlers, one at a time. Invoke each one getting the state before and after. Maybe have the server mock up stuff you haven’t figured out yet ("will send message to other clients her).
Once you can add a client this way, you can start another Erlang shell in another terminal and try to add a second client.
Once you can do this, you can use what you were typing into the shells as the start of your client module’s code.
I have some not very polished notes based on stuff I go over in class, and you are welcome to have a look. Please don’t expect support
gen_server
overview and Getting started with gen_server
s. (The assignments and solutions are not available on the web now, and that’s on purpose.) Again, this is pretty rough, but if you are feeling stuck, it may help get moving!
I hope this is useful. Don’t forget to enjoy the ride! Programming in Erlang is super fun!