The emergence of the websocket

In order to implement push technology, many websites use polling technology. Polling is when the browser makes an HTTP request to the server at a specific time interval (e.g., every 1 second), and the server returns the latest data to the client’s browser. This traditional pattern has obvious disadvantages, namely, the browser needs to make continuous requests to the server. However, HTTP requests may contain long headers, in which only a small portion of the data is really valid, which obviously wastes a lot of bandwidth and other resources.

WebSocket protocol was born in 2008 and became an international standard in 2011. All browsers already support it. It can better save server resources and bandwidth, and can be more real-time communication.

The websocket protocol

Websocket is actually a new protocol that has nothing to do with HTTP, but is compatible with the existing browser handshake specification. That is to say, it is a supplement to HTTP. Websocket borrows HTTP protocol to complete part of the handshake
GET the ws: / / localhost: 3000 / ws/chat HTTP / 1.1 Host: localhost Upgrade: websocket Connection: Upgrade Origin: http://localhost:3000 Sec-WebSocket-Key: client-random-string Sec-WebSocket-Version: 13Copy the code

This request differs from a normal HTTP request in several ways:

  • GET does not request an address like /path/, but an address that begins with ws://;
  • Request headers Upgrade: webSocket and Connection: Upgrade indicate that this Connection will be converted to a WebSocket Connection;
  • Sec-websocket-key is used to identify the connection, not to encrypt data;
  • Sec-websocket-version Specifies the WebSocket protocol Version.
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade sec-websocket-accept: server-random-stringCopy the code

This response code 101 indicates that the HTTP protocol for this connection is about to be changed, and the changed protocol is Upgrade: webSocket specified webSocket protocol.

So the handshake is done and the connection doesn’t break, and the browser and the server can use this connection to send messages to each other. However, the connection is upgraded to a WebSocket instead of an HTTP connection. Browsers and servers do not send HTTP requests to each other).

It has good compatibility with HTTP protocol. The default ports are also 80 and 443, and the handshake phase uses HTTP protocol, so it is not easy to mask the handshake and can pass various HTTP proxy servers.

Differences between WebSocket and Http

Different ways of communication

WebSocket is a two-way communication mode, only in the handshake phase between the client and server is the use of HTTP protocol “request-response” mode interaction, and once the connection is established after the use of two-way communication, both the client and the server can send data to each other at any time; HTTP protocol, on the other hand, uses the request-response mode to communicate from beginning to end. Because of this, the communication efficiency of HTTP is not as high as WebSocket. Data transfer and request

In traditional Web applications, data interaction between the browser and the server usually goes through the following steps:

  1. The DNS query
  2. TCP three-way handshake
  3. Sends HTTP request headers
  4. Pass the HTTP request body (if any)
  5. The server processes and sends the response header
  6. The server transmits the response body
  7. Disconnecting TCP Connections

Interaction in WebSocket usually involves the following steps:

  1. The DNS query
  2. TCP three-way handshake
  3. The WebSocket handshake
  4. Browser sends request
  5. The server sends a response
  6. Disconnecting TCP Connections

It can be seen from the above that if there is only one communication, the difference is not very big, and WebSocket even has one more handshake than normal. But the advantages of Websockets come to light when data needs to be exchanged frequently.

For example, when there are 10 data interactions, the former establishes 10 TCP connections (HTTP 1.0 requires 10 connections,HTTP 1.1 can reuse TCP connections through keep-alive connections), and then sends 10 request headers (including cookies and other information, which may reach K level). The received response information may be only a few bytes (for example, some heartbeat packets), which will greatly waste bandwidth and other resources.

Imagine if you are making a chat application and want to get the number of people currently online, you need to send all your cookies (at least a few hundred bytes) to the server, in addition to the HTTP header containing other information, such as URL, host, etc., which is essential. In the end, the server returned hundreds of bytes, but less than 10 of them were really needed (just how many people were online, other information was useless).

