In this tutorial, we show how to easily run AJAX over WebSockets for browser to server communication. We also go over situations where AJAX should not be used and when real time messages sent over a WebSocket connection is preferred over using AJAX.
You are probably thinking why would I want to run AJAX over WebSockets. There are several benefits to running AJAX over WebSockets, such as:
(*) Few developers directly use the browser's antiquated and difficult to use XmlHttpRequest object, which is required for any type of AJAX interaction. Instead, they rely on AJAX libraries, such as the AJAX library in JQuery, which shields the developer from directly using the XmlHttpRequest object. The WebSocket API on the other hand is very easy to understand and use; thus, developers can easily take advantage of the WebSocket API without requiring a JavaScript library to simplify the programming.
AJAX stands for Asynchronous JavaScript and XML, but it really just refers to using any kind of asynchronous HTTP call from a web front-end to the server back-end. In fact most developers use JSON these days since XML is bulky and adds a lot of overhead.
A server back-end may provide many AJAX services, and to differentiate one service from another, each service may have its own URL. In other words, most modern AJAX calls are to REST APIs using JSON as the data serialization format. As an example, we may have a service 'math' that provides a set of mathematical services such as 'add' and 'subtract'. The 'add' service may use an URI such as "/math/add" and the 'subtract' service may use an URI such as "/math/subtract".
AJAX returns a single response to a single request. However, AJAX is asynchronous; thus, a web front-end can send any number of AJAX requests without having to wait for each response.
WebSockets on the other hand can handle multiple bidirectional messages between the browser and the server. There is no correlation between the messages sent in either direction. It is up to the application to interpret and manage these messages. If you have any TPC/IP socket experience, you can simply think of WebSockets as a TCP stream that includes a packet size header. Each WebSocket message is sent and received as one packet.
Implementing AJAX on a WebSocket connection is conceptually very easy:
To simplify the coding, we wrap the client side logic into an easy to use JavaScript library. There are a few AJAX specific features we need to think about when designing this library. As we mentioned above, AJAX is asynchronous on the client side, thus many AJAX requests can be in-flight at the same time. Each AJAX request is associated with a JavaScript callback that gets called when the server sends the response. We must find a way to uniquely identify each in-flight request such that each AJAX response triggers the correct callback function. The following figure depicts three asynchronous and in-flight AJAX requests:
Each of the asynchronous responses A', B', and C' must trigger the correct response callback function for the request A, B, and C, even if the response messages should be in a different order.
The easiest way to implement the client side AJAX callback handling is to store each callback in an object and give each callback a unique ID. The ID, which can for example be a number, is sent to the server as part of the message. The server side then bundles this ID with the response data.
When the client receives the AJAX response, the client finds the callback function by using the ID received from the server. The callback IDs need to be unique for each request/response pair. A super easy way to find a unique ID is to use a random number and look for collisions.
1 2 3 4 5 6 7 8 9 10 | var callbacks={} // saved AJAX callbacks: key=id, val=function function saveCallback(callback) { var rpcID; // Find a unique ID do { rpcID=Math.floor(Math.random() * 100000); } while (callbacks[rpcID]); // while collisions callbacks[rpcID]=callback; // Save callback, where rpcID is the key. return rpcID; // The ID sent to the server, where it is echoed. } |
Say we have a math AJAX service, where we can add two numbers:
1 | function math.add(a, b) => return a+b |
To call this from the client, we must provide the two arguments a and b, the callback function, and the math REST URI.
The API we designed for our AJAX over WebSocket client JavaScript library requires that the two arguments 'callback' and 'REST URI' are provided as the two first arguments. For example, the math.add(a,b) function must be called from the client as follows:
1 | ajax(callback, "math/add" , a, b) |
The AJAX response callback receives two arguments, the response data and possibly an error message. The response data 'rsp' is null if the error argument is provided.
1 | function myCallback(rsp,err) |
If you are a JavaScript developer, you are probably wondering why we use the antiquated asynchronous callback design pattern and not the new JavaScript Promise object. The callback API may be easier to understand for new JavaScript developers than the new Promise object, however we have included an additional library and example using the new Promise API. Here is an example on how to use this API:
1 2 3 4 | var ajax=createAjax( 'service.lsp' , .....); ajax( "math/add" , a, b). then( function (rsp) { console.log( "Result: " + rsp); }). catch ( function (err) { console.log( "Error: " + err); }); |
The AJAX over WebSocket client library is implemented in a mere 22 lines of code. To get this code and a fully working example, including the server code, download our AJAX over WebSocket Example.
The 'Mako Server' and the 'Barracuda App Server library' are typically used for device management and our focus is on these type of systems. For device management, the most obvious reason for not using AJAX is when the solution requires real time data pushed from the server to the client. AJAX can be used for polling new data, but it is much easier to simply push the real time data over a WebSocket connection.
Another reason for not using AJAX is much more subtle and may not be discovered until the complete solution is designed, resulting in expensive re-design if not properly thought of during the initial design phase. As an example, let's assume we need to design a solution for controlling one light bulb (one shared resource). The light switch state is presented in the browser as either on or off. AJAX may seem like the perfect candidate for such a simple solution. The user clicks the light switch in the browser, an AJAX request is sent to the server, the server turns the light bulb on or off, and the AJAX response is sent to the browser. However, there is a subtle problem with this solution -- the web is a multi user system enabling multiple users to surf to the same web server. In other words, the server (and the light bulb controlled by the server) is a shared resource that may be controlled by many users. What happens if two users try to turn the light bulb on or off at the same time? The two browser interfaces displaying the light switch state will be out of synch and not show the real on/off state for the light bulb. For this reason, AJAX is not a good solution for such a system.
Instead of using AJAX, we can take full advantage of WebSocket's bi-directional features and design a reactive system that encompasses the complete solution, including all connected browsers, and the server. The following figure depicts such a reactive system, where the light switch click is propagated to the server and then to all connected browsers.
WebSockets lets us respond to user inputs in real time and reactively update all connected browser user interfaces simultaneously.
See the tutorial Modern Approach to Embedding a Web Server in a Device if you are interested in learning more.
Real Time Logic provides three WebSocket implemenations:
The following video shows how fast Websockets are compared to traditional REST/AJAX interfaces. The client side is implemented in JavaScript and the server side is implemented in Lua. You can find more information on this project at the Barracuda App Server for ESP32 GitHub page page.
Navigate the world of embedded web servers and IoT effortlessly with our comprehensive tutorials. But if time isn't on your side or you need a deeper dive, don't fret! Our seasoned experts are just a call away, ready to assist with all your networking, security, and device management needs. Whether you're a DIY enthusiast or seeking expert support, we're here to champion your vision.
Expedite your IoT and edge computing development with the "Barracuda App Server Network Library", a compact client/server multi-protocol stack and toolkit with an efficient integrated scripting engine. Includes Industrial Protocols, MQTT client, SMQ broker, WebSocket client & server, REST, AJAX, XML, and more. The Barracuda App Server is a programmable, secure, and intelligent IoT toolkit that fits a wide range of hardware options.
SharkSSL is the smallest, fastest, and best performing embedded TLS stack with optimized ciphers made by Real Time Logic. SharkSSL includes many secure IoT protocols.
SMQ lets developers quickly and inexpensively deliver world-class management functionality for their products. SMQ is an enterprise ready IoT protocol that enables easier control and management of products on a massive scale.
SharkMQTT is a super small secure MQTT client with integrated TLS stack. SharkMQTT easily fits in tiny microcontrollers.
An easy to use OPC UA stack that enables bridging of OPC-UA enabled industrial products with cloud services, IT, and HTML5 user interfaces.
Use our user programmable Edge-Controller as a tool to accelerate development of the next generation industrial edge products and to facilitate rapid IoT and IIoT development.
Learn how to use the Barracuda App Server as your On-Premises IoT Foundation.
The compact Web Server C library is included in the Barracuda App Server protocol suite but can also be used standalone.
The tiny Minnow Server enables modern web server user interfaces to be used as the graphical front end for tiny microcontrollers. Make sure to check out the reference design and the Minnow Server design guide.
Why use FTP when you can use your device as a secure network drive.
PikeHTTP is a compact and secure HTTP client C library that greatly simplifies the design of HTTP/REST style apps in C or C++.
The embedded WebSocket C library lets developers design tiny and secure IoT applications based on the WebSocket protocol.
Send alarms and other notifications from any microcontroller powered product.
The RayCrypto engine is an extremely small and fast embedded crypto library designed specifically for embedded resource-constrained devices.
Real Time Logic's SharkTrust™ service is an automatic Public Key Infrastructure (PKI) solution for products containing an Embedded Web Server.
The Modbus client enables bridging of Modbus enabled industrial products with modern IoT devices and HTML5 powered HMIs.