[Computer Network] 7. Websocket concept, sdk, implementation

insert image description here

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

insert image description here
insert image description here

Its features include:

  • Based on the TCP protocol, the server-side implementation is relatively easy.
    insert image description here

  • 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
insert image description here

Then send data and receive data at the same time:

insert image description here

The actual request is as follows:
insert image description here

The server-side print log is as follows:
insert image description here

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:

insert image description here

insert image description here

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

Guess you like

Origin blog.csdn.net/jiaoyangwm/article/details/130760176
Recommended