Sunday, September 15, 2024

Getting Started with WebSockets

In this article we will work with the concept of WebSockets, which are a new addition to the HTML5 specifications and, in turn, allow a web server to establish a connection with the browser and communicate directly with it, without any delay. Normally, regular communication consists of a series of requests and responses between the browser and the web server, where, for web applications that work in real time, this technique is not very efficient.

With the use of WebSockets, we can establish a connection only once, and then the communication between the server and the browser can follow without delays or lags. This is the kind of technology we may need to use for applications that require regular and quick updates from a web server, such as multiplayer gaming and chat applications, among others.

The initial idea for the construction of the web was that a client requests data and the server fulfills these requests by passing the requested data. This was an unquestionable paradigm for years, until AJAX starts to do its magic around 2005, where many explorers looked for new possibilities to make connections between a client and server in a bidirectional manner. With the growth of web applications, the amount of data being consumed was growing exponentially, and the most important way to maintain these applications was to use the traditional HTTP transaction model initiated by the client.

In order to overcome this barrier, many strategies had been developed to allow servers to send data to clients, among which the Log-Polling strategy was highlighted, where the goal was to keep the HTTP connection open until the server had data available to pass to the client. The problem is that this kind of solution led HTTP to an overhead with increasing latency. So, what could be done that there was a persistent connection to support transactions initiated between server and client and that in addition, had a low latency? This is what we will discuss in this article, which is the use of WebSockets.

How to Work with WebSockets?

WebSockets are intended to provide a persistent connection between a client and a server where both parties can be used to initiate the sending of data at any time. In this way, the client establishes a WebSocket connection through a process, in which the client sends a request via HTTP to the server on a regular basis, thus an update header is included in this request that informs the server that the client wants to establish a connection via WebSocket. An example header that can be sent is displayed as follows:

GET ws://example.websocket.com/ HTTP/1.1
 Origin: http://example.com
 Connection: Upgrade
 Host: example.websocket.com
 Upgrade: websockets

As we can see, the WebSocket URL uses the ws schema, where in addition to this, we can still use WSS that is for secure WebSocket connections, which is equivalent to HTTPS. In case the server supports the use of WebSockets protocols, it agrees with the update and returns a new update header in its response, as we can see below:

HTTP 101 WebSocket Protocol Handshake
Date: Wed, 16 Jun 2018 22:07:34 GMT
Connection: Upgrade
Upgrade: WebSocket

Once the communication has been completed, the initial HTTP is replaced by a WebSocket connection that uses the same underlying TCP/IP connection, from this moment either party can initiate the sending of the data. With WebSockets, data transfer can be done without the occurrence of overloads associated with HTTP requests, where the data is transferred through a WebSocket as a message that consists of one or more frames and have the data that are being sent.

In order for the message to be properly rebuilt, upon reaching the client, each frame is prefixed with a size of 4 to 12 bytes of payload data. With the use of this frame-based messaging system, there is a significant reduction in the latency and amount of data that would be sent in a single packet. It is important to note here that the client will only be notified of the new message once all the frames have been received and the original message load has been rebuilt.

So that we can better understand the concept of WebSockets, nothing better than learning by developing a simple example, for this, we will present from this moment some of the most important and common items in the use of WebSocket in HTML5.

Creating a Simple Example

To start with our example, we will first create our index.html page, according to the one presented in Listing 1.

Listing 1. Creating the index.html page.

<!DOCTYPE html>
<html>
  <head>
	  <title>WebSockets with HTML5</title>
	  <meta charset="UTF-8">
	  <meta name="viewport" content="width=device-width, initial-scale=1.0">
	  <link rel="stylesheet" href="style.css">
  </head>
  <body>
	  <div id="page-wrapper">
		  <h1>Example of WebSockets</h1>

		  <div id="status">Conecting to the app…</div>

		  <ul id="messages"></ul>

		  <form id="form-msg" action="#" method="post">
			  <textarea id="msg" placeholder="Write your msg here!" required></textarea>
			  <button type="submit">Send msg</button>
			  <button type="button" id="close">Close connection</button>
		  </form>
	  </div>
	  <script src="app.js"></script>
  </body>
</html>

At this point, we have some key elements created that will be used by our application, among which we have here a <div> responsible for displaying messages about the connection status; besides it, we have a list that will be used to present the messages sent and received from the server; and, we will have a form to insert the messages that will be sent.

Now that we have created our index.html page, realize that we will also have the creation of a CSS file, which we call style.css and a JavaScript file, which we call app.js, both presented at Listings 2 and 3.

Listing 2. Creating the style.css file.

*,
*:before,
*:after {
	-moz-box-sizing: border-box;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
}

html {
	font-family: Helvetica, Arial, sans-serif;
	font-size: 100%;
	background: #333;
}

#page-wrapper {
	width: 650px;
	background: #FFF;
	padding: 1em;
	margin: 1em auto;
	border-top: 5px solid #69c773;
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
}

h1 {
	margin-top: 0;
}

#status {
	font-size: 0.9rem;
	margin-bottom: 1rem;
}

.open {
	color: green;
}

.closed {
	color: red;
}

ul {
	list-style: none;
	margin: 0;
	padding: 0;
	font-size: 0.95rem;
}

ul li {
	padding: 0.5rem 0.75rem;
	border-bottom: 1px solid #EEE;
}

ul li:first-child {
	border-top: 1px solid #EEE;
}

