目录

项目结构

消息结构

服务端代码

定义客户端行为

服务启动

测试代码

总结


学习笔记,写到哪是哪。

websocket也是常用的协议了,在上一篇中主要测试使用了一下grpc。

下面我会把代码贴出来,然后对方法进行说明。

整体思路是服务端在连接上来的客户端进行记录,并且回复客户端连接情况。编写测试客户端在连接后定时发消息,来看一下使用情况。

项目结构

先简单的看一下项目结构,如下图:

这里的消息协议还是使用protocol,如果有不清楚怎么使用,可以看我这篇文章:Go语学习笔记 - grpc server/client protobuf | 从零开始Go语言_剑客阿良_ALiang的博客-CSDN博客

消息结构

先看一下message.proto文件,内容如下:

syntax = "proto3";
option go_package = "./protocol";message Message{int32 id = 1;string from = 2;string to = 3;string content = 4;
}

From主要是消息来源,To主要是接收人。

服务端代码

先看一下ws目录下的server.go代码,主要是构建一个服务结构体去接受连接上来的客户端对象。

代码如下:

package wsimport ("fmt""google.golang.org/protobuf/proto""learn-gorilla/protocol"
)var MyServer = NewWsServer()type WsServer struct {WsClients map[string]*WsClientRegister  chan *WsClientUngister  chan *WsClientHandler   chan []byte
}func NewWsServer() *WsServer {return &WsServer{WsClients: make(map[string]*WsClient),Register:  make(chan *WsClient),Ungister:  make(chan *WsClient),Handler:   make(chan []byte),}
}func (w *WsServer) Start() {fmt.Println("server starting...")for {select {case _client := <-w.Register:fmt.Printf("客户端:%s,注册\n", _client.Name)w.WsClients[_client.Name] = _client_msg := &protocol.Message{From:    "System",To:      _client.Name,Content: "you register",}_protoMsg, _ := proto.Marshal(_msg)_client.Msg <- _protoMsgcase _client := <-w.Ungister:fmt.Printf("客户端:%s,取消注册\n", _client.Name)if _, ok := w.WsClients[_client.Name]; ok {close(_client.Msg)delete(w.WsClients, _client.Name)}case _msg := <-w.Handler:_protoMsg := &protocol.Message{}proto.Unmarshal(_msg, _protoMsg)fmt.Printf("收到消息:%s\n", _protoMsg)if _protoMsg.From != "" && w.WsClients[_protoMsg.From] != nil {_client := w.WsClients[_protoMsg.From]_responseMsg := &protocol.Message{From:    "System",To:      _client.Name,Content: "receive your message",}_protoResponse, _ := proto.Marshal(_responseMsg)_client.Msg <- _protoResponse}}}
}

代码说明

1、可以看到WsServer结构体有几个channel,Register主要是注册通道,Ungister主要是下线通道,Hander主要是处理消息通道。

2、Start方法一旦启动,会不停的轮询几个通道,如果有消息则进行对应的处理。

3、注意到Hander通道会生成一个报文推给客户端对象Msg通道,这个后面在说client.go代码的时候提到。

定义客户端行为

这里理解上会有误区,其实client.go文件内,主要是用来定义客户端行为的,不是真实的客户端。

先看一下代码:

package wsimport ("fmt""github.com/gorilla/websocket""google.golang.org/protobuf/proto""learn-gorilla/protocol"
)type WsClient struct {Conn *websocket.ConnName stringMsg  chan []byte
}//读取数据
func (c *WsClient) Read() {defer func() {MyServer.Ungister <- cc.Conn.Close()}()for {_, _message, _err := c.Conn.ReadMessage()if _err != nil {fmt.Println("client read message error ", _err.Error())MyServer.Ungister <- cc.Conn.Close()break}_msg := &protocol.Message{}proto.Unmarshal(_message, _msg)if _msg.From == "" {_msg.From = c.Name}_req, _ := proto.Marshal(_msg)fmt.Printf("client read message -> %s\n", _msg)MyServer.Handler <- _req}
}//发送数据
func (c *WsClient) Write() {defer func() {MyServer.Ungister <- cc.Conn.Close()}()for message := range c.Msg {c.Conn.WriteMessage(websocket.BinaryMessage, message)}
}

