This repository contains a multi-user chat server with support for rooms built on top of the telnet protocol.
We leverage the telnetlib
library to handle the Telnet protocol details. It provides an
implementation of Node's
net.Server
TCP
server that understands Telnet datagrams.
npm i
npm run dev
All of this should be fairly obvious while reading the code, but in the interest of looking out for my future-self, who will absolutely have forgotten about every detail of writing this, I think it's valuable to lay this out explicitly in prose.
At a high level, event handlers are attached to the socket object in the following order at request-time:
negotiated
username set
client constructed
When a client connects to the server, telnetlib
handles all the
connection
negotiation.
Once it does that, it
emits
the negotiated
signal.
In this listener, we handle that signal by displaying a welcome message and prompt the user to enter a unique username.
Once the user does so, username set
is emitted with the validated
username as the payload.
In this listener, we use the username to construct the Client
object
we'll be storing in memory. We then display some additional
info/instructions to the user.
Once all that has been written, we emit the client constructed
event
with the Client
object as the payload.
Once we have the client, we attach our listeners for data
(i.e.
handling user input) and end
(handling user disconnection).
The server is built around two main concepts:
Client
: TheClient
class closes over the logic for handling any action a user can do, such as creating or leaving a room. It is also responsible for sending feedback messages over the socket to the user when they perform an action.Room
: TheRoom
class is responsible for both broadcasting chat messages to other users, as well as writing chat logs to disk every so often.
When a new telnet client connects to the server, a Client
class is
instantiated and stored in memory in the
ALL_CLIENTS
map. This map maps usernames to clients.
The client has a handle on the telnet socket. It uses this to send feedback messages to the end user, such as success messages when the user creates a room.
The client also has the concept of the "current
room."
This is a handle on a Room
object. The user can only chat in, change
the topic of, etc., the "current room."
Additionally, the client tracks all the rooms in which it is a member. The client will receive messages from any room it's a member of, even if it's not the "current room."
Each room, of course, tracks all the clients "in" the room. Being "in the room" means that the user will receive messages sent by other users to that room.
Each room will also log the chat to disk. This is currently just bare minimum, very primitive implementation of logging. It's not being used for anything, but was rather an exercise in making it work.
On process termination, any outstanding logs in memory are synchronously flushed to disk.