HTML5 Tutorial – WebSocket Client

imageWebSocket, introduced of as part of HTML5, defines a full-duplex single socket connection over which messages can be sent between client and server.

WebSocket is not just another enhancement to HTTP. The WebSocket standard simplifies much of the complexity around bi-directional web communication and connection management.

In fact, it reduces a lot of unnecessary network traffic and provides such a dramatic improvement that it makes all the old Comet and Ajaz polling, long-polling, and streaming solutions obsolete.

It scales.

Implementing this new data interchange technology is simple if you follow these steps:

1. Use a client browser that implements the WebSocket protocol.

2. Write code in a webpage that creates a client WebSocket .

3. Write code on a web server that responds to a client request through a WebSocket .

In this post, I’ll explore when to use WebSocket and how to write a webpage or Windows 8 client that uses a client WebSocket. In the next post, I’ll show how to build a web server.

btw. It looks harder than it is.

When to Use WebSockets

In this section, you learn about advantages, disadvantages, and key scenarios for using WebSockets.

WebSocket Advantages

HTML5 WebSockets provide an enormous reduction in unnecessary network traffic and latency compared to the unscalable polling and long-polling solutions that were used to simulate a full-duplex connection by maintaining two connections.

WebSockets provides your applications the ability to traverse firewalls and proxies. Communications are done over TCP port number 80, which is of benefit for those environments which block non-standard Internet connections using a firewall.

WebSockets is designed to be implemented in web browsers and web servers, but it can be used by any client or server application.

The WebSocket Protocol is an independent TCP-based protocol. Its only relationship to HTTP is that its handshake is interpreted by HTTP servers as an Upgrade request.

image

In addition once the initial handshake is completed, you can see an order of magnitude reduction, just in the headers being used to transport data. In the case of a 870 byte header in HTTP verses a 20 byte header in WebSocket:

 

Number of Clients 1000 clients polling each second 1000 Websocket clients receiving one message each second
1000 6.6 Mbps .015 Mbps
10,000 66 Mbps .153 Mbps
100,000 665 Mbps 1.526 Mbps
Why Not Polling or Long Polling or Streaming?

Current attempts to provide real-time web applications largely revolve around polling and other server-side push technologies, the most notable of which is Comet, which delays the completion of an HTTP response to deliver messages to the client. Comet-based push is generally implemented in JavaScript and uses connection strategies such as long-polling or streaming.

With polling, the browser sends HTTP requests at regular intervals and immediately receives a response. Real-time data is often not that predictable, making unnecessary requests inevitable and as a result, many connections are opened and closed needlessly in low-message-rate situations.

image

With long-polling, the browser sends a request to the server and the server keeps the request open for a set period. If a notification is received within that period, a response containing the message is sent to the client. If a notification is not received within the set time period, the server sends a response to terminate the open request.

image

With streaming, the browser sends a complete request, but the server sends and maintains an open response that is continuously updated and kept open indefinitely (or for a set period of time). The response is then updated whenever a message is ready to be sent, but the server never signals to complete the response, thus keeping the connection open to deliver future messages. However, since streaming is still encapsulated in HTTP, intervening firewalls and proxy servers may choose to buffer the response, increasing the latency of the message delivery.

image

WebSocket Disadvantages

WebSocket is implement only in the latest browsers. For Microsoft fans, IE10 and Windows 8 do support WebSocket.

That said, all the latest browsers except Android browser do support the latest specification (RFC 6455) of the WebSocket protocol.

AutobahnTestsuite provides a list of browsers and servers that support WebSocket. You can also see the current state of browser support at the caniuse.com website.

WebSocket Scenarios

You can imagine several scenarios for WebSockets. Anytime you want real-time communication from a server to a client application. Most of the examples involve chat scenarios with a server because they move a string in real time. But it is possible to implement chat-scenarios from browser to browser in the real time.

In other words, use sockets anytime a user would otherwise have to refresh a web page to see new data. Or use WebSocket to replace Ajax long polling to retrieve new data.

You can move more complex information, such as near stock quotes or news feeds or real time gaming that need to scale with latencies under 500 milliseconds.

While chat is often used as an example, you can do a whole lot more.

Workarounds and Alternatives

You can use Modernizr polyfills that assist older browsers fall back to Comet or CORS, or long-polling.

SignalR also provides a very simple, high-level API for doing server to client RPC (call JavaScript functions in your clients’ browsers from server-side .NET code) in your ASP.NET application, as well as adding useful hooks for connection management, e.g. connect/disconnect events, grouping connections, authorization. See Asynchronous scalable web applications with real-time persistent long-running connections with SignalR. SignalR supports WebSockets, Server-sent events, forever frames, and long-polling.

Comet is an umbrella term, encompassing multiple techniques. Comet is known by several other names, including Ajax Push, Reverse Ajax, Two-way-web, HTTP Streaming, and HTTP server push among others.

How WebSocket Works

To establish a WebSocket connection, the client and server upgrade from the HTTP protocol to the WebSocket protocol during their initial handshake.

