[Go WebSocket] 你的第一个Go WebSocket服务: echo server

本文介绍了如何使用Go构建一个WebSocket回显服务器。首先,对于初学者,建议快速了解Go的基础语法和常用包。然后,通过Github找到高星的`gorilla/websocket`库进行技术选型。接着,创建项目,安装依赖,并复制官方的echo服务器示例代码。在本地运行服务器后,可以使用浏览器和WebSocket连接进行交互。文章还解析了代码,解释了如何处理WebSocket连接和HTTP请求,以及如何确保收发消息的顺序性。

我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。

背景

上篇文章:《为什么我选用Go重构Python版本的WebSocket服务?》,介绍了我的目标。

从这篇文章开始,我们进入实战,正式介绍Go WebSocket框架。

还没学过Go,要先看什么?

建议你花1天时间,看一下Go的原理简介、基础语法。什么教程都可以,知名的教程就行。

至少要明白:各种数据类型,控制流(for、if等)写法,弄懂channel和goroutine,如何加锁。

一定要自己写写goroutine和channel试一下,了解一下基础语法。

此外,还要了解常用包的用法,包括fmt、net/http。

技术选型

面对自己不熟悉的语言和不熟悉的框架,该怎么做技术选型呢?

我告诉你个小技巧,直接在Github上搜索,看Star最多的那个仓库,就可以啦~

1.png

看吧,我们搜到了gorilla/websocket,star数以显著差异甩开了后面几名。这就没有什么好纠结的了,果断使用它。

新建项目

在使用GoLand时,新建Go Project会有2个选项:

2.png

我们选用第一个即可。

如果你没有GoLand,也可以手动创建文件夹,在里面新建文件go.mod(我是使用的目前最新稳定版1.18)

module echo

go 1.18

安装依赖

go get github.com/gorilla/websocket

拷贝echo代码

gorilla/websocket的官方demo拷贝过来即可,我们慢慢分析:

  • https://github.com/gorilla/websocket/blob/master/examples/echo/server.go

只需要拷贝这一个文件,命名为server.go即可。

先尝试运行

go run server.go

然后浏览器打开 localhost:8080就可以了~

3.png

  • 点击「Open」建立WebSocket连接
  • 编辑好文本,按Send发送一个消息给服务器
  • 服务器立马回复一个一模一样的消息,这就是echo
  • 点击「Close」关闭连接,之后无法Send

你的所有操作都会记录在页面上:

4.png

当然,也可以打开开发者工具,查看WebSocket连接,就像你查看Http请求那样。这篇文章教了你怎样使用Chrome的开发者面板抓包:《遇到表格,手动翻页太麻烦?我教你写脚本,一页展示所有数据》。

5.png

6.png

代码解读

引入依赖

package main

import (
   "flag"
   "html/template"
   "log"
   "net/http"

   "github.com/gorilla/websocket"
Go语言中实现WebSocket通信,可以通过标准库`net/http`或第三方库(如`gorilla/websocket`)来完成。以下是具体的实现方法: ### 使用标准库 `net/http` Go语言的标准库提供了对WebSocket的基本支持[^1]。通过`net/http`模块中的函数和类型,可以手动处理WebSocket的握手过程以及数据传输。 ```go package main import ( "fmt" "io" "net/http" ) func handleWebSocket(w http.ResponseWriter, r *http.Request) { // 检查请求头中的Upgrade字段是否为websocket if r.Header.Get("Upgrade") != "websocket" { http.Error(w, "Not a WebSocket request", http.StatusBadRequest) return } // 设置响应头 w.Header().Set("Connection", "Upgrade") w.Header().Set("Upgrade", "websocket") w.Header().Set("Sec-WebSocket-Accept", computeAcceptKey(r.Header.Get("Sec-WebSocket-Key"))) // 升级HTTP连接到WebSocket conn, bufrw, err := w.(http.Hijacker).Hijack() if err != nil { http.Error(w, "Failed to hijack connection", http.StatusInternalServerError) return } defer conn.Close() fmt.Println("Client connected.") // 读取并写入数据 for { _, msg, err := bufrw.ReadMessage() if err != nil { if err == io.EOF { fmt.Println("Client disconnected.") } else { fmt.Println("Error reading message:", err) } break } fmt.Printf("Received: %s\n", msg) if err := bufrw.WriteMessage(1, msg); err != nil { fmt.Println("Error sending message:", err) break } } } // 计算Sec-WebSocket-Accept值 func computeAcceptKey(key string) string { // 实现WebSocket握手时的密钥计算逻辑 return key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" } func main() { http.HandleFunc("/ws", handleWebSocket) fmt.Println("Starting server on :8080") err := http.ListenAndServe(":8080", nil) if err != nil { panic(err) } } ``` ### 使用 Gorilla 库 `gorilla/websocket` 是一个流行的第三方库,它简化了WebSocket服务的构建过程,隐藏了许多底层细节,使开发者能够更专注于业务逻辑[^2]。 #### 安装依赖 首先需要安装`gorilla/websocket`包: ```bash go get github.com/gorilla/websocket ``` #### 示例代码 以下是一个简单的服务器端示例,展示了如何使用该库创建WebSocket服务: ```go package main import ( "fmt" "github.com/gorilla/websocket" "log" "net/http" ) var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true // 允许跨域访问 }, } func echo(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Print("upgrade:", err) return } for { messageType, p, err := conn.ReadMessage() if err != nil { log.Println("read:", err) break } log.Printf("recv: %s", p) if err := conn.WriteMessage(messageType, p); err != nil { log.Println("write:", err) break } } } func main() { http.HandleFunc("/ws", echo) fmt.Println("Starting server on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } ``` #### 客户端示例 以下是一个客户端代码示例,用于连接上述WebSocket服务器: ```go package main import ( "fmt" "github.com/gorilla/websocket" ) var wsUrl = "ws://localhost:8080/ws" func main() { conn, _, err := websocket.DefaultDialer.Dial(wsUrl, nil) if err != nil { panic(err) } defer conn.Close() err = conn.WriteMessage(websocket.TextMessage, []byte("Hello, Server!")) if err != nil { panic(err) } _, reply, err := conn.ReadMessage() if err != nil { panic(err) } fmt.Printf("Server replied: %s\n", reply) } ``` ### 总结 - **标准库**:适用于对性能有较高要求且希望减少外部依赖的情况。 - **Gorilla**:适用于快速开发,同时希望利用成熟社区库的优势。 以上两种方法均能实现WebSocket通信,开发者可以根据具体需求选择合适的方式。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hull Qin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值