代码说明

1、当客户端连接上来,我们会构造一个我定义的WsClient结构对象,将连接作为构造参数之一。

2、然后我们定义了WsClient需要执行的两种行为,一种是读,一种是写。读操作就是循环读取真实客户端内的数据,并吐给服务端的轮询器。写操作就是服务端将数据推送给Msg通道,轮询Msg通道将数据发给真实客户端。

服务启动

主要看一下main.go文件,里面是怎么启动的。

package mainimport ("fmt""github.com/google/uuid""github.com/gorilla/websocket""learn-gorilla/ws""net/http"
)var upGrader = websocket.Upgrader{//定义读写缓冲区大小WriteBufferSize: 4096,ReadBufferSize:  4096,CheckOrigin: func(r *http.Request) bool {return true},
}//升级链接,处理客户端
func Connect(w http.ResponseWriter, r *http.Request) {//通过升级器获得链接_conn, _err := upGrader.Upgrade(w, r, nil)if _err != nil {fmt.Println("获取连接失败:", _err.Error())return}//创建客户端_client := &ws.WsClient{Conn: _conn,Name: uuid.New().String(),Msg:  make(chan []byte),}ws.MyServer.Register <- _clientgo _client.Read()go _client.Write()
}func main() {//后台启动服务处理go ws.MyServer.Start()http.HandleFunc("/socket", Connect)http.ListenAndServe("127.0.0.1:8081", nil)
}

代码说明

1、这里可以看到试讲http升级为websocket,Connect方法主要进行连接后处理,可以看到会先构建一个WsClient对象,并将该对象通知到服务端Register通道中。

2、协程启动client的Read和Write方法,这部分是客户端对象需要去做的。

测试代码

OK,我们看一下启动后的打印。

client_test.go文件代码如下:

package wsimport ("fmt""github.com/gorilla/websocket""google.golang.org/protobuf/proto""learn-gorilla/protocol""sync""testing""time"
)var wg sync.WaitGroupfunc TestClient(t *testing.T) {conn, _, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8081/socket", nil)if err != nil {fmt.Println("错误信息:", err)}wg.Add(2)go read(conn)go send(conn)wg.Wait()
}func read(conn *websocket.Conn) {defer wg.Done()for {_, _msg, _ := conn.ReadMessage()_protoMsg := &protocol.Message{}proto.Unmarshal(_msg, _protoMsg)fmt.Printf("测试客户端收到消息:%s\n", _protoMsg.Content)}
}func send(conn *websocket.Conn) {defer wg.Done()for {time.Sleep(10 * time.Second)_reqMsg, _ := proto.Marshal(&protocol.Message{To:      "System",Content: "test data",})conn.WriteMessage(1, _reqMsg)}
}

测试代码说明

1、可以看到在连接后会不停的读取连接返回的数据,同时每隔10秒钟会发送一条测试数据。

执行结果

看一下服务的日志。

测试客户端日志。

总结

越学习go语言,越发现和Java的一些写法有些区别。主要还是站在思维角度的不一致,给人的差异感比较重。

分享:

不是“我觉得”三个字,就可以弥补所有因为好心办坏事带来的后果。