Once the handshake is complete, HTTP is completely out of the picture at this point. Using the lightweight WebSocket wire protocol, messages can now be sent or received by either endpoint at any time.

Your application can opens a connection, sends and receives messages, and then closes the WebSocket connection. You can do this through the APIs.

The Handshake

HTTP servers can share their default HTTP and HTTPS ports (80 and 443) with a WebSocket gateway or server. WebSocket protocol client implementations try to detect if the user agent is configured to use a proxy when connecting to destination host and port. Then, uses HTTP CONNECT method to set up a persistent tunnel.

First, the HTTP request.

Diagram illustrating an HTTP GET Upgrade Request from an HTTP Client to an HTTP Server.

The handshake is shown in the following example:

GET /mychat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com

I’ll show the code you use to set up this connection in the next section.

Next the server responds. I’ll show the code that responds in the next posting.

Diagram illustrating an HTTP 101 Switching Protocols Respsonse from an HTTP Server Client to an HTTP Client.

And the response.

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

The handshake resembles HTTP, but actually isn’t. It allows the server to interpret part of the handshake request as HTTP, then switch to WebSocket. Once established, WebSocket messages are sent back and forth in full duplex-mode.

This means that text messages can be sent back and forth in either direction at the same time.

Setting up the handshake and then sending and receiving messages and errors is all done through the API.

NOTE: each line needs an end of line sequence and there must be a blank line at the end.

Sec-WebSocket-Key, Sec-WebSocket-Version, Sec-WebSocket-Accept

The client sends a Sec-WebSocket-Key and Sec-WebSocket-Version that are both handled by the API.

Sec-WebSocket-Key holds the unique client context, so different clients can be distinguished. This value represents some kind of session key.

Sec-WebSocket-Version defines the particular WebSocket implementation version. WebSocket working draft provides a number of different versions in the specification.

To demonstrate that it understands the WebSocket Protocol, the server performs a standardized transformation on the Sec-WebSocket-Key from the client request and returns the results in the Sec-WebSocket-Accept header.

The client handshake established a HTTP-on-TCP connection between browser and server. After the server returns its 101 response, the application-layer protocol switches from HTTP to WebSockets which uses the previously established TCP connection.

WebSocket Messages

Using the lightweight WebSocket wire protocol, messages can now be sent or received by either endpoint at any time.

Diagram illustrating WebSocket messages being sent between two Web sockets.

A message is composed as a sequence of one or more message fragments or data “frames.”

Each frame includes information such as:

  • Frame length
  • Type of message (binary or text) in the first frame in the message
  • A flag (FIN) indicating whether this is the last frame in the message

IE10 reassembles the frames into a complete message before passing it to the script.

I’ll show how to send and receive messages in a following section.

Closing the Connection

Either endpoint (the application or the server) can initiate a closing handshake.

A special kind of frame – a close frame – is sent to the other endpoint. The close frame may contain an optional status code and reason for the close. The protocol defines a set of appropriate values for the status code. The sender of the close frame must not send further application data after the close frame.

Diagram illustrating the Close frame message and response.

When the other endpoint receives the close frame, it responds with its own close frame in response.

Security

The WebSocket protocol defines two new URI schemes which are similar to the HTTP schemes.

  • “ws:” “//” host [ “:” port ] path [ “?” query ] is modeled on the “http:” scheme. Its default port is 80. It is used for unsecure (unencrypted) connections.
  • “wss:” “//” host [ “:” port ] path [ “?” query ] is modeled on the “https:” scheme. Its default port is 443. It is used for secure connections tunneled over Transport Layer Security.

When proxies or network intermediaries are present, there is a higher probability that secure connections will be successful, as intermediaries are less inclined to attempt to transform secure traffic.

Client Code – Using WebSocket API

Let’s look at a simple example and break down each of the steps in connecting your client to a WebSocket server. This example opens a WebSocket, sends a string to the WebSocket.org server, that string is returned and written to the screen, and the connection is closed.

Client Example

