Article directory
1. Background
There is already the http protocol, why do we need the websocket protocol? It is because http is only one-way, and the client gets messages from the server. However, if the server itself has continuous state changes, it is difficult for the client to perceive. At this time, there are two solutions:
- Client rotation training: it is a waste of resources (need to open + close the connection continuously), not timely
- websocket protocol
2. Introduction
The websocket protocol was created in 2008 and became an international standard in 2011, supported by all browsers. It is one of the server push technologies
Its features include:
-
Based on the TCP protocol, the server-side implementation is relatively easy.
-
It has good compatibility with HTTP protocol. The default ports are also 80 and 443, and the handshake phase uses the HTTP protocol, so it is not easy to shield during the handshake, and can pass through various HTTP proxy servers.
- The WebSocket protocol is an independent TCP-based protocol. Its only relationship with HTTP is that the upgrade request of the handshake operation to establish a connection is based on the HTTP server.
- WebSocket uses port 80 for connection by default, while WebSocket connection based on TLS (RFC2818) is based on port 443.
-
The data format is relatively lightweight, the performance overhead is small, and the communication is efficient.
-
Either text or binary data can be sent.
-
There is no same-origin restriction, the client can communicate with any server.
-
The protocol identifier is ws (or wss if encrypted), and the server URL is the URL.
ws://example.com:80/some/path
Three, client
The following client examples can be run online here :
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.");
};
3.1 ws constructor
Executing the following will make the client connect to the server. For the API of the ws object, see
var ws = new WebSocket('ws://localhost:8080');
3.2 ws.readyState
Return the current state of the instance object, there are four types.
- CONNECTING: The value is 0, indicating that it is connecting.
- OPEN: The value is 1, indicating that the connection is successful and communication is possible.
- CLOSING: A value of 2 indicates that the connection is being closed.
- CLOSED: The value is 3, indicating that the connection has been closed, or failed to open the connection.
Here is an example:
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;
}
3.3 ws.onopen
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 addEventListener():
ws.addEventListener('open', function (event) {
ws.send('Hello Server, i am callback func1!');
});
ws.addEventListener('open', function (event) {
ws.send('Hello Server, i am callback func 2!');
});
3.4 ws.onclose
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
});
3.5 ws.onmessage
ws.onmessage = function(event) {
var data = event.data;
// 处理数据
};
ws.addEventListener("message", function(event) {
var data = event.data;
// 处理数据
});
Note that 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");
}
}
3.6 ws.send
Send data to server
// 发文本
ws.send('your message');
// 发 Blob 对象
var file = document
.querySelector('input[type="file"]')
.files[0];
ws.send(file);
// 发 ArrayBuffer 对象
// 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);
3.7 ws.bufferedAmount
Indicates how many bytes of binary data are still not sent. It can be used to judge whether the sending is over.
var data = new ArrayBuffer(10000000);
socket.send(data);
if (socket.bufferedAmount === 0) {
// 发送完毕
} else {
// 发送还没结束
}
3.8 ws.onerror
Callback function when error is reported
socket.onerror = function(event) {
// handle error event
};
socket.addEventListener("error", function(event) {
// handle error event
});
4. server
4.1 go
gorilla/websocket library documentation
package main
import (
"fmt"
"github.com/gorilla/websocket"
"log"
"net/http"
)
func main() {
fmt.Println("Hello World")
setupRoutes()
log.Fatal(http.ListenAndServe(":8080", nil))
}
func setupRoutes() {
http.HandleFunc("/", homePage)
http.HandleFunc("/ws", wsEndpoint)
}
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Home Page")
}
func wsEndpoint(w http.ResponseWriter, r *http.Request) {
// We'll need to define an Upgrader, this will require a Read and Write buffer size
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
upgrader.CheckOrigin = func(r *http.Request) bool {
return true } // 允许跨域
// upgrade this connection to a WebSocket connection
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
}
log.Println("Client Connected")
err = ws.WriteMessage(1, []byte("Hi Client!"))
if err != nil {
log.Println(err)
}
reader(ws)
}
// listen indefinitely for new messages coming through on our WebSocket connection
func reader(conn *websocket.Conn) {
for {
// read in a message
messageType, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
// print out that message for clarity
fmt.Println(string(p))
if err := conn.WriteMessage(messageType, p); err != nil {
log.Println(err)
return
}
}
}
4.1.1 apifox client
connect first
Then send data and receive data at the same time:
The actual request is as follows:
The server-side print log is as follows:
4.1.2 js client
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Go WebSocket Tutorial</title>
</head>
<body>
<h2>Hello World</h2>
<script>
let socket = new WebSocket("ws://127.0.0.1:8080/ws");
console.log("Attempting Connection...");
socket.onopen = () => {
console.log("Successfully Connected");
socket.send("Hi From the Client!")
};
socket.onclose = event => {
console.log("Socket Closed Connection: ", event);
socket.send("Client Closed!")
};
socket.onerror = error => {
console.log("Socket Error: ", error);
};
</script>
</body>
</html>
In F12, you can see the log and ws sending and receiving records:
5. Paradigm
The establishment of websocket connection is purely technical, but how to agree on the interaction between client and server is the scope of system design.
- The concept of "item provider" can be designed. It provides goods interface{}, which can be put on the shelves. Users can implement their own goods (such as apple, orange...).
- Maintain the ledger (ledger) to record the goods needed by each subscriber, and distribute them to subscribers when a message is received.
- Provide an interface for subscribing, unsubscribing, and viewing subscription content
- Provides customization of how each good is personalized
todo: source code implementation of websocket subscription design
ws go tutorial
websocket introduction
ws protocol
rfc ws protocol Chinese translation