Through WebSocket, the browser can send a request of 1 to 2 bytes to the server (there is no need to bring cookies for authentication, authentication can be performed during handshake, once a TCP connection is established, the communication on the connection is authenticated data, which is also one of its advantages: This request contains only a specific control code (specified by the application layer protocol implemented by the developer), the server only needs to return the specific return code and data, all useless bytes are omitted.

Different protocol formats

  • The HTTP protocol is bulky, while the WebSocket protocol is lightweight.
  • For HTTP, a packet is a complete message; The minimum unit of communication between WebSocket client and server is frame, which consists of one or more frames to form a complete message. That is, the sender cuts the message into multiple frames and sends it to the server. The server receives the message frame and reassembles the associated frame into a complete message.
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 ... | +---------------------------------------------------------------+Copy the code

Advantages and application scenarios of WebSocket

WebSocket is a protocol for full-duplex communication over a TCP connection. It works on ports 80 and 443 by default, as well as HTTP, and supports HTTP proxy and middleware. Therefore, WebSocket can adapt to HTTP. The working mechanism of WebSocket is to establish a connection through HTTP protocol, and then use WebSocket protocol for data communication

Websocket advantages

Full duplex communication

In traditional Web applications, the browser-server interaction is half duplex communication (but not exactly half duplex communication, the server cannot actively push to the browser). That is, the data flow is single at the same time. After the browser sends a request to the server, it needs to wait for the server to return the data. In WebSocket, the browser and the server can communicate at any time without waiting for the other side to complete the transmission. After the browser receives the data from the server, the onMessage event will be triggered automatically.

The real time

Traditional Web applications are difficult to communicate in real time, and usually use long connections or polling. For the server, the polling method passively transmits data. Even if the data is updated, the browser has not sent the request, so the message cannot be pushed in real time.

Better binary support.

WebSocket defines binary frames, making it easier to process binary content than HTTP.

Extensions can be supported.

WebSocket defines extensions that users can extend and implement partially customized sub-protocols.

There are no same-origin restrictions, and clients can communicate with any server.

Websocket application scenarios

1. High real-time requirements for data transmission

Web chat room, post bar live post, Weibo topic wall, thematic discussion, real-time network attack display (applied), etc

2. Push apps

Website message notification, mailbox new mail reminder

3. Monitor the online status and accurately count the online duration

Collecting User Behavior Statistics

4. Remote debugging code, cloud command system

Some mobile debugging tools are developed based on WebSocket (here the WebSocket protocol, not the WebSocket API)

Websocket function description

WebSocket() constructor

grammar

var aWebSocket = new WebSocket(url [, protocols]);
Copy the code

Url: the URL to connect to; This should be the URL that the WebSocket server will respond to.

/ / the server URL is the URL ws://example.com: 80 / some/path wss://example.com: 80 / some/path / / encryptionCopy the code

Protocols: A protocol string or an array containing protocol strings. These strings are used to specify subprotocols so that a single server can implement multiple WebSocket subprotocols (for example, you might want one server to be able to handle different types of interactions based on the protocol specified). If no protocol string is specified, an empty string is assumed.

WebSocket properties

Websocket. binaryType Returns the type of binary data transferred over the WebSocket connection.

  • Blob: Transmits bloB data.
  • Data of type ArrayBuffer is transferred.
aWebSocket.binaryType = "blob"; // or aWebSocket.binaryType = "arraybuffer"; // An ArrayBuffer is an array of binary dataCopy the code

Websocket.bufferedamount is a read-only property that returns the number of bytes of data that has been queued by the send() method but has not yet been sent to the network. This property value is reset to 0 once all data in the queue has been sent to the network. However, if the connection is closed during sending, the property value is not reset to 0. If you keep calling send(), the value of this property will keep growing

var bufferedAmount = aWebSocket.bufferedAmount;
Copy the code
var data = new ArrayBuffer(10000000); aWebSocket.send(data); If (aWebSocket. BufferedAmount = = = 0) {/ / sent} else {/ / send doesn't end}Copy the code

The websocket. readyState property returns the current state of the instance object, of which there are four.

  • CONNECTING: The value is 0, indicating that a connection is being established.
  • OPEN: the value is 1, indicating that the connection is successful and communication can be started.
  • CLOSING: A value of 2 indicates that the connection is CLOSING.
  • CLOSED: the value is 3, indicating that the connection is CLOSED or fails to be opened.

The websocket. onopen property defines an event handler that is called when the webSocket connection state readyState changes to 1. This means that the current connection is ready to send and receive data. This event handler is fired by an event (when a connection is established).

aWebSocket.onopen = function () { aWebSocket.send('Hello Server! '); }Copy the code

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

aWebSocket.addEventListener('open', function (event) { aWebSocket.send('Hello Server! '); });Copy the code

The websocket. onClose attribute returns an event listener that is called when the readyState of the WebSocket connection becomes CLOSED and receives a CloseEvent named “close”.

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

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

The websocket. onMessage property is an Event handler that is called when a message is received from the server. It is called by a MessageEvent.

aWebSocket.onmessage = function(event) { var data = event.data; // process data}; aWebSocket.addEventListener("message", function(event) { var data = event.data; });Copy the code

Note that the server data can be textual or binary (bloB objects or Arraybuffer objects).

aWebSocket.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"); }}Copy the code

In the websocket. onError property, you can define a callback function to execute when an error occurs. The event name of this event is “error”.

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

aWebSocket.addEventListener("error", function(event) {
  // handle error event
});
Copy the code

WebSocket method

WebSocket.close()

WebSocket.close();
Copy the code

parameter

  • Close :(optional) a numeric status code that explains why the connection is closed. If this parameter is not passed, 1005 is used by default.

  • Reason (Optional) A readable string that explains why the connection was closed. The UTF-8 encoded string cannot exceed 123 bytes.

WebSocket.send()

aWebSocket.send('your message');
Copy the code

The websocket.send () method queues the data that needs to be sent to the server over the WebSocket link and increases the value of bufferedAmount based on the size of the data bytes that need to be sent.

parameter

  • Text string. The string is added to the buffer in UTF-8 format, and bufferedAmount adds the value of the number of bytes that the string was encoded in UTF-8 format.

  • An ArrayBuffer can send the underlying binary data using a typed array object. Its binary data memory will be cached in a buffer, and bufferedAmount will add the value of the required number of bytes.

  • The Blob type transmits the raw data in the queue Blob in binary. BufferedAmount will add the value of the number of bytes of the original data.

Example of sending a Blob object.


var file = document.querySelector('input[type="file"]').files[0];
aWebSocket.send(file);
Copy the code

An example of sending an ArrayBuffer object.

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];
}
aWebSocket.send(binary.buffer);
Copy the code

Websocket heartbeat mechanism

Cause of heart reconnection

The purpose of heartbeat and reconnection is to ensure that the client and server are still alive to avoid packet loss.

The front-end disconnect

During the use of WebSocket, the network may be disconnected, for example, the signal is not good or the network is temporarily shut down. At this time, the webSocket connection has been disconnected. Different browsers have different mechanisms and trigger onclose at different times. The onclose method of websocket is not ideally executed, and we have no way of knowing whether to disconnect and therefore reconnect.

The back-end disconnect

If the backend needs to disconnect ws for some reason, under controllable circumstances, a disconnection message will be sent, and then the connection will be disconnected and we will reconnect. If the connection is disconnected due to some anomaly, we do not feel it, so if we send a heartbeat for a certain amount of time and the back end does not return a heartbeat response message and the front end does not receive any other messages, we can conclude that the back end is actively disconnected.

Therefore, a mechanism is needed to detect whether the client and server are properly connected. The connection is ensured by sending heartbeat packets at a specified interval. If the connection fails, the onCLOSE event needs to be manually triggered, and then the reconnection operation can be performed. So websocket heartbeat reconnection comes into being.

Realization of heartbeat reconnection

Create the connection with createWebSocket

function createWebSocket() { try { aWebSocket = new WebSocket(wsUrl); init(); } catch(e) { console.log('catch'); reconnect(wsUrl); }}Copy the code

Initialize some listening events and bind the reconnection method on close or error if we want the WebSocket connection to persist.

Function init() {awebsocket.onclose = function () {console.log(' link closed '); reconnect(wsUrl); }; Awebsocket. onerror = function() {console.log(' error '); reconnect(wsUrl); }; Awebsocket.onopen = function () {// Heartbeat check reset heartcheck.start (); }; Awebsocket. onMessage = function (event) {console.log(' received message '); // Heartcheck.start (); }}Copy the code

The reconnect operation, which avoids repeated connections by setting the lockReconnect variable

var lockReconnect = false; Function reconnect(url) {if(lockReconnect) {return; }; lockReconnect = true; // If no connection is made, the connection will always be reconnected. Set latency to avoid too many requests. tt = setTimeout(function () { createWebSocket(url); lockReconnect = false; }, 4000); }Copy the code

The heartbeat detection

TimeoutObj: null, serverTimeoutObj: {timeoutObj: null, serverTimeoutObj: {timeoutObj: null, serverTimeoutObj: null, start: function(){ var _this = this; var _num = this.num; this.timeoutObj && clearTimeout(this.timeoutObj); this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj); This.timeoutobj = setTimeout(function(){this.timeoutobj = setTimeout(function(){ Awebsocket. send("123456789"); _num--; If (_num === 0) {awebsocket.colse (); } }, this.timeout) } }Copy the code

How to use Websocket

Use WebSocket on the client side

Web browsers are required to use Websockets on Web pages. For details about how websockets are supported by different browser versions, see Browser compatibility.

var url = "ws://localhost:8080/websocket/text";
var aWebSocket = new WebSocket(url);
aWebSocket.onopen = function(event) {
    console.log("websocket connection open.");
    console.log(event);
};

aWebSocket.onmessage = function(event) {
    console.log("websocket message received.")
    console.log(event.data);
};

aWebSocket.onclose = function (event) {
    console.log("websocket connection close.");
    console.log(event.code);
};

aWebSocket.onerror = function(event) {
    console.log("websocket connection error.");
    console.log(event);
};
Copy the code

Use WebSocket on the server

Using WebSocket on the server requires the support of the server component. The following uses native WebSocket in Tomcat 8.5.41 (WebSocket is supported after Tomcat 7) as an example.

Since the WebSocket API is required to use WebSocket on the server, API dependency management needs to be added:

<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-websocket-api</artifactId> The < version > 8.5.41 < / version > < / dependency >Copy the code

Server:

@ServerEndpoint(value="/websocket/text") public class WebSocketTest { private static final Logger logger = LoggerFactory.getLogger(WsChatAnnotation.class); private static final AtomicInteger counter = new AtomicInteger(0); Private static final Set<WsChatAnnotation> connections = new CopyOnWriteArraySet<WsChatAnnotation>(); Private Session Session = null; // WebSocket session object private Integer Number = 0; Public WsChatAnnotation() {number = counter. IncrementAndGet (); } /** * @param session */ @onOpen public void start(session session) {logger.info("on open"); this.session = session; connections.add(this); try { session.getBasicRemote().sendText(new StringBuffer().append("Hello: ").append(number).toString()); } catch (IOException e) { e.printStackTrace(); / / @onClose public void close() {logger.info("session close"); try { this.session.close(); } catch (IOException e) { e.printStackTrace(); } finally { connections.remove(this); Public void message(String message) {logger.info("message: {}", message); for(WsChatAnnotation client : connections) { synchronized (client) { try { client.session.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); } } } } @OnError public void error(Throwable t) { logger.error("client: {} error", number, t.getMessage()); }}Copy the code

Reverse proxy WebSocket support

At present, Nginx and Haporxy already support WebSocket protocol.

The following describes how to configure the Nginx proxy Websocket protocol when Nginx is used as the reverse proxy.

# add websocket proxy location ~ /ws {proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_pass http://8080; }Copy the code