Minor adaptions from Websocket.org echo example.


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script type="text/javascript">
var wsUri = "ws://echo.websocket.org/";
var output;
var socket;
function init() {
output = document.getElementById("output");
if (window.WebSocket) {
// WebSocket supported
writeToScreen("WebSocket supported");
testWebSocket();
} else {
// WebSocket not supported
writeToScreen("WebSocket not supported");
}
}
function testWebSocket() {
socket = new WebSocket(wsUri);
socket.onopen = function (evt) { onOpen(evt) };
socket.onclose = function (evt) { onClose(evt) };
socket.onmessage = function (evt) { onMessage(evt) };
socket.onerror = function (evt) { onError(evt) };
}
function onOpen(evt) {
writeToScreen("CONNECTED");
doSend("WebSocket is on the air");
}
function onClose(evt) {
writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data.toString() + '</span>');
socket.close();
}
function onError(evt) {
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function doSend(message) {
if (socket.readyState != WebSocket.OPEN)
return;
socket.send(message);
writeToScreen("SENT: " + message);
}
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
// should probably use jQuery.ready here
if (window.addEventListener) {
// for W3C DOM
window.addEventListener("load", init, false);
} else {
// legacy IE
window.attachEvent("onload", init);
}
</script>
</head>
<body>
<h2>WebSocket Test</h2>
<div id="output"></div>
</body>
</html>

The result is:

image

Check for WebSocket Support

You can check for WebSocket support in your browser by checking for the existence of WebSocket in the window object.


function onload() {
if (window.WebSocket) {
// WebSocket supported
} else {
// WebSocket not supported
}
}

Create and Connect to a WebSocket Server

To connect with an end-point, you create a new WebSocket instance. Provide the URL that represents the end-point you want to connect with. You use ws:// and wss:// prefixes to indicate an unsecured WebSocket or secured WebSocket.


var wsUri = "ws://echo.websocket.org/";
var webSocket = = new WebSocket(wsUri);

Note: You may see port numbers as part of wsUri. WebSocket is designed to use port 80 and 443. You may see other port numbers that have been assigned for convenience or because the demo is running locally in a development environment.

Add Event Listeners

Because WebSockets performs an asynchronous programming model, once you open a socket, you then wait for events. You add callback functions to listen for events.


websocket.onopen = function (evt) { onOpen(evt) };
websocket.onclose = function (evt) { onClose(evt) };
websocket.onmessage = function (evt) { onMessage(evt) };
websocket.onerror = function (evt) { onError(evt) };

view raw

gistfile1.js

hosted with ❤ by GitHub

Send a Message

You send a message to Websocket server as UTF-8 text, ArrayBuffers, or Blobs. In the example, we sent text in the doSend function.

websocket.send(message);

But you will probably want to test that the connection is still open. So this snippet verifies that the WebSocket is open before sending.


function doSend(message) {
if (websocket.readyState != WebSocket.OPEN)
return;
websocket.send(message);
}

Messages are often text, including JSON. But you can also send and receive binary Blog or an ArrayBuffer depending on the value of WebSocket binaryType property.

For example, you can send an array


var a = new Uint8Array([8,6,7,3,5,9.0]);
websocket.send(a.buffer);

Or a binary message, such as this sample code that takes a canvas and send it to your WebSocket.


function sendBinaryMessage() {
if (websocket.readyState != WebSocket.OPEN)
return;
var sourceCanvas = document.getElementById('source');
// msToBlob returns a blob object from a canvas image or drawing
websocket.send(sourceCanvas.toBlob());
// …
}

The Canvas toBlog function is now a part of the HTML Canvas specification. In IE and Windows 8, it is implemented as msToBlog. Other browsers are implementing.

In more advanced uses, you will want to measure how much data is backed up in the outgoing buffer before calling send. The buggered amount represents the number of bytes that you have sent on the socket, but have not yet been sent on the network.

<


if (websocket.bufferedAmount < bufferThreshold) {
websocket.send(message);
}

Receive a Message

You register a handler to the WebSocket’s onmessage event. The event handler receives a MessageEvent which contains the data in MessageEvent.data. Data can be received as text or binary messages.

When a binary message is received, the WebSocket.binaryType attribute controls whether the message data is returned as a Blob or an ArrayBuffer datatype. The attribute can be set to either “blob” or “arraybuffer.”

In the example, text is displayed.


function onMessage(evt) {
writeToScreen( evt.data );
}

But if you might receive multiple data types, you could check your incoming data like this:


function onMessage(evt) {
if (evt.data instanceof Blob) {
// do something with evt.data
} else {
document.getElementById("textresponse").value = evt.data;
}
};

Close WebSocket

Similar to the opening handshake, there is a closing handshake. Either endpoint (the application or the server) can initiate this handshake.

A special kind of frame – a close frame – is sent to the other endpoint. The close frame may contain an optional status code and reason for the close. The protocol defines a set of appropriate values for the status code. The sender of the close frame must not send further application data after the close frame.

In the example code, we closed the connection immediately after sending the message.

socket.close();

Then we updated the web page after receiving the close event.


function onClose(evt) {
writeToScreen("DISCONNECTED");
}

WebSocket API Summary

The WebSocket API is simple and uncomplicated, requiring very little code. You can easily take advantage of low-latency bidirectional data interchanges that will help you create faster online games, instant social network notifications, real-time displays of stock and weather information, and other timely data. You can send and receive text and binary data.

The WebSocket API is documents at the W3C. There’s also documentation on MSDN for WebSockets (Windows).

Debugging

There are several ways to debug the WebSocket connection.

Fiddler is a popular HTTP debugging proxy. There is some support in the latest versions for the WebSocket protocol. You can inspect the headers exchanged in the WebSocket handshake:

Screen shot of Fiddler showing a WebSocket request.

WebSocket messages are also logged in Fiddler.

References

Also these videos are sessions from the Microsoft //Build/ conference from September 2011:

For information about how to connected WebSockets to your Windows Store Apps, see Connecting with WebSockets (Windows Store apps using JavaScript and HTML) (Windows). This article points to hands on articles using the Windows 8 SDK:

Sample Code

Sample code is available in the DevDays GitHub repository. See https://github.com/devdays/html5-tutorials