1. 目标
- 使用 Golang 编写一个WebSocker 服务端
- 能接受客户端的WebSocket连接
- 启动服务器时接收命令行参数
- 能响应客户端数据
- 模式一 : webSocket 回音模式
- 模式二 : 图灵机器人聊天模式
- 使用Golang 编写一个WebSocket 客户端
- 使用 Vue 编写一个WebSocket 客户端
- 能和服务端通信
- 能主动获取WebSocket当前状态
- 能主动关闭WebSocket连接
- 能主动重新连接WebSokct连接
- 能显示WebSocket通信数据
2. 项目目录
|__common|____service_robot.go| server.go| myClient.go
common\service_robot.go
图灵机器人服务 , 提供给其他服务调用
package commonimport ( "bytes" "encoding/json" "errors" "io/ioutil" "log" "net/http")var err errorconst ( // 你的图灵机器人 apikey APIKEY = "" // 你的 userId USERID = "" // 图灵的接口请求地址 URL = "http://openapi.tuling123.com/openapi/api/v2")type ReqData struct { ReqType int `json:"reqType"` Perception map[string]interface{} `json:"perception"` UserInfo map[string]string `json:"userInfo"`}type Result struct { ResultType string `json:"resultType"` Values map[string]interface{} `json:"values"` GroupType int `json:"groupType"`}type RespData struct { Intent map[string]interface{} `json:"intent"` Results []Result `json:"results"`}func NewRobot() *ReqData { info := map[string]string{ "apiKey": APIKEY, "userId": USERID, } return &ReqData{ ReqType: 0, Perception: nil, UserInfo: info, }}func (r *ReqData) Chat(v interface{}) (error, []interface{}) { if val, ok := v.(string); ok { inputText := map[string]string{ "text": val, } r.Perception = make(map[string]interface{}) r.Perception["inputText"] = inputText jsonData, err := json.Marshal(r) if err == nil { return r.Post(jsonData) } } else { err = errors.New("format error") } return err, nil}func (r *ReqData) Post(data []byte) (error, []interface{}) { defer func() { if err != nil { log.Print("service_robot:post", err) } }() body := bytes.NewBuffer([]byte(data)) req, err := http.NewRequest("POST", URL, body) if err == nil { req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) defer resp.Body.Close() if err != nil { return err, nil } result, err := ioutil.ReadAll(resp.Body) if err != nil { return err, nil } log.Println(string(result)) var response RespData err = json.Unmarshal(result, &response) if err != nil { return err, nil } var outPut []interface{} for _, v := range response.Results { for _, value := range v.Values { outPut = append(outPut, value) } } return nil, outPut } return err, nil}
server.go
WebSocket 服务端
package mainimport ( "GoNote/chapter1/ws/common" "flag" "github.com/gorilla/websocket" "log" "net/http")var modeDescribe = `webSocket response model :--echo default value--robot The Turing robot responded`var addr = flag.String("addr", "", "http service addr")var model = flag.String("model", "", modeDescribe)var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true },}func echoFunc(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println("upgrader :", err) return } defer func() { log.Println(c.RemoteAddr().String(), "connect close") }() defer c.Close() log.Println(c.RemoteAddr().String(), "connect success") var robot = common.NewRobot() for { mt, message, err := c.ReadMessage() if err != nil { break } //t := time.Now().Format("2006-01-02 15:04:05") m := string(message) log.Println("server receive message :", m) // robot 机器人应答 if *model == "robot" { err, result := robot.Chat(m) if err == nil { for _, k := range result { if s, ok := k.(string); ok { message = []byte(s) err = c.WriteMessage(mt, message) } } } } else if *model == "echo" { // echo 回音服务 message = []byte(m) err = c.WriteMessage(mt, message) if err != nil { log.Println("write :", err) } } }}func main() { flag.Parse() log.SetFlags(0) log.Println("addr : ", *addr) http.HandleFunc("/echo", echoFunc) log.Fatal(http.ListenAndServe(*addr, nil))}
myclient.go
WebSocket 客户端
package mainimport ( "flag" "fmt" "github.com/gorilla/websocket" "log" "net/url" "os" "os/signal")var addr2 = flag.String("addr", "127.0.0.1:8080", "ws service addr")var key stringfunc main() { defer func() { if err := recover(); err != nil { log.Println("error : ", err) } }() flag.Parse() log.SetFlags(0) interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) u := url.URL{ Scheme: "ws", Host: *addr2, Path: "/echo", } log.Printf("connecting to %s", u.String()) c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) if err != nil { log.Fatal("websocket connect error : ", err) } defer c.Close() receiveData := make(chan string) responseMessage := make(chan string) go func() { for { fmt.Print("请输入聊天内容 : ") fmt.Scan(&key) if key != "" { receiveData <- key } data := <-responseMessage fmt.Println("I receive message : ", data) } }() for { select { case <-interrupt: log.Println("interrupt") err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) if err != nil { log.Println("write close:", err) return } close(receiveData) case data := <-receiveData: err := c.WriteMessage(websocket.TextMessage, []byte(data)) if err != nil { log.Println("write:", err) return } _, message, err := c.ReadMessage() if err != nil { log.Println("read from server err:", err) } else { responseMessage <- string(message) } } }}
WebSocket 客户端测试
$ go run server.go -addr 127.0.0.1:8080 -model robotaddr : 127.0.0.1:8080127.0.0.1:52402 connect successserver receive message : 北京天气{"intent":{"actionName":"","code":10008,"intentName":"","parameters":{"date":"","city":"北京"}},"results":[{"groupType":1,"resultType":"text","values":{"text":"北京:周六 05月30日,雷阵雨 南风,最低气温18度,最高气温30度。"}}]}
$ go run myClient.goconnecting to ws://127.0.0.1:8080/echo请输入聊天内容 : 北京天气I receive message : 北京:周六 05月30日,雷阵雨 南风,最低气温18度,最高气温30度。请输入聊天内容 :
vue WebSocket 客户端
这边我们使用的 JavaScript 库是 Vue
也可以就用简单的html去实现
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!-- import Vue before Element --> <script src="https://unpkg.com/vue/dist/vue.js"></script> <!-- import JavaScript --> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <!-- 引入样式 --> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"></head><body> <div id="app"> <el-row :gutter="20"> <el-col :span="16"> <div class="grid-content bg-purple"> <el-input v-model="message" placeholder="请输入内容" @keyup.enter.native="submitMessage"></el-input> </div> </el-col> <el-col :span="8"> <div class="grid-content bg-purple"> <el-button @click="submitMessage">发送信息</el-button> </div> </el-col> </el-row> <el-row> <el-col :span="24"> <div class="grid-content bg-purple-dark"> <el-button type="primary" @click="GetWsStatus">获取WS状态</el-button> <el-button type="success" @click="defaultWs">重新连接WS</el-button> <el-button type="info" @click="reloadPage">重新加载页面</el-button> <el-button type="warning" @click="showRespData" v-model="showButtonTitle">{{showButtonTitle}}数据</el-button> <el-button type="danger" @click="doCloseWS">关闭WS连接</el-button> </div> </el-col> </el-row> <el-row> <el-col :span="24"> <div class="grid-content bg-purple-light" v-show="showSwitch && showData != ''" v-html="showData"> </div> </el-col> </el-row> </div> <script> new Vue({ el: '#app', data() { return { addr: '127.0.0.1', port: '8080', message: '', showSwitch: false, showData: '', showButtonTitle:'显示', ws: null } }, // 初始创建 created() { this.defaultWs() }, // 离开页面时销毁ws destroyed() { this.ws.close() }, methods: { defaultWs() { console.log('try to connect webSocket') if ("WebSocket" in window) { console.log('support webSocket') var connectInfo = 'ws://' + this.addr + ':' + this.port + '/echo' this.ws = new WebSocket(connectInfo) this.ws.onopen = this.wsOnopen this.ws.onmessage = this.wsOnMessage this.ws.onclose = this.wsOnClose } }, wsOnopen() { console.log('connect success') }, wsOnMessage(e) { console.log('receive data: ') this.showData += '<span style="float:right">'+ e.data + ' : say server' + '</span><br />' console.log(e.data) console.log(this.showData) }, wsOnClose(e) { this.ws.close() }, wsOnSend(data) { this.ws.send(data) }, // 提交信息 submitMessage() { if (this.message !== '') { console.log('submit some data....') this.wsOnSend(this.message) this.showData += 'I say:' + this.message+'<br />' this.message = '' } }, // ws 状态查询 GetWsStatus() { switch (this.ws.readyState) { // 正在连接 case WebSocket.CONNECTING: this.$message({ message: 'WebSocket正在连接', }) break // 连接成功 case WebSocket.OPEN: this.$message({ message: 'WebSocket连接成功', type:'success' }) break // 已经关闭 case WebSocket.CLOSED: this.$message({ message: 'WebSocket连接已经关闭', type: 'error' }) break // 正在关闭 case WebSocket.CLOSING: this.$message({ message: 'WebSocket连接正在关闭', }) break } }, // 执行关闭ws doCloseWS() { this.wsOnClose() this.$message({ message: 'WebSocket关闭成功', type: 'success' }); }, // 重新加载页面 reloadPage() { location.reload() }, // 显示获取的数据 showRespData() { this.showSwitch ? this.showButtonTitle = '显示' : this.showButtonTitle='隐藏' this.showSwitch ? this.showSwitch = false : this.showSwitch = true } }, }) </script></body><style> .el-row { margin-top: 10px; } .bg-purple-light { background-color: aliceblue; } .speak-input { height: 200px; } </style></html>
前端的 WebSocket 测试

websocket-vue-test.png
文章来源:智云一二三科技
文章标题:第三十三章: Golang WebSocket Vue构建通信
文章地址:https://www.zhihuclub.com/1026.shtml