Go语学习笔记 - websocket gorilla(附测试代码) | 从零开始Go语言相关推荐

  1. sqlite3学习笔记-方法介绍和测试代码

    创建数据库 :sqlite3 test.db 查看数据数据 : .databases 创建表 :create table student (id integer primary key autoinc ...

  2. 《Python编程:从入门到实践》学习笔记——第11章 测试代码

    文章目录 前言 1 测试函数 1.1 单元测试和测试用例 1.2 可通过的测试 1.3 不能通过的测试 1.4 测试未通过时怎么办 1.5 添加新测试 2 测试类 2.1 各种断言方法 2.2 一个要 ...

  3. Go语学习笔记 - gorm使用 - gorm处理错误 Web框架Gin(十)

    学习笔记,写到哪是哪. 接着上一篇文章:Go语学习笔记 - gorm使用 - 原生sql.命名参数.Rows.ToSQL | Web框架Gin(九)_的博客-CSDN博客 目前gorm对数据库的一些操 ...

  4. Go语学习笔记 - 增加时间工具 | Web框架Gin(五)

    学习笔记,写到哪是哪. 接着上一篇的文章:Go语学习笔记 - 跨域配置.全局异常捕获 | Web框架Gin(四)_剑客阿良_ALiang的博客-CSDN博客_gin全局异常捕获 在上一篇中已经将一些基 ...

  5. 《Go语言圣经》学习笔记 第十一章 测试

    <Go语言圣经>学习笔记 第十一章 测试 目录 go test 测试函数 测试覆盖率 基准测试 剖析 示例函数 注:学习<Go语言圣经>笔记,PDF点击下载,建议看书. Go语 ...

  6. x%3e=y%3e=z的c语言表达式,我的C语学习笔记-C语言教程(三).doc

    我的C语学习笔记- C语言教程(三) C语言教程---第一章: C语言概论 C语言教程---第二章: 数据类型.运算符.表达式 C语言教程---第三章: C语言程序设计初步 C语言教程---第四章: ...

  7. c语言第七章函数笔记,我的C语学习笔记-C语言教程(七).doc

    我的C语学习笔记- C语言教程(七) C语言教程---第一章: C语言概论 C语言教程---第二章: 数据类型.运算符.表达式 C语言教程---第三章: C语言程序设计初步 C语言教程---第四章: ...

  8. 【Python】python初学者应该知道与其他语言差异化的高效编程技巧(附测试代码+详细注释)

    目录 1. 交换变量 2. 集合去重 3. 列表推导.集合推导和字典推导 4. 统计字符串中各个字符出现的次数 5.优雅地打印JSON数据 6.行内的if语句 6. 符合正常逻辑的数值比较 7. 田忌 ...

  9. 三种方式获取大疆照片的EXIF/XMP信息(附测试代码)

    目录 软件方式 在线方式 Python方式 第一种:pyexiv2 第二种:pyexif 测试代码:三种方式获取大疆照片的EXIF/XMP信息(附测试代码) - 小锋学长生活大爆炸 (xfxuezha ...

最新文章

  1. 爬一爬 iPhone 11为何嘴上说真丑,销量却真香?
  2. Rust 2018 即将到来:设法从 Rust 2015 过渡
  3. 机器学习面试知识点汇总(Machine Learning Core Concepts Collection)
  4. maven报错: 错误的类文件:… 类文件具有错误的版本 52.0,应为 54.0
  5. 多线程—AQS独占锁与共享锁原理
  6. 怎么隐藏Windows11开始菜单中的推荐面板
  7. centos搭建NFS服务器
  8. 让自制脚本随系统开机运行
  9. 中职生c语言搜题软件,适合法考学生用的搜题软件,这几款帮你搞定!
  10. 数组扁平化——flat方法理解
  11. 实验吧——WEB-天下武功唯快不破
  12. 曾经懵懂少年,曾经年少轻狂
  13. Windows10 安装spyder3
  14. [搞笑图片] 搞笑图片
  15. 工作4年从美团、陌陌、百度、阿里面试回来感想
  16. 肖战真的没我帅!我自己写的Python颜值检测说的!
  17. node.js请求接口
  18. centos7—DNS域名系统
  19. MySQL中对敏感字段值进行加密存储
  20. 2015年《大数据》高被引论文Top10文章No.7——大数据机器学习系统研究进展(下)...

热门文章

  1. trojan-gamethief.win32.magania.alsz病毒解决方案
  2. SQL Server数据库实操 第一波 数据查询
  3. pd.concat用法
  4. 更改airsim无人机模型
  5. 华为手机Android系统优缺点,华为手机与iPhone相比有哪些优缺点?
  6. SLsec招新题wp
  7. Python3.6 车牌识别代码源码
  8. 阿里云服务器4核8G配置CPU性能报价表
  9. 英国电信收入大增 收购EE效应初步显现
  10. Redis pool 配置详解