WebSocket API
I want to read about the WebSocket API because I need to either use web sockets or server sent events to implement push notifications.
References
Related
- Backpressure
- An important concept in streams is backpressure - this is the process by which a single stream or pipe chain regulates the speed of reading/writing. When a stream later in the chain is still busy and isn't yet ready to accept more chunks, it sends a signal backwards through the chain to tell earlier transform streams (or the original source) to slow down delivery so that you don't end up with a bottleneck anywhere.
- WebSocket Opening Handshake
- The server must listen for incoming socket connections using a standard TCP socket. Depending on your platform, this may be handled for you automatically. The handshake is the
Web
in WebSockets. It's the bridge from HTTP to WebSockets. In the handshake, details of the connection are negotiated, and either party can back out before completion if the terms are unfavorable. The server must be careful to understand everything the client asks for, otherwise security issues can occur.
- The server must listen for incoming socket connections using a standard TCP socket. Depending on your platform, this may be handled for you automatically. The handshake is the
- Berkeley Sockets
- A Berkeley socket is an Application programming interface (API) for Internet domain sockets and Unix domain sockets, used for inter-process communication. It is commonly implemented as a library of linkable modules. It originated with the 4.2BSD Unix operating system, which was released in 1983.
- A socket is an abstract representation (handle) for the local endpoint of a network communication path. The Berkeley sockets API represents it as a file descriptor in the Unix philosophy that provides a common interface for input and output streams of data.
Notes
The WebSocket API makes it possible to open a two-way interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive responses without having to poll the server for a reply.
The WebSocket API provides two alternative mechanisms for creating and using web socket connections: the WebSocket
interface and the WebSocketStream
interface.
- The
WebSocket
interface is stable and has good browser and server support. However, it doesn't support backpressure. As a result, when messages arrive faster than the application can process them it will either fill up the device's memory by buffering those messages, become unresponsive due to 100% CPU usage, or both. - The
WebSocketStream
interface is aPromise
-based alternative toWebSocket
. It uses the Streams API to handle receiving and sending messages, meaning that socket connections can take advantage of stream backpressure automatically, regulating the speed of reading and writing to avoid bottlenecks in the application. However,WebSocketStream
is non-standard and currently only supported in one rendering engine.
The WebTransport API
is similar to the WebSocket API, but is more low-level, versatile, and has less cross-browser support.
Interfaces
WebSocket
- The primary interface for connecting to a WebSocket sever and then sending and receiving data on the connection.
WebSocketStream
- Promise-based interface for connecting to a WebSocket server; uses streams to send and receive data on the connection.
CloseEvent
- The event sent by the WebSocket object when the connection closes.
MessageEvent
- The event sent y the WebSocket object when a message is received from the server.
Related HTTP Headers
Sec-WebSocket-Key
- An HTTP request header that contains a nonce from the client. This is used in the WebSocket opening handshake to verify that the client explicitly intends to open a WebSocket. It is added automatically by the browser.
Sec-WebSocket-Accept
- An HTTP response header used in the WebSocket opening handshake to indicate that the server is willing to upgrade to a WebSocket connection. The value in the directive is calculated from the value of
Sec-WebSocket-Key
in the corresponding request.
- An HTTP response header used in the WebSocket opening handshake to indicate that the server is willing to upgrade to a WebSocket connection. The value in the directive is calculated from the value of
Sec-WebSocket-Version
- An HTTP header that in requests indicates the version of the WebSocket protocol understood by the client. In responses, it is sent only if the requested protocol version is not supported by the server, and lists the versions that the server supports.
Sec-WebSocket-Protocol
- An HTTP header that in requests indicates the sub-protocols supported by the client in preferred order. In responses, it indicates the sub-protocol selected by the server from the client's preferences.
Sec-WebSocket-Extensions
- An HTTP header that in requests indicates the WebSocket extensions supported by the client in preferred order. IN responses, it indicates the extension selected by the server from the client's preferences.
Tools
- Node.js Popular WebSocket Libraries
- Python - Flask Popular WebSocket Library
- Go WebSocket Library
Writing WebSocket Client Applications
WebSocket client applications use the WebSocket API to communicate with WebSocket servers using the WebSocket protocol.
In order to communicate using the WebSocket protocol, you need to create a WebSocket
object; this will automatically attempt to open the connection to the server. The WebSocket constructor accepts one required and one optional parameter.
webSocket = new WebSocket(url, protocols);
url
- The URL to which to connect; this should be the URL to which the WebSocket server will respond. This should be the URL scheme
wss://
, although some software may allow you to use the insecurews://
for a local connection. Relative URL values andhttps://
andhttp://
schemes are also allowed in most recent browser versions.
- The URL to which to connect; this should be the URL to which the WebSocket server will respond. This should be the URL scheme
protocols
- Either a single protocol string or an array of protocol strings. These strings are used to indicate sub-protocols, so that a single server can implement multiple WebSocket sub-protocols.
Connection Errors
If an error occurs while attempting to connect, an error
event is first sent to the WebSocket
object (thereby invoking any handlers), followed by a close
event that indicates the reason for the connection's closing.
The browser may also output to its console a more descriptive error message as well as a closing code.
Example
// Creates a new WebSocket, connecting to the server in the
// first argument. A custom protocol is named in this example
// On return, exampleSocket.readyState is CONNECTING. The
// readyState will become OPEN once the connection is ready
// to transfer data
const exampleSocket = new WebSocket(
"wss://www.example.com/socketserver",
"protocolOne",
);
// Soecify multiple protocols
// exampleSocket.protocol will tell you which protocol of the server is
// selected
const exampleSocket = new WebSocket("wss://www.example.com/socketserver", [
"protocolOne",
"protocolTwo",
]);
Sending Data to the Server
Once you've opened you connection, you can begin transmitting data to the server. To do this, call the WebSocket
object's send()
method for each message you want to send:
exampleSocket.send("Here's some text that the server is urgently awaiting!");
You can send data as a string, Blob
or ArrayBuffer
You can make sure that sending data only takes place once a connection is established by defining an onopen
event handler to do the work:
exampleSocket.onopen = (event) => {
exampleSocket.send("Here's some text that the server is urgently awaiting!");
};
Using JSON to Transmit Objects
One handy thing you can do is use JSON to send reasonably complex data to the server.
Sending JSON Objects
// Send text to all users through the server
function sendText() {
// Construct a msg object containing the data the server needs to process the message from the chat client.
const msg = {
type: "message",
text: document.getElementById("text").value,
id: clientID,
date: Date.now(),
};
// Send the msg object as a JSON-formatted string.
exampleSocket.send(JSON.stringify(msg));
// Blank the text input element, ready to receive the next line of text from the user.
document.getElementById("text").value = "";
}
Receiving JSON Objects
exampleSocket.onmessage = (event) => {
const f = document.getElementById("chat-box").contentDocument;
let text = "";
const msg = JSON.parse(event.data);
const time = new Date(msg.date);
const timeStr = time.toLocaleTimeString();
switch (msg.type) {
case "id":
clientID = msg.id;
setUsername();
break;
case "username":
text = `User <em>${msg.name}</em> signed in at ${timeStr}<br>`;
break;
case "message":
text = `(${timeStr}) ${msg.name} : ${msg.text} <br>`;
break;
case "reject-username":
text = `Your username has been set to <em>${msg.name}</em> because the name you chose is in use.<br>`;
break;
case "user-list":
document.getElementById("user-list-box").innerText = msg.users.join("\n");
break;
}
if (text.length) {
f.write(text);
document.getElementById("chat-box").contentWindow.scrollByPages(1);
}
};
Text received over a WebSocket connection is in UTF-8 format.
Closing The Connection
When you've finished using the WebSocket connection, call the WebSocket method close()
.
exampleSocket.close();
It may be helpful to examine the socket's bufferedAmount
attribute before attempting to close the connection to determine if any data has yet to be transmitted on the network. If this value isn't 0, there's pending data still, so you may wish to wait before closing the connection.
Writing WebSocket Servers
A WebSocket server is nothing more than an application listening on any port of a TCP server that follows a specific protocol. [...] It can actually be quite straightforward to implement a basic WebSocket server on your platform of choice
A WebSocket can be written in any server-side programming language that is capable of Berkeley sockets.
WebSocket servers are often separate and specialized servers (for load-balancing or other practical reasons), so you will often use a reverse proxy (such as a regular HTTP server) to detect WebSocket handshakes, pre-process them, and send those clients to a real WebSocket server.
The WebSocket handshake
First, the server must listen for incoming socket connections using a standard TCP socket. Depending on your platform, this may be handled for you automatically. For example, let's assume that your server is listening on example.com
, port 8000, and your socket server responds to GET
requests at example.com/chat
.
The handshake is the bridge from HTTP to WebSockets. In the handshake, details of the connection are negotiated, and either party can back out before completion if the terms are unfavorable. The server must be careful to understand everything the client asks for, otherwise security issues can occur.
The client still has to start the WebSocket handshake process by contacting the server and requesting a WebSocket connection. The client will send a pretty standard HTTP request with headers that look like this:
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
The client can solicit extensions and/or subprotocls here. Some other headers may be here. If any header is not understood or has an incorrect value, the server should send a 400
response and immediately close the socket. When the server receives the handshake request, it should send back a special response that indicates that the protocol will be changing from HTTP to WebSocket.
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Once the server sends the Sec-WebSocket-Accept
header, the handshake is complete and you can start swapping data.
Your server must keep track of clients' sockets so you don't keep handshaking again with clients who have already completed the handshake.
Exchanging Data Frames
Either the client or the server can choose to send a message at any time - that's the magic of WebSockets. However, extracting information from these so-called frames
of data can be complicated.
Frame format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
pings and Pongs: The Heartbeat of WebSockets
At any point after the handshake, either the client or the server can choose to send a ping to the other party. When the ping is received, the recipient must send back a pong as soon as possible. You can use this to make sure that the client is still connected, for example.
Closing the Connection
To close a connection, either the client or server can send a control frame with data containing a specified control sequence to begin the closing handshake.
Miscellaneous
WebSocket extensions and subprotocols are negotiated via headers during the handshake. Sometimes extensions and subprotocols are very similar, but there is a clear distinction. Extensions control the WebSocket frame and modify the payload, while subprotocols structure the WebSocket payload and never modify anything. Think of an extension as compressing a file before emailing it to someone. Think of a subprotocol as a custom XML schema or a doctype declaration. You're still using XML and its syntax, but you're additionally restricted by a structure you agreed on. WebSocket subprotocols are just like that. They do not introduce anything fancy, they just establish structure.
Using WebSocketStream to Write in a Client
The WebSocketStream
API is a Promise-based alternative to WebSocket
for creating and using client-side WebSocket connections. WebSocketStream
uses the Streams API to handle receiving and sending messages, meaning that socket connections can take advantage of stream backpressure automatically, regulating the speed of reading or writing to avoid bottlenecks in the application.
if ("WebSocketStream" in self) {
// WebSocketStream is supported
}
// Create a new WebSocketStream
const wss = new WebSocketStream("wss://example.com/wss");
Comments
You have to be logged in to add a comment
User Comments
There are currently no comments for this article.