In my latest post, I have presented WebSockets as the modern way to realize “server push”, and generally for anything that involves much communication between client and server.
But: While it is generally very easy to use WebSockets directly (like using the native browser API, or using libraries that implement the reference protocol), there are some annoyances. Here are the main ones for me:
- There is no automatic reconnection logic. Imagine a web application that is connected to a server. You are using the web application on your laptop while sitting in a train & using the WiFi onboard. If your laptop temporarily looses the connection, e.g. because you closed the lid or because the WiFi stopped working, the connection won’t be automatically resumed.
These are no big problems. For (1), you could implement a retry logic: After the connection is lost, the web application could try to reconnect every X seconds - and additionally when the network connection changes (see here for the Network Information API - not supported in all browsers). Or you use a small wrapper library like reconnecting-websocket. For (2), you could implement
receive wrapper functions that serialize the data before sending, and de-serialize it on receival.
Still, for my past projects, I preferred not to fiddle with these annoyances, but to use a library like Socket.IO. Socket.IO is not a WebSocket implementation. Instead, it is a client-server communication framework that abstracts away they actual transport technology, and offers easy-to-use
receival methods. Under the hood, it starts with long-polling, and then tries to upgrade the connection to WebSockets. On top of that, it adds features like automatic reconnection logic, and sending arbitrary data structures (as long they are serializable). By using Socket.IO, you won’t have to implement all things on your own and can focus on your actual application logic.
Building a simple chat
Let’s dive into it with a simple chat! You can try out the live chat here: https://chat.sukram21.repl.co The full code is public on Repl.it - you can directly run the code, modify it, and play with it! 😀
Let’s start with the server. What should the server do? Probably something like this:
- Whenever a new client connects, it should broadcast a “New client has joined” notice to all clients (the new client included).
- Whenever a client sends a message to the server, it should broadcast this message to all clients (the sender inluded).
- Whenever a client disconnects, it should broadcast a “Client has left” notice to all remaining clients.
And that’s all! Besides setting up the server, we just have to implement three event listeners and their respective handlers. Around 20 LoC (lines of code) including (!) the white space:
Please note that using the
express package is not strictly necessary. Instead, you could simply use the built-in Node.js web server. The reason I am using express here is so my Node.js webserver can serve the static website files as well (
app.use(express.static('public')), meaning the client app.
If this was a larger project, I would rather split my project into an
api and an
app part. And then serve the static
app files directly through a much faster web server like nginx or Caddy.
That’s already all for the server, now getting to the client…
The client code is a litte bit more complex than the server code. Not regarding the actual logic, but because we have to deal with HTML elements. My client code assumes that we have an HTML form with an input field and a submit button, and a readonly textarea that will act as a chatlog. Please see the code on Repl.it for the exact HTML & CSS code.
So, what should the client exactly do?
- Whenever a user submits the form, the form’s input value should be sent to the server.
- Whenever a new chat message from the server is incoming, the message should be appended to the chatlog.
- Whenever a new notice from the server is incoming, the notice should be appended to the chatlog.
This code does what we were planning, and it does a bit more:
- On load, it makes the browser focus the input element.
- It generates a random user name that is used for sending the chat messages.
- After the message was sent, it clears the input.
- On new messages, it makes the chatlog scroll to the bottom.
Under 50 LoC - together with the server JS code, we are at exactly 70 LoC, including whitespace & comments. Pretty neat!
I hope this blog post gave you a bit of an inspiration what is possible using Socket.IO (of course, building on an exchellent technology like WebSockets). There is much to do, and great applications to be built! 😍
- Customizing my shell: From bash to zsh to fish
- JSON5: JSON with comments (and more!)
- jq: Analyzing JSON data on the command line
- Get Total Directory Size via Command Line
- Changing DNS server for Huawei LTE router
- Notes on Job & Career Development
- Adding full-text search to a static site (= no backend needed)
- Generating a random string on Linux & macOS
- Caddy web server: Why use it? How to use it?
- Tailwind CSS: A Primer