Project screenshots



Introduction to the

In this course, we will learn to use WebSocket to build a real-time chat system. We will learn from the following aspects:

  1. What is websocket?
  2. What is the difference between Websocket and the traditional HTTP protocol?
  3. The advantages of Websocket;
  4. How to establish a connection;
  5. How to maintain the connection;
  6. Golang Actual combat project — real-time chat system;
  7. Conclusion;

What is websocket?

WebSocket protocol is a new network protocol based on TCP. It enables full-duplex communication between the browser and the server — allowing the server to actively send messages to the client.

WebSocket communication protocol was established as standard RFC 6455 by IETF in 2011, and is supplemented by RFC7936.

The WebSocket protocol supports full-duplex communication between clients running untrusted code in a controlled environment and remote hosts that opt in to communicate with that code. The security model used for this is the original security pattern commonly used by Web browsers. The protocol consists of an open handshake and subsequent message frames on the TCP layer. The goal of this technology is to provide a communication mechanism for browser-based applications that require two-way communication with the server (the server cannot rely on opening multiple HTTP connections (for example, using XMLHttpRequest or IFrame and long polling).

What is the difference between Websocket and the traditional HTTP protocol?

HTTP and Websocket are application layer protocols. They specify how to encapsulate data, and their transmission channels are provided by the lower layer. This means that both HTTP requests and WebSocket requests use connections provided by the transport layer, namely TCP connections (transport layer and UDP connections). It’s just that the HTTP1.0 protocol says that when you get a response for a request, you close the connection. So you can’t send HTTP requests that stay connected (they can stay connected for quite a while if the server doesn’t return, but they can also timeout). The WebSocket protocol says we can’t break the connection until the handshake is complete. Although the WebSocket handshake uses HTTP requests, there are special fields in the request header and response header. When the browser or server receives the request, the corresponding protocol will be converted. So there is a fundamental difference between a long connection where HTTP requests are held and not returned and a WebSocket connection.

What are the advantages of WebSocket?

In terms of advantages, the comparison here is HTTP, which in a nutshell supports two-way communication, is more flexible, more efficient, and has better scalability.

  1. Support two-way communication, more real-time.
  2. Better binary support.
  3. Less control overhead. After the connection is created, when the WS client and server exchange data, the packet header controlled by the protocol is small. Without the header, the client-to-client header is only 2 to 10 bytes (depending on the packet length), with an additional 4-byte mask for client-to-server. HTTP requires a complete header for each communication.
  4. Support for extensions. The WS protocol defines extensions, and users can extend the protocol or implement custom subprotocols. (such as support for custom compression algorithms)

For the latter two points, students who have not studied the WebSocket protocol specification may not understand it intuitively, but it does not affect the WebSocket

How do I establish a connection?

The client negotiates the upgrade protocol with the WebSocket server through HTTP requests. After the protocol upgrade, the subsequent data exchange follows the WebSocket protocol.

1. On the client, apply for a protocol upgrade

First, the client initiates a protocol upgrade request. As you can see, the standard HTTP packet format is adopted and only the GET method is supported.

GET/HTTP/1.1 Host: localhost:8080 Origin: http://127.0.0.1:3000 Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==Copy the code

The significance of the key request header is as follows:

  • Connection: Upgrade: indicates that the protocol is to be upgraded
  • Upgrade: websocket: indicates that the webSocket protocol is to be upgraded.
  • Sec-websocket-version: 13: indicates the WebSocket Version. If the server does not support the version, you need to return an sec-websocket-versionheader containing the version number supported by the server.
  • Sec-websocket-key: is compatible with the sec-websocket-accept at the beginning of the server response, providing basic protection, such as malicious or unintentional connections.

2. Server: Response protocol upgrade

The status code 101 indicates protocol switchover. The protocol upgrade is completed here, and subsequent data interaction is based on the new protocol.

HTTP/1.1 101 Switching Protocols Connection:Upgrade Upgrade: websocket Sec- websocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=Copy the code

3. Sec-websocket-accept calculation

Sec-websocket-accept Is calculated based on the sec-websocket-key in the header of the client request.

The calculation formula is:

  1. willSec-WebSocket-Keywith258EAFA5-E914-47DA-95CA-C5AB0DC85B11Joining together.
  2. The abstract is calculated by SHA1 and converted intobase64A string.

How do I maintain the connection?

WebSocket To maintain real-time bidirectional communication between the client and server, ensure that the TCP channel between the client and server is not disconnected. However, if a connection is maintained for a long time without data exchange, the connection resources may be wasted.

However, in some scenarios, the client and server need to be connected even though no data has been exchanged for a long time. At this point, a heartbeat can be used to achieve this.

  • Sender -> Receiver:ping
  • Recipient -> Sender:pong

Ping and pong operations correspond to two control frames of WebSocket with opcode 0x9 and 0xA respectively.

For example, a WebSocket server can ping a client using the following code (using the WS module)

Golang real – time chat system

Here’s an example using an open source project on Github.

Get Golang’s WebSocket library

go get github.com/gorilla/websocketCopy the code

Get test program

git clone https://github.com/scotch-io/go-realtime-chat.gitCopy the code

Server

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

var clients = make(map[*websocket.Conn]bool) // connected clients
var broadcast = make(chan Message)           // broadcast channel

// Configure the upgrader
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}

