WebSocket
WebSocket is a mechanism for low-cost, full-duplex communication on Web, which protocol was standardized as RFC 6455.
The following diagram, quoted by Wikipedia, describe a communication using WebSocket between client and server.
"Handshake" is explained in the following two articles.
- Deep dive into WebSocket opening handshake protocol with Go
 - Learn WebSocket handshake protocol with gorilla/websocket server
 - How decided a value set in Sec-WebSocket-Key/Accept header
 
This post will focus on "Bidirectional messages"
WebSocket server
Let's learn the WebSocket bidirectional messaging protocol with a specific Go implementation, which is available on a GitHub repository.
package main
import (
    "fmt"
    "log"
    "net/http"
    "github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{}
func main() {
    p := 12345
    log.Printf("Starting websocket echo server on port %d", p)
    http.HandleFunc("/", echo)
    if err := http.ListenAndServe(fmt.Sprintf(":%d", p), nil); err != nil {
        log.Panicf("Error while starting to listen: %#v", err)
    }
}
func echo(w http.ResponseWriter, r *http.Request) {
    // Start handshaking
    c, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Printf("Upgrading error: %#v\n", err)
        return
    }
    defer c.Close()
    // End
    log.Println("Success to handshake with client")
    // Start bidirectional messages
    for {
        mt, message, err := c.ReadMessage()
        if err != nil {
            log.Printf("Reading error: %#v\n", err)
            break
        }
        log.Printf("recv: message %q", message)
        if err := c.WriteMessage(mt, message); err != nil {
            log.Printf("Writing error: %#v\n", err)
            break
        }
    }
    // End bidirectional messages
}
Open handshake
The first half of the code is the server implementation for "Handshake".
// 1. Initializes parameters for upgrading an HTTP connection to a WebSocket connection.
var upgrader = websocket.Upgrader{}
// 2. Starts WebSocket server which waits client HTTP requests on port 12345
func main() {
    p := 12345
    log.Printf("Starting websocket echo server on port %d", p)
    http.HandleFunc("/", echo)
    if err := http.ListenAndServe(fmt.Sprintf(":%d", p), nil); err != nil {
        log.Panicf("Error while starting to listen: %#v", err)
    }
}
func echo(w http.ResponseWriter, r *http.Request) {
    // 3. Start handshaking
    c, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Printf("Upgrading error: %#v\n", err)
        return
    }
    defer c.Close()
    // End
    log.Println("Success to handshake with client")
    // ...(Bidirectional messages part)
As noted in the comments in the code, to start up a WebSocket server, do the following:
- Initializes parameters for upgrading an HTTP connection to a WebSocket connection.
 - Starts WebSocket server which waits client HTTP requests on port 12345
 - Start handshaking
 
When you run this program, it will start an HTTP server on port 12345.
$ go run server/main.go
2021/12/13 11:54:22 Starting websocket echo server on port 12345
You can check if the handshake is actually working by making a curl request (but it's not perfect).
$ curl -X GET http://localhost:12345 \
-H "Connection: upgrade" \
-H "Upgrade: websocket" \
-H "Sec-Websocket-version: 13" \
-H "Sec-Websocket-Key: 08kp54j1E3z4IfuM1m75tQ==" \
-H "Host: localhost:12345" \
-H "Origin: http://localhost:12345"
When you send it, you will see that the request was accepted in stdout.
$ go run main.go
(omit)...
2021/12/13 12:07:19 Success to handshake with client
See Learn WebSocket handshake protocol with gorilla/websocket server for more information.
WebSocket connection
The key to understanding the message exchanging is the return value of this line.
c, err := upgrader.Upgrade(w, r, nil)
The variable c is a pointer of websocket.Conn struct. The Conn type represents a WebSocket connection.
When the handshake is completed, you can start exchanging messages over the established WebSocket connection.
You can receive and send messages by calling the Conn's ReadMessage and WriteMessage.
// Start bidirectional messages
for {
    mt, message, err := c.ReadMessage()
    if err != nil {
        log.Printf("Reading error: %#v\n", err)
        break
    }
    log.Printf("recv: message %q", message)
    // (omit...writing message part)
}
// End bidirectional messages
Use for loop so that the server keeps waiting for messages from the client. 
Data Framing
In the WebSocket protocol, data is transmitted using a sequence of frames. This wire format for the data transfer part is described by the ABNF (Augmented BNF for Syntax Specification). A overview of the framing is given in the following figure.
Server and client exchange data in accordance with this method.
Read messages from the peer
ReadMessage is a method to read messages from the peer which interprets data in base framing protocol.
mt, message, err := c.ReadMessage()
The implementation of ReadMessage is as follow:
func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
    var r io.Reader
    messageType, r, err = c.NextReader()
    if err != nil {
        return messageType, nil, err
    }
    p, err = ioutil.ReadAll(r)
    return messageType, p, err
}
The Conn.NextReader function reads bytes received from the peer.
messageType, r, err = c.NextReader()
Opcode
First return value is messageType which represents WebSocket opcode numbers defined in RFC 6455 - 11.8.  WebSocket Opcode Registry.
| Opcode | Meaning | 
|---|---|
| 0 | Continuation Frame | 
| 1 | Text Frame | 
| 2 | Binary Frame | 
| 8 | Connection Close Frame | 
| 9 | Ping Frame | 
| 10 | Pong Frame | 
There are two types of opcode: message for exchanging data and controlling WebSocket connection.
- For exchanging data
- Text Frame
 - Binary Frame
 
 - For controlling WebSocket connection
- Connection Close Frame
 - Ping Frame
 - Pong Frame
 - Contination Frame
 
 
The messages for exchanging data distinguishes between text and binary data messages. And, the WebSocket protocol defines four types of control messages: close, ping, pong, and continuation.
Conclusion
This article explains WebSocket bidirectional message using gorilla/websocket server implementation.
Related articles are here.


    
Top comments (0)