September 24, 2018











WebSocket is a protocol that makes two-way communication in real-time between the user and the server possible. A common use cases are chats and online multiplayer games. Today we cover implementing it both on frontend and backend. Let’s go!

Explaining WebSockets

WebSocket is a different protocol than HTTP. Despite that, to establish a connection, the client sends a WebSocket handshake request which is HTTP call. Let’s look into the request call:

1 2 3 4 5 6 7 Request URL : ws : //localhost:8080/ Request Method : GET Status Code : 101 Switching Protocols Headers : Connection : Upgrade Upgrade : websocket

This request contains the Connection: Upgrade and Upgrade: websocket headers that indicate that the client wishes to establish a WebSocket connection. The same headers can be found in the server response. You can see here that WebSocket URLs use the ws:// scheme. There is also wss:// for the WebSocket Secure connections which is encrypted in a similar manner than HTTPS and you should always use it if possible. Once the connection is established, the communication is continued in a two-way manner, which is different than HTTP.

Preparing a server able to create a WebSocket connection

While there are quite a few libraries that help you handle the WebSocket connections. One of the ways is to use ws, which is a backend only library. Another one is Socket.io, which covers both backend and frontend needs. Since we want to walk through the native WebSockets API, today we will use ws. Let’s prepare a simple server that will enable us to make a WebSocket connection.

1 2 3 4 5 6 7 8 9 10 const WebSocket = require ( 'ws' ) ; const server = new WebSocket . Server ( { port : 8080 } ) ; server . on ( 'connection' , socket = > { socket . on ( 'message' , message = > { console . log ( ` received from a client: $ { message } ` ) ; } ) ; socket . send ( 'Hello world!' ) ; } ) ;

Through the WebSocket.Server constructor we create an instance of a WebSocket server. By passing { port: 8080 } we bind it to the port 8080.

The WebSocket.Server.prototype inherits from the EventEmitter, which is one of the native prototypes provided in Node.js. Thanks to that we can call the on function on it and listen to the connection event for upcoming connections. The connection event is emitted when the handshake between the client and the server is complete.

If you would like to know more about prototypes, check out Prototype. The big bro behind ES6 class

When that happens, the callback is called and its first argument is the socket, an instance of the WebSocket.prototype. It inherits from the EventEmitter too. Through it, you can listen to any upcoming messages from the clients through the message event.

Another crucial line here is the usage of the send function. With it, you can send data to the clients. It can be of any type, the object included.

If you prefer using Express, you can surely do that. Instead of just sending a port to the WebSocket.Server constructor, you can pass your server there:

1 2 3 4 5 6 const WebSocket = require ( 'ws' ) ; const express = require ( 'express' ) ; const app = express ( ) ; const server = new WebSocket . Server ( { server : app . listen ( 8080 ) } ) ;

When you run the script, the server starts listening for input. Let’s provide it with some.

Sending messages from the client

In all modern browsers, we have a built-in API to handle WebSockets. If you are worried about older browsers, you might give the already mentioned Socket.io a try, because it contains a polyfill for such cases.

Here is a basic example:

1 2 3 4 5 6 7 8 9 const socket = new WebSocket ( 'ws://localhost:8080' ) ; socket . addEventListener ( 'open' , ( ) = > { socket . send ( 'Hello World!' ) ; } ) ; socket . addEventListener ( 'message' , event = > { console . log ( ` Message from server : $ { event . data } ` ) ; } ) ;

We pass an URL to which to connect to the WebSocket constructor and receive an instance of the WebSocket object. As you can see, it begins with ws:// – this prefix indicates a WebSocket connection.

When you run EventTarget.prototype.isPrototypeOf(socket) you will notice that WebSockets inherits from EventTarget. Thanks to that, you can listen to events. One of them is the open event that fires when the connection is opened. Another important one is the message event that happens when the message is received from the server.

When you call the send function, the message is sent to the server. An important thing to notice is that it can’t be of just any type. Here you can find the list of all possible types.

Having all that knowledge is almost enough to build a simple chat application. The frontend part is pretty easy:

1 2 3 4 5 6 7 8 9 10 11 12 function sendMessage ( socket , userName , content ) { socket . send ( JSON . stringify ( { userName , content } ) ) } function receiveMessage ( message ) { console . log ( ` $ { message . userName } is saying: $ { message . content } ` ) }

Every time a user clicks a button to send a message you pass it to the server along with his username. When there is an incoming message, you display it in any way you see fit.

On the backend side, there is a small catch. You need to send the message to all connected clients. You can do that thanks to the fact that all clients are being tracked in the server.clients array:

1 2 3 4 5 6 7 server . on ( 'connection' , socket = > { socket . on ( 'message' , message = > { server . clients . forEach ( client = > { client . send ( message ) ; } ) ; } ) ; } ) ;

And you’re done! Simple, isn’t it?

Summary

The article covered the very basics of WebSockets. It includes an explanation of what they are, how to create a simple server that handles WebSocket connections and how to connect with it on the frontend. To do that we used the ws library on the backend and the WebSocket API in the browser. This is enough to build your own simple live chat, which is a good way to practice the knowledge contained here. Good luck!