// Define our message object
type Message struct {
    Email    string `json:"email"`
    Username string `json:"username"`
    Message  string `json:"message"`
}

func main() {
    // Create a simple file server
    fs := http.FileServer(http.Dir(".. /public"))
    http.Handle("/", fs)

    // Configure websocket route
    http.HandleFunc("/ws", handleConnections)

    // Start listening for incoming chat messages
    go handleMessages()

    // Start the server on localhost port 8000 and log any errors
    log.Println("http server started on :8000")
    err := http.ListenAndServe(": 8000", nil)
    iferr ! = nil { log.Fatal("ListenAndServe: ", err)
    }
}

func handleConnections(w http.ResponseWriter, r *http.Request) {
    // Upgrade initial GET request to a websocket
    ws, err := upgrader.Upgrade(w, r, nil)
    iferr ! = nil { log.Fatal(err) } // Make sure we close the connection when thefunction returns
    defer ws.Close()

    // Register our new client
    clients[ws] = true

    for {
        var msg Message
        // Read in a new message as JSON and map it to a Message object
        err := ws.ReadJSON(&msg)
        iferr ! = nil { log.Printf("error: %v", err)
            delete(clients, ws)
            break
        }
        // Send the newly received message to the broadcast channel
        broadcast <- msg
    }
}

func handleMessages() {
    for {
        // Grab the next message from the broadcast channel
        msg := <-broadcast
        // Send it out to every client that is currently connected
        for client := range clients {
            err := client.WriteJSON(msg)
            iferr ! = nil { log.Printf("error: %v", err)
                client.Close()
                delete(clients, client)
            }
        }
    }
}Copy the code

The client

new Vue({
    el: '#app',

    data: {
        ws: null, // Our websocket
        newMsg: ' ', // Holds new messages to be sent to the server
        chatContent: ' ', // A running list of chat messages displayed on the screen
        email: null, // Email address used for grabbing an avatar
        username: null, // Our username
        joined: false // True if email and username have been filled in
    },

    created: function() {
        var self = this;
        this.ws = new WebSocket('ws://' + window.location.host + '/ws');
        this.ws.addEventListener('message'.function(e) {
            var msg = JSON.parse(e.data);
            self.chatContent += '<div class="chip">'
                    + '<img src="' + self.gravatarURL(msg.email) + '" >' // Avatar
                    + msg.username
                + '</div>'
                + emojione.toImage(msg.message) + '<br/>'; // Parse emojis

            var element = document.getElementById('chat-messages');
            element.scrollTop = element.scrollHeight; // Auto scroll to the bottom
        });
    },

    methods: {
        send: function () {
            if(this.newMsg ! =' ') {
                this.ws.send(
                    JSON.stringify({
                        email: this.email,
                        username: this.username,
                        message: $('<p>').html(this.newMsg).text() // Strip out html
                    }
                ));
                this.newMsg = ' '; // Reset newMsg
            }
        },

        join: function () {
            if(! this.email) { Materialize.toast('You must enter an email', 2000);
                return
            }
            if(! this.username) { Materialize.toast('You must choose a username', 2000);
                return
            }
            this.email = $('<p>').html(this.email).text();
            this.username = $('<p>').html(this.username).text();
            this.joined = true;
        },

        gravatarURL: function(email) {
            return 'http://www.gravatar.com/avatar/'+ CryptoJS.MD5(email); }}});Copy the code

conclusion

The specific technology to be used depends on the use scenario. Here I summarize the different use scenarios of HTTP and Websocket for your reference.

HTTP :

  • Retrieve Resource
  • Highly Cacheable Resource
  • Idempotency and Safety
  • Error Scenarios

websockt

  • Fast Reaction Time
  • Ongoing Updates
  • Ad-hoc Messaging

Incorrect HTTP application scenario

  • Rely on client-side polling services rather than user-initiated.
  • Frequent service calls are required to send small messages.
  • Clients need to respond quickly to changes to resources, and there is no way to predict when changes will occur.

Incorrect WebSockets application scenario

  • Connections are used for very few events or very short periods of time, and the client does not need to respond quickly to events.
  • Need to open multiple WebSockets to the same service at once.
  • Open the WebSocket, send the message, then close it – and then repeat the process.
  • Re-implement the request/response pattern in the messaging layer.
The above content is my own some feelings, share out welcome correction, incidentally beg a wave of attention, have a question or better idea of small partners can comment on the private letter I oh ~ or
Click on theJava Learning Exchange GroupTalk to each other!


             

Author: DailyProgrammer


Link: https://zhuanlan.zhihu.com/p/81694216


Source: Zhihu