The WebSocket protocol, described in the specification RFC 6455 provides a way to exchange data between browser and server via a persistent connection.
Once a websocket connection is established, both client and server may send the data to each other.
WebSocket is especially great for services that require continuous data exchange, e.g. online games, real-time trading systems and so on.
A simple example
To open a websocket connection, we need to create new WebSocket using the special protocol ws in the url:
let socket = new WebSocket("ws://javascript.info");
Thereâs also encrypted wss:// protocol. Itâs like HTTPS for websockets.
wss://The wss:// protocol not only encrypted, but also more reliable.
Thatâs because ws:// data is not encrypted, visible for any intermediary. Old proxy servers do not know about WebSocket, they may see âstrangeâ headers and abort the connection.
On the other hand, wss:// is WebSocket over TLS, (same as HTTPS is HTTP over TLS), the transport security layer encrypts the data at sender and decrypts at the receiver, so it passes encrypted through proxies. They canât see whatâs inside and let it through.
Once the socket is created, we should listen to events on it. There are totally 4 events:
openâ connection established,messageâ data received,errorâ websocket error,closeâ connection closed.
â¦And if weâd like to send something, then socket.send(data) will do that.
Hereâs an example:
let socket = new WebSocket("wss://javascript.info/article/websocket/demo/hello");
socket.onopen = function(e) {
alert("[open] Connection established, send -> server");
socket.send("My name is John");
};
socket.onmessage = function(event) {
alert(`[message] Data received: ${event.data} <- server`);
};
socket.onclose = function(event) {
if (event.wasClean) {
alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
alert('[close] Connection died');
}
};
socket.onerror = function(error) {
alert(`[error] ${error.message}`);
};
For demo purposes, thereâs a small server server.js written in Node.js, for the example above, running. It responds with âhelloâ, then waits 5 seconds and closes the connection.
So youâll see events open â message â close.
Thatâs actually it, we can talk WebSocket already. Quite simple, isnât it?
Now letâs talk more in-depth.
Opening a websocket
When new WebSocket(url) is created, it starts an HTTP handshake (HTTPS for wss://).
The browser asks the server: âDo you support Websocket?â And if the server says âyesâ, then the talk continues in WebSocket protocol, which is not HTTP at all.
Hereâs an example of browser request for new WebSocket("wss://javascript.info/chat").
GET /chat
Host: javascript.info
Origin: https://javascript.info
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==
Sec-WebSocket-Version: 13
Originâ the origin of the client page. WebSocket is cross-origin by nature. There are no special headers or other limitations. Old servers are unable to handle WebSocket anyway, so there are no compabitility issues. ButOriginheader is important, as it allows the server to decide whether or not to talk WebSocket with this website.Connection: Upgradeâ signals that the client would like to change the protocol.Upgrade: websocketâ the requested protocol is âwebsocketâ.Sec-WebSocket-Keyâ a random browser-generated key for security.Sec-WebSocket-Versionâ WebSocket protocol version, 13 is the current one.
We canât use XMLHttpRequest or fetch to make this kind of HTTP-request, because JavaScript is not allowed to set these headers.
If the server agrees to switch to WebSocket, it should send code 101 response:
101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=
Here Sec-WebSocket-Accept is Sec-WebSocket-Key, recoded using a special algorithm. The browser uses it to make sure that the response corresponds to the request.
Afterwards, the data is transfered using WebSocket protocol, weâll see its structure (âframesâ) soon. And thatâs not HTTP at all.
Extensions and subprotocols
There may be additional headers Sec-WebSocket-Extensions and Sec-WebSocket-Protocol that describe extensions and subprotocols.
For instance:
-
Sec-WebSocket-Extensions: deflate-framemeans that the browser supports data compression. An extension is something related to transferring the data, not data itself. -
Sec-WebSocket-Protocol: soap, wampmeans that weâre going to transfer not just any data, but the data in SOAP or WAMP (âThe WebSocket Application Messaging Protocolâ) protocols. WebSocket subprotocols are registered in the IANA catalogue.
Sec-WebSocket-Extensions is sent by the browser automatically, with a list of possible extensions it supports.
Sec-WebSocket-Protocol depends on us: we decide what kind of data we send. The second optional parameter of new WebSocket lists subprotocols:
let socket = new WebSocket("wss://javascript.info/chat", ["soap", "wamp"]);
The server should respond with a list of protocols and extensions that it agrees to use.
For example, the request:
GET /chat
Host: javascript.info
Upgrade: websocket
Connection: Upgrade
Origin: https://javascript.info
Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: deflate-frame
Sec-WebSocket-Protocol: soap, wamp
Response:
101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=
Sec-WebSocket-Extensions: deflate-frame
Sec-WebSocket-Protocol: soap
Here the server responds that it supports the extension deflate-frame, and only SOAP of the requested subprotocols.
WebSocket data
WebSocket communication consists of âframesâ that can be sent from either side:
- âtext framesâ â contain text data that parties send to each other.
- âbinary data framesâ â contain binary data that parties send to each other.
- âping/pong framesâ are used to check the connection, sent from the server, the browser responds to these automatically.
- âconnection close frameâ and a few other service frames.
In the browser, we only care about text or binary frames.
WebSocket .send() can send either text or binary data, doesnât matter.
For sending, socket.send(body) allows strings or any binary format, including Blob, ArrayBuffer, etc. No settings required: just send it out.
Textual data always comes as string. For receiving binary data, we can choose between Blob and ArrayBuffer formats.
The socket.bufferType is "blob" by default, so binary data comes in Blobs.
Blob is a high-level binary object, it directly integrates with <a>, <img> and other tags, so thatâs a sane default. But for binary processing, to access individual data bytes, we can change it to "arraybuffer":
socket.bufferType = "arraybuffer";
socket.onmessage = (event) => {
// event.data is either a string (if text) or arraybuffer (if binary)
};
Rate limiting
Imagine, our app is generating a lot of data to send. But network connection is not that fast. The user may be on a mobile, in rural area.
We can call socket.send(data) again and again. But the data will be buffered in memory and sent out only as fast as network speed allows.
The socket.bufferedAmount property stores how many bytes are buffered at this moment, waiting to be sent over the network.
We can examine it to see whether the socket is actually available for transmission.
// every 100ms examine the socket and send more data only if no data buffered
setInterval(() => {
if (socket.bufferedAmount == 0) {
socket.send(moreData());
}
}, 100);
Connection close
Normally, when a party wants to close the connection (both browser and server have equal rights), they send a âconnection close frameâ with a numeric code and a textual reason.
The method is:
socket.close([code], [reason]);
Then the other party in close event handle can get the code and the reason, e.g.:
// one party:
socket.close(1000, "Work complete");
// another party
socket.onclose = event => {
// event.code === 1000
// event.reason === "Work complete"
// event.wasClean === true (clean close)
};
The code is not just any number, but a special WebSocket closing code.
Most common values:
1000â the default, normal closure,1006â canât set such code manually, indicates that the connection was broken (no close frame).
There are other codes like:
1001â the party is going away, e.g. server is shutting down, or a browser leaves the page,1009â the message is too big to process,1011â unexpected error on server,- â¦and so on.
Please refer to the RFC6455, §7.4.1 for the full list.
WebSocket codes are somewhat like HTTP codes, but different. In particular, any codes less than 1000 are reserved, thereâll be an error if we try to set such a code.
// in case connection is broken
socket.onclose = event => {
// event.code === 1006
// event.reason === ""
// event.wasClean === false (no closing frame)
};
Connection state
To get connection state, additionally thereâs socket.readyState property with values:
0â âCONNECTINGâ: the connection has not yet been established,1â âOPENâ: communicating,2â âCLOSINGâ: the connection is closing,3â âCLOSEDâ: the connection is closed.
Chat example
Letâs review a chat example using browser WebSocket API and Node.js WebSocket module https://github.com/websockets/ws.
HTML: thereâs a <form> to send messages and a <div> for incoming messages:
<!-- message form -->
<form name="publish">
<input type="text" name="message">
<input type="submit" value="Send">
</form>
<!-- div with messages -->
<div id="messages"></div>
JavaScript is also simple. We open a socket, then on form submission â socket.send(message), on incoming message â append it to div#messages:
let socket = new WebSocket("wss://javascript.info/article/websocket/chat/ws");
// send message from the form
document.forms.publish.onsubmit = function() {
let outgoingMessage = this.message.value;
socket.send(outgoingMessage);
return false;
};
// show message in div#messages
socket.onmessage = function(event) {
let message = event.data;
let messageElem = document.createElement('div');
messageElem.textContent = message;
document.getElementById('messages').prepend(messageElem);
}
Server-side code is a little bit beyond our scope here. Weâre using browser WebSocket API, a server may have another library.
Still it can also be pretty simple. Weâll use Node.js with https://github.com/websockets/ws module for websockets.
The algorithm will be:
- Create
clients = new Set()â a set of sockets. - For each accepted websocket,
clients.add(socket)and listen for its messages. - When a message received: iterate over clients and send it to everyone.
- When a connection is closed:
clients.delete(socket).
const ws = new require('ws');
const wss = new ws.Server({noServer: true});
const clients = new Set();
http.createServer((req, res) => {
// in real project we have additional code here to handle non-websocket requests
wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect);
});
function onSocketConnect(ws) {
clients.add(ws);
ws.on('message', function(message) {
message = message.slice(0, 50); // max message length will be 50
for(let client of clients) {
client.send(message);
}
});
ws.on('close', function() {
clients.delete(ws);
});
}
Hereâs the working example:
You can also download it (upper-right button in the iframe) and run locally. Just donât forget to install Node.js and npm install ws before running.
Summary
WebSocket is a modern way to have persistent browser-server connections.
- WebSockets donât have cross-origin limitations.
- They are well-supported in browsers.
- Can send/receive strings and binary data.
The API is simple.
Methods:
socket.send(data),socket.close([code], [reason]).
Events:
open,message,error,close.
WebSocket by itself does not include reconnection, authentication and many other high-level mechanisms. So there are client/server libraries that add them. But itâs also possible to implement these manually and integrate WebSockets with an existing site.
For integration purposes, a WebSocket server is usually running in parallel with the main server, and they share a single database. Requests to WebSocket use wss://ws.site.com, a subdomain that leads to WebSocket server, while https://site.com goes to the main HTTP-server.
Surely, other ways of integration are also possible. Many servers (such as Node.js) can support both HTTP and WebSocket protocols.
Yorumlar
<code>kullanınız, birkaç satır eklemek için ise<pre>kullanın. EÄer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)