Login With Github

WebSocket Tutorial in 10 Minutes

WebSocket is a network communication protocol, which is required for many advanced features.

I'll introduce the usage of the WebSocket protocol in this article.

1. Why do we need WebSocket?

People who are new to WebSocket may ask the same question: Since we have already had the HTTP protocol, why do we need another protocol? What benefits can it bring to us?

The answer is that because the HTTP protocol has a flaw: communication only can be initiated by the client.

For example, let's say we want to know the weather today: the server will return the results of the query only if the client makes a request to the server. The HTTP protocol doesn't allow the server to push information to the client actively.

The characteristic of the one-way request is that it will be very troublesome for the client to know if a continuous state change has happened on the server. So we have to use polling: we will send a query every other time to find out if the server has new information. The most typical scene is the chat room.

Polling is inefficient and it will waste resources (you have to keep connecting, or the HTTP connection is always needed to be open). Therefore, in order to find a better way, engineers invented WebSocket.

2. Introduction

The WebSocket protocol was born in 2008 and became an international standard in 2011. All browsers have been already supported it.

The biggest feature of the protocol is that the server can push information to the client actively, and the client can also send information to the server actively. It is a true two-way equal communication and is one of the server push technologies.

Other features include:

  • (1) It's based on the TCP protocol, and the implementation on the server side is relatively easy.
  • (2) It has good compatibility with the HTTP protocol. The default ports are also 80 and 443. And it uses the HTTP protocol on the handshake phase, so it isn't easy to be shielded during the handshake phase, and can work through various HTTP proxy servers.
  • (3) The data format is relatively light, and the performance overhead is small, but the communication is efficient.
  • (4) You can send text or binary data.
  • (5) There is no homologous restriction, and the client can communicate with any server.
  • (6) The protocol identifier is ws (wss if encrypted) , and the URL is exactly the server's URL.
ws://example.com:80/some/path

3. A simple client example

The usage for the WebSocket is quite easy.

Let's look at an example of a web script. (click here to see the results)

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

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
}; 

4. The API of the client

The API for the WebSocket client is as follows.

4.1 WebSocket constructor

The WebSocket object acts as a constructor for creating a new WebSocket instance.

var ws = new WebSocket('ws://localhost:8080');

After executing the above statement, the client will connect to the server.

For the list of all the properties and methods of the instance object, see here.

4.2 webSocket.readyState

The readyState property is used to return the current state of the instance object:

  • CONNECTING: The value is 0, and it indicates that it is connecting.
  • OPEN: The value is 1, and it indicates that the connection is successful and can be used for communication.
  • CLOSING: The value is 2, and it indicates that the connection is closing.
  • CLOSED: The value is 2, and it indicates that the connection has been closed or the connection has failed.

Let's take a look at an example below.

switch (ws.readyState) {
  case WebSocket.CONNECTING:
    // do something
    break;
  case WebSocket.OPEN:
    // do something
    break;
  case WebSocket.CLOSING:
    // do something
    break;
  case WebSocket.CLOSED:
    // do something
    break;
  default:
    // this never happens
    break;
}

4.3 webSocket.onopen

The onopen property of the instance object is used to specify the callback function after the connection is successful.

ws.onopen = function () {
  ws.send('Hello Server!');
}

If you want to specify multiple callback functions, you can use the addEventListener method.

ws.addEventListener('open', function (event) {
  ws.send('Hello Server!');
});

4.4 webSocket.onclose

The onclose property of the instance object is used to specify the callback function after the connection is closed.

ws.onclose = function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
};

ws.addEventListener("close", function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
});

4.5 webSocket.onmessage

The onmessage property of the instance object is used to specify the callback function after receiving the server data.

ws.onmessage = function(event) {
  var data = event.data;
  // handle data
};

ws.addEventListener("message", function(event) {
  var data = event.data;
  // handle data
});

Note that the server data may be text or binary data (blob objects or Arraybuffer objects).

ws.onmessage = function(event){
  if(typeof event.data === String) {
    console.log("Received data string");
  }

  if(event.data instanceof ArrayBuffer){
    var buffer = event.data;
    console.log("Received arraybuffer");
  }
}

In addition to determining the type of the received data dynamically, you can also use the binaryType property to explicitly specify the type of binary data received.

// if it received blob data
ws.binaryType = "blob";
ws.onmessage = function(e) {
  console.log(e.data.size);
};

// if it received ArrayBuffer data
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
  console.log(e.data.byteLength);
};

4.6 webSocket.send()

The send() method of the instance object is used to send data to the server.

Here is an example for sending text.

ws.send('your message');

An example of sending a Blob object.

var file = document
  .querySelector('input[type="file"]')
  .files[0];
ws.send(file);

An example of sending an ArrayBuffer object.

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
  binary[i] = img.data[i];
}
ws.send(binary.buffer);

4.7 webSocket.bufferedAmount

The bufferedAmount property of the instance object tells how many bytes of binary data have not been sent out. It can be used to determine whether the transmission is over.

var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  // the transmission is over
} else {
  // the transmission have not been over
}

4.8 webSocket.onerror

The onerror property of the instance object is used to specify the callback function when the error is reported.

socket.onerror = function(event) {
  // handle error event
};

socket.addEventListener("error", function(event) {
  // handle error event
});

5. The implementation of the server

The implementation of the WebSocket server can be viewed in Wikipedia.

There are three common ways to implement Node.

You can go to their documentations for specific usages.

6. WebSocketd

Well, I'd like to recommend a great WebSocket server now: Websocketd.

The biggest features of the Websocketd are:

  • 1) there is no limit to languages in the background script.
  • 2) the stdin is the input of the WebSocket.
  • 3) the stdout is the output of the WebSocket.

For example, here is a Bash script counter.sh.

#!/bin/bash

echo 1
sleep 1

echo 2
sleep 1

echo 3

Run the script from the command line, and it will output 1, 2, and 3, with each value separated by 1 second.

$ bash ./counter.sh
1
2
3

Now let's start websocketd and specify the script as the service.

$ websocketd --port=8080 bash ./counter.sh

The above command will start a WebSocket server with a port of 8080. Whenever the client connects to the server, the counter.sh script will be executed and its output will be pushed to the client.

var ws = new WebSocket('ws://localhost:8080/');

ws.onmessage = function(event) {
  console.log(event.data);
};

The above is the client's JavaScript code, which will print 1, 2, and 3 in the console after running.

Then you can send the output of the command line to the browser easily.

$ websocketd --port=8080 ls

The above code will execute the ls command to send the contents of the current directory to the browser. It's with great ease of using the method to monitor the server in real time (here is the code).

You can refer to the official examples for more usages.

In essence, websocketd is the WebSocket proxy of the command line. As long as the program can be executed by the command line, WebSocket communication with the browser can be done through the program. Let's take a look at a Node implementation of the echo service greeter.js.

process.stdin.setEncoding('utf8');

process.stdin.on('readable', function() {
  var chunk = process.stdin.read();
  if (chunk !== null) {
    process.stdout.write('data: ' + chunk);
  }
});

The command to start the script is as follows.

$ websocketd --port=8080 node ./greeter.js

There are also examples for various other languages in the official repository.

7. Reference

0 Comment

temp