对于 golang 的 web 开发, 之前写过 2 篇 blog, 分别介绍了:
- 在 gin 框架下, 各类 http api 的开发方法(包括文件上传, 下载等) golang web 方案
- gin 框架下反向代理的使用:
这里再给之前的 web 方案中加上 websocket 的部分, 基本就能涵盖日常开发所需的所有接口类型了.
golang websocket 库这里使用的 websocket 库来自 gorilla web toolkit
下面用代码来演示如何在 gin 框架中结合 websocket api
示例后端
后端提供 2 种 api, 分别支持 text 格式和 json 格式的消息 示例中的 api 每次收到消息后, 返回 10 次
1 package main 2 3 import ( 4 "log" 5 "net/http" 6 "strconv" 7 "time" 8 9 "github.com/gin-contrib/static" 10 "github.com/gin-gonic/gin" 11 "github.com/gorilla/websocket" 12 ) 13 14 var upgrader = websocket.upgrader{ 15 checkorigin: func(r *http.request) bool { 16 return true 17 }, 18 } 19 20 // websocket返回text 格式 21 func textapi(c *gin.context) { 22 //升级get请求为websocket协议 23 ws, err := upgrader.upgrade(c.writer, c.request, nil) 24 if err != nil { 25 log.println("error get connection") 26 log.fatal(err) 27 } 28 defer ws.close() 29 //读取ws中的数据 30 mt, message, err := ws.readmessage() 31 if err != nil { 32 log.println("error read message") 33 log.fatal(err) 34 } 35 36 //写入ws数据, pong 10 times 37 var count = 0 38 for { 39 count++ 40 if count > 10 { 41 break 42 } 43 44 message = []byte(string(message) + " " + strconv.itoa(count)) 45 err = ws.writemessage(mt, message) 46 if err != nil { 47 log.println("error write message: " + err.error()) 48 } 49 time.sleep(1 * time.second) 50 } 51 } 52 53 //websocket返回 json格式 54 func jsonapi(c *gin.context) { 55 //升级get请求为websocket协议 56 ws, err := upgrader.upgrade(c.writer, c.request, nil) 57 if err != nil { 58 log.println("error get connection") 59 log.fatal(err) 60 } 61 defer ws.close() 62 var data struct { 63 a string `json:"a"` 64 b int `json:"b"` 65 } 66 //读取ws中的数据 67 err = ws.readjson(&data) 68 if err != nil { 69 log.println("error read json") 70 log.fatal(err) 71 } 72 73 //写入ws数据, pong 10 times 74 var count = 0 75 for { 76 count++ 77 if count > 10 { 78 break 79 } 80 81 err = ws.writejson(struct { 82 a string `json:"a"` 83 b int `json:"b"` 84 c int `json:"c"` 85 }{ 86 a: data.a, 87 b: data.b, 88 c: count, 89 }) 90 if err != nil { 91 log.println("error write json: " + err.error()) 92 } 93 time.sleep(1 * time.second) 94 } 95 } 96 97 func websocketgin() { 98 r := gin.default() 99 r.get("/json", jsonapi) 100 r.get("/text", textapi) 101 102 // static files 103 r.use(static.serve("/", static.localfile("./public", true))) 104 105 r.noroute(func(c *gin.context) { 106 c.file("./public/index.html") 107 }) 108 109 r.run(":8000") 110 }
后端代码中有个静态文件的路由 r.use(static.serve("/", static.localfile("./public", true)))
只要将下面的前端代码命名为 index.html 并放在和后端代码根目录下的 public 文件夹中,
就可以在启动后端之后, 直接通过访问 *" 来显示页面了
前端
前端很简单, 就是在页面初始化完成后创建 websocket 连接, 然后发送消息并显示接受到的消息
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8" /> 5 <title>index</title> 6 </head> 7 <body> 8 <h1>test websocket</h1> 9 <p id="message-json"></p> 10 <p id="message-text"></p> 11 <script> 12 function jsonws() { 13 var ws = new websocket("ws://localhost:8000/json"); 14 //连接打开时触发 15 ws.onopen = function (evt) { 16 console.log("connection open ..."); 17 var obj = { a: "bb", b: 2 }; 18 ws.send(json.stringify(obj)); 19 }; 20 //接收到消息时触发 21 ws.onmessage = function (evt) { 22 console.log("received message: " + evt.data); 23 document.getelementbyid("message-json").innertext += evt.data; 24 }; 25 //连接关闭时触发 26 ws.onclose = function (evt) { 27 console.log("connection closed."); 28 }; 29 } 30 31 function textws() { 32 var ws = new websocket("ws://localhost:8000/text"); 33 //连接打开时触发 34 ws.onopen = function (evt) { 35 console.log("connection open ..."); 36 ws.send("text message"); 37 }; 38 //接收到消息时触发 39 ws.onmessage = function (evt) { 40 console.log("received message: " + evt.data); 41 document.getelementbyid("message-text").innertext = evt.data; 42 }; 43 //连接关闭时触发 44 ws.onclose = function (evt) { 45 console.log("connection closed."); 46 }; 47 } 48 // 启动 websocket 49 jsonws(); 50 textws(); 51 </script> 52 </body> 53 </html>结论
运行之后, 就可以看到页面上显示的 message 了, 发送一次信息, 会收到 10 条返回.