在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连接。 -
让劫机开始!
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服务器:
现在让我们使用Dial()
服务器收到客户端的请求后,它会接受并升级到WebSocket连接。建立连接后,我们看到客户端向其发送消息,我们看到:
服务器带有消息回复客户端:
希望此博客可以帮助您在Golang构建Websocket客户端和服务器。
在评论中让我知道您是否有任何疑问或反馈! 快乐的编码!ð©âð»