Webocket在GO中:劫机者的观点
#编程 #教程 #go #distributedsystems

previous blog中,我们深入研究了Websocket。
在此博客中,让我们在GO中构建一个WebSocket客户端和服务器,并进行代码深度潜水。

当我们这样做时,我们还可以在上一个博客中的理解与我们编写的Golang代码之间进行相似之处。

我们将使用WebSocket库:"github.com/gorilla/websocket"£!

让我们从客户开始

为什么?因为这就是启动WebSocket请求的原因。 ðÖ

下面,客户端“拨号”ð原始服务器。观察URL从代表WebSocket协议的ws开始。

import "github.com/gorilla/websocket"

func main() {
    URL := "ws://localhost:8080/talk-to-server"

    conn, _, err := websocket.DefaultDialer.Dial(URL, nil)
}

想知道Dial()实际上是什么? - 如果您深入挖掘库,您会看到它只需发送Upgrade标头的HTTP1.1请求。
看看此代码:https://github.com/golang/net/blob/master/websocket/hybi.go#L412

现在,如果Websocket服务器接受此请求。我们很好...否则错误!

让我们看一下服务器上真正发生的事情

import (
    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
    "net/http"
)

func main() {
    fmt.Println("Starting WebSocket Server...")
    httpServer := gin.Default()

    httpServer.GET("/talk-to-server", handleWebsocket)

    err := http.ListenAndServe(":8080", httpServer)
    if err != nil {...}
}

对于初学者,我们刚刚在port 8080上初始化了HTTP服务器。
到目前为止什么都没有。
接下来,我们将端点/talk-to-server注册到我们的HTTP服务器

我们将使此端点能够升级到WebSocket。

gorilla/websocket库提供了带有Upgrade() func
Upgrader界面

func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) 

让我们看一下如何在handleWebSocket函数中使用上述内容:

import (
    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
    "net/http"
)
func main() {
    ...
    httpServer.GET("/talk-to-server", handleWebsocket)
    ...
}

func handleWebSocket(ginContext *gin.Context) {

    upgrader := websocket.Upgrader{}

    //Upgrader upgrades the HTTP connection to WebSocket

    websocketConn, err := upgrader.Upgrade(
        ginContext.Writer,
        context.Request,
        nil)

    if err != nil {...}
}

Upgrade() func带有3个参数:

ðhttp.ResponseWriter:那是升级连接的主要演员。等等,您很快就会知道
ð*http.Request:为了阅读或验证WebSocket客户端发送的所有标题 - 有助于对是否升级进行判决。
ðhttp.Header:要在WebSocket下设置自定义子协议(仅当成功升级到WebSocket时,仅设置HTTP连接)。我们将其设置为零以简单。

如果满足了所有验证,则Upgrade()函数返回WebSocket连接实例。是的! ð»

一旦我们知道它是有效的,服务器就需要使用握手响应回复。

如果这是正常的HTTP连接,我们本可以使用http.ResponseWriter回复。
但是,我们可以在这里使用它,因为一旦发送响应,它将关闭基础的TCP连接。 -

让劫机开始!

Angry cat saying "Hijacked !"

http.Hijacker是一个具有Hijack()函数的接口,它返回了TCP连接的基础。

库代码看起来像:

func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {    
    ...
    h, ok := w.(http.Hijacker)      //Typecasting the http.ResponseWriter to http.Hijacker
    if !ok {...}

    var brw *bufio.ReadWriter
    netConn, brw, err := h.Hijack()     //Hijacked !!!    
    if err != nil {...}
    ...
}

netConn本质上是我们的原始TCP连接。

这允许我们:

1.直接在RAW TCP连接上写入。

由于它是我们正在升级到的Websocket,因此服务器需要编写一些标题,例如
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

2.随意管理和关闭连接,这是我们对Websocket所需的。 -

作为图书馆的消费者,一旦您掌握了WebSocket连接...VOILã!您可以读或写消息!

//reading message...
_, message, err := websocketConn.ReadMessage()
if err != nil {...}
fmt.Println("Message: ",  string(message))

//writing message...
err = websocketConn.WriteMessage(websocket.TextMessage, []byte("Hello from server!"))
if err != nil {...}

同样,客户端也可以使用类似的API在WebSocket连接上写入/读取。

最后,您的代码看起来像:

客户端:

package main

import (
    "fmt"
    "github.com/gorilla/websocket"
)

func main() {
    url := "ws://localhost:8080/talk-to-server"

    conn, _, err := websocket.DefaultDialer.Dial(url, nil)
    if err != nil {...}

    //Sending message...
    err := conn.WriteMessage(websocket.TextMessage, []byte("Hello from client!\n"));
    if err != nil {...}

    //Reading message...
    _, message, err := conn.ReadMessage()
    if err != nil {...}

    fmt.Print("Received:", string(message))
}

服务器:

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
    "net/http"
)

func main() {
    httpServer := gin.Default()

    upgrader := websocket.Upgrader{}

    httpServer.GET("/talk-to-server", func(context *gin.Context) {
        websocketConn, err := upgrader.Upgrade(context.Writer, context.Request, nil)
        if err != nil {...}

        _, message, err := websocketConn.ReadMessage()
        if err != nil {...}
        fmt.Println("Message: ", string(message))

        err = websocketConn.WriteMessage(websocket.TextMessage, []byte("Hello from server!\n"))
        if err != nil {...}
    })

    err := http.ListenAndServe(":8080", httpServer)
    if err != nil {...}
}

让我们来运行吧!

首先提出Websocket服务器:

Started WebSocket server...that is listening on port 8080.

现在让我们使用Dial()

启动客户端的Websocket请求

Started client...that dialed to the server

服务器收到客户端的请求后,它会接受并升级到WebSocket连接。建立连接后,我们看到客户端向其发送消息,我们看到:

Server upgraded the request from client...Log displays the message sent from client.

服务器带有消息回复客户端:

Log displays the message sent from server.

希望此博客可以帮助您在Golang构建Websocket客户端和服务器。
在评论中让我知道您是否有任何疑问或反馈! 快乐的编码!ð©âð»