ul li span {
	display: inline-block;
	width: 90px;
	font-weight: bold;
	color: #999;
	font-size: 0.7rem;
	text-transform: uppercase;
	letter-spacing: 1px;
}

.sent {
	background-color: #F7F7F7;
}

.received {}

#form-msg {
	margin-top: 1.5rem;
}

textarea {
	width: 100%;
	padding: 0.5rem;
	font-size: 1rem;
	border: 1px solid #D9D9D9;
	border-radius: 3px;
	box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
	min-height: 100px;
	margin-bottom: 1rem;
}

button {
	display: inline-block;
	border-radius: 3px;
	border: none;
	font-size: 0.9rem;
	padding: 0.6rem 1em;
	color: white;
	margin: 0 0.25rem;
	text-align: center;
	background: #BABABA;
	border-bottom: 1px solid #999;
}

button[type="submit"] {
	background: #86b32d;
	border-bottom: 1px solid #5d7d1f;
}

button:hover {
	opacity: 0.75;
	cursor: pointer;
}

Now that we have our style class created, where we have defined only a few items for a better view of our example project, we will start with the creation of our JavaScript file, as presented by Listing 3.

Listing 3. Creating the app.js file.

window.onload = function() {
  
  // get the references of the page elements.
  var form = document.getElementById('form-msg');
  var txtMsg = document.getElementById('msg');
  var listMsgs = document.getElementById('msgs');
  var socketStatus = document.getElementById('status');
  var btnClose = document.getElementById('close');
};

As shown, we have created several variables here and initialized them to search for the key elements on the page. With this, we have a demo application configured and therefore we can start learning about the WebSocket API. At first, we will see here how we can make a connection through WebSocket. To create a connection there will be no difficulty, where all we need to do is call the WebSocket constructor and pass the server URL that will be used, as we can see from the following code (to be included in the app.js file) presented by Listing 4.

Listing 4. Opening a connection to WebSockets.

// Creating a new WebSocket connection.
 var socket = new WebSocket('ws://echo.websocket.org');

Once we have the connection established, the open() event will be triggered to our WebSocket instance. In our application, what we are doing is adding an event listener that will update the status of the <div> with a message, showing that the connection has been established. Now that we have our connection, let’s add the following code snippet in our app.js file, as shown in Listing 5.

Listing 5. Introducing a WebSocket opening message.

socket.onopen = function(event) {
	socketStatus.innerHTML = 'Connected to: ' + event.currentTarget.URL;
	socketStatus.className = 'open';
};

With WebSockets we can also recover errors that may occur during a listener that starts from an error event. In our case, for simplicity, we are only presenting these errors on a console through the code shown according to Listing 6.

Listing 6. Code referring to occurrence of errors.

socket.onerror = function(error) {
	console.log('WebSocket error: ' + error);
};

Now that we’ve seen how to make a connection and catch errors, what else can we have with the WebSockets? What we are missing now is the sending of the messages, which, to be sent through the WebSocket connection, must be called by the send() method of a WebSocket instance, that we will pass the data to be transferred, just as follows:

socket.send(data); 

With WebSockets, we can send both text and binary data, in our example we will send the contents of the field to the server when the form is sent. For us to do this, we will need to initially have a listener event configured on the form, which we will do according to Listing 7.

Listing 7. Sending the form data.

form.onsubmit = function(e) {
    e.preventDefault();
    
    // Recovering the message of the textarea.
    var msg = txtMsg.value;
    
    // Sending the msg via WebSocket.
    socket.send(msg);
    
    // Adding the msg in a list of sent messages.
    litsMsgs.innerHTML += '<li class="sent"><span>Sent:</span>' + msg + '</li>';
    
    // Cleaning up the field after sending.
    txtMsg.value = '';
    
    return false;
};

When the form is submitted, the code presented in Listing 7 is intended to retrieve the message from the text message field and then send it through WebSocket. The message is then added to the message list called listMsgs and displayed on the page. To finish our example, we need to get the txtMsg value as a ready response for the user to type a new message.

Receiving a Message

When a message is received the event is triggered. This event includes a property called data that can be used to access the content of the message. For our example, we will need to create a listener event that will be triggered when a new message is received, so the code must retrieve the event message and display it in the message list. This method is being presented according to Listing 8.

Listing 8. Copying the message that is sent to the server.

socket.onmessage = function(event) {
    var msg = event.data;
    listMsgs.innerHTML += '<li class="received"><span>Received:</span>' + msg + '</li>';
};

After we have finished using our WebSocket, what we need to do is close our connection, so just use the close() method for that:

socket.close();

After our connection has been closed, the browser will trigger a close event. To demonstrate that our connection has been closed, we will only update the connection status according to the code shown in Listing 9, which will also be added to our app.js file.

Listing 9. Closing the WebSocket connection.

socket.onclose = function(event) {
    socketStatus.innerHTML = 'Disconnected from the WebSocket.';
    socketStatus.className = 'closed';
};

Conclusion

With the use of WebSockets, we have seen that we can replace long-pollings, a very interesting concept in which a client sends a request to the server, and essentially keeps the connection open until refresh occurs; but dangerous because of the big lantecy it generates.

The uses for WebSockets are expressively huge, and many companies are using it today to accomplish certain goals on that reversed communication path. Feel free to explore it and take a look at the demos page of the official WebSocket website.

About the Author

Diogo Souza works as a Java Developer at PagSeguro and has worked for companies such as Indra Company, Atlantic Institute and Ebix LA. He is also an Android trainer, speaker at events on Java and mobile world.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured