gorilla/websocket的chat示例代码简单分析

 代码地址:https://github.com/gorilla/websocket/tree/main/examples/chat

文件包含:main.go、hub.go、client.go、home.html

main.go文件

func main() {flag.Parse()hub := newHub() // 实例化Hubgo hub.run() // 使用chan处理 增删Hub的连接 和 广播消息http.HandleFunc("/", serveHome) // 访问home.html页面// 处理websockethttp.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {serveWs(hub, w, r)})server := &http.Server{Addr:              *addr,ReadHeaderTimeout: 3 * time.Second,}err := server.ListenAndServe()if err != nil {log.Fatal("ListenAndServe: ", err)}
}

hub.go文件

// 相当于连接池
type Hub struct {// Registered clients.clients map[*Client]bool // 存放所有websocket连接// Inbound messages from the clients.broadcast chan []byte // 广播消息// Register requests from the clients.register chan *Client // 添加websocket连接// Unregister requests from clients.unregister chan *Client // 删除websocket连接
}// 实例化Hub
func newHub() *Hub {return &Hub{broadcast:  make(chan []byte),register:   make(chan *Client),unregister: make(chan *Client),clients:    make(map[*Client]bool),}
}// 使用chan处理 增删Hub的连接 和 广播消息
func (h *Hub) run() {for {select {case client := <-h.register:h.clients[client] = true // 添加连接case client := <-h.unregister:if _, ok := h.clients[client]; ok {delete(h.clients, client) // 删除连接close(client.send)}case message := <-h.broadcast:for client := range h.clients {select {case client.send <- message: // 广播消息default:close(client.send)delete(h.clients, client)}}}}
}

client.go文件

// 连接
type Client struct {hub *Hub // 引用Hub// The websocket connection.conn *websocket.Conn // websocket连接// Buffered channel of outbound messages.send chan []byte // 消息发送chan
}// readPump pumps messages from the websocket connection to the hub.
//
// The application runs readPump in a per-connection goroutine. The application
// ensures that there is at most one reader on a connection by executing all
// reads from this goroutine.
func (c *Client) readPump() { // 读数据defer func() {c.hub.unregister <- cc.conn.Close()}()c.conn.SetReadLimit(maxMessageSize)c.conn.SetReadDeadline(time.Now().Add(pongWait))c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })for {_, message, err := c.conn.ReadMessage() // 接收消息if err != nil {if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {log.Printf("error: %v", err)}break}message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))c.hub.broadcast <- message // 将消息发送到广播消息chan}
}// writePump pumps messages from the hub to the websocket connection.
//
// A goroutine running writePump is started for each connection. The
// application ensures that there is at most one writer to a connection by
// executing all writes from this goroutine.
func (c *Client) writePump() { // 写数据ticker := time.NewTicker(pingPeriod)defer func() {ticker.Stop()c.conn.Close()}()for {select {case message, ok := <-c.send: // 从连接的chan接收消息c.conn.SetWriteDeadline(time.Now().Add(writeWait))if !ok {// The hub closed the channel.c.conn.WriteMessage(websocket.CloseMessage, []byte{})return}w, err := c.conn.NextWriter(websocket.TextMessage)if err != nil {return}w.Write(message) // 发送消息// Add queued chat messages to the current websocket message.n := len(c.send)for i := 0; i < n; i++ {w.Write(newline)w.Write(<-c.send)}if err := w.Close(); err != nil {return}case <-ticker.C:c.conn.SetWriteDeadline(time.Now().Add(writeWait))if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {return}}}
}// serveWs handles websocket requests from the peer.
func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {conn, err := upgrader.Upgrade(w, r, nil)if err != nil {log.Println(err)return}// 初始化Clientclient := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}client.hub.register <- client// Allow collection of memory referenced by the caller by doing all work in// new goroutines.go client.writePump() // 写数据go client.readPump() // 读数据
}

其他chat参考:

https://github.com/android-coco/chat 

https://github.com/GoLangFengShen/chat

https://www.golangblogs.com/read/im/date-2023.02.19.09.38.24

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/131606.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态路由协议OSPF项目部署(二)

1. 静态和动态路由的区别&#xff1b; 2. OSPF协议通信过程与部署&#xff1b; 3. OSPF协议在项目上的应用场景 - OSPF - 开放式最短路径优先 - 一个动态路由协议 - 路由器转发数据 - 路由器需要一张地图 - 路由表 - 路由表如何构建的&#xff1f; - 依靠手动 或…

访问者模式

详情可参考&#xff1a;https://zhuanlan.zhihu.com/p/380161731 意图&#xff1a;主要将数据结构与数据操作分离。 适用于&#xff1a;系统中有稳定的数据结构&#xff0c;且数据结构的功能经常发生变化。 双分派&#xff1a;我的理解是两次多态操作&#xff0c;动态获取对象…

卷积与反卷积

文章目录 1 卷积1.1 卷积后的输出维度 2 反卷积2.1 来源2.2 原理2.3 使用2.3.1 padding2.3.2 strideoutput_padding 2.3 意义 1 卷积 1.1 卷积后的输出维度 设输入维度为i x i&#xff0c;卷积核大小为k x k&#xff0c;padding为p&#xff0c;strides为s&#xff0c;则输出维…

APM建设踩了哪些坑?去哪儿旅行分布式链路追踪系统实践

一分钟精华速览 分布式链路追踪系统在企业的APM体系中扮演着重要的角色。本文分享了去哪儿旅行构建分布式链路追踪系统的实践经验。从APM整体架构设计入手&#xff0c;讲述了日志收集、Kafka传输和Flink任务处理等环节的性能优化实践和踩坑经验。 同时&#xff0c;作者结合丰…

C/C++数据结构之顺序表函数实现以及详细解析

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂。 目录 1.前言 2.顺序表…

删除链表的倒数第 N 个结点

LeetCode 19. 删除链表的倒数第 N 个结点 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *n…

定积分的几何应用(总结非常全面!)

文章目录 1 原函数存在性和可积性1.1 函数可积的充分条件&#xff08;判定条件&#xff09;1.2 函数存在原函数的充分条件&#xff08;判定条件&#xff09;1.3 函数可积的必要条件&#xff08;性质&#xff09;1.4 变上限积分的性质 2 平面图形2.1 平面图形的面积2.1.1 直角坐…

【ES专题】ElasticSearch集群架构剖析

目录 前言阅读对象阅读导航前置知识笔记正文一、ES集群架构1.1 为什么要使用ES集群架构1.2 ES集群核心概念1.2.1 节点1.2.1.1 Master Node主节点的功能1.2.1.2 Data Node数据节点的功能1.2.1.3 Master Node主节点选举流程 1.2.2 分片1.3 搭建三节点ES集群1.3.1 ES集群搭建步骤1…

解析python爬取Ebay数据的方式

前言 Ebay是全球著名的电子商务平台之一&#xff0c;每天都有海量的商品信息涌入其中&#xff0c;在电商行业获取这些数据试试非常有价值的&#xff0c;为了更好地了解市场动态&#xff0c;掌握更多的电商行情。Python爬虫成为了必不可少的工具&#xff0c;本文将通过使用Http…

idea中配置spring boot单项目多端口启动

参照文章 https://zhuanlan.zhihu.com/p/610767685 项目配置如下 下面为 idea 2023&#xff0c;不同版本的设置有区别&#xff0c;但是没那么大&#xff0c;idea 2023默认使用新布局&#xff0c;切换为经典布局即可。 在项目根目录的.idea/workspace.xml文件里添加如下配置 &l…

高等数学教材重难点题型总结(十二)无穷级数

最后一更&#xff01;原谅博主已经开始瞎写字了&#xff0c;最近压力太大&#xff0c;以后有时间用mathPix打出来&#xff0c;看起来更舒适一些~ 高数最后一章&#xff0c;重点在于审敛法和求解幂级数的收敛半径~ 高数基础笔记全部更新完毕&#xff0c;共12*336期&#xff0c…

SpringBoot项目多环境开发

1.yml文件&#xff08;旧&#xff09; 说明&#xff1a;旧的写法。 #应用环境 spring:profiles:active: dev --- #设置环境#生产环境 spring:profiles: pro server:port: 81--- #开发环境 spirng:profiles: dev server:port: 81--- #测试环境 spring:profiles: test server:p…

C/C++ static关键字详解(最全解析,static是什么,static如何使用,static的常考面试题)

目录 一、前言 二、static关键字是什么&#xff1f; 三、static关键字修饰的对象是什么&#xff1f; 四、C 语言中的 static &#x1f34e;static的C用法 &#x1f349;static的重点概念 &#x1f350;static修饰局部变量 &#x1f4a6;static在修饰局部变量和函数的作用 &a…

基于Google Colaboratory安装Go语言编译器操作流程

文章目录 1. 什么是Google Colaboratory2. 访问Google Colaboratory3. 创建新的笔记本4.【方法①】使用apt命令安装golang5.【方法②】使用Go安装包安装golang6. 参考链接 1. 什么是Google Colaboratory Colab是一种托管的笔记本电脑服务&#xff0c;不需要安装即可使用&#x…

STL(第三课):list

STL中的list是一个双向链表&#xff0c;它可以动态地将元素插入和删除&#xff0c;提供了许多方便的操作接口。下面我们来讲解一下CSTL list的相关内容。 list的创建比较简单&#xff0c;只需要包含头文件#include<list>&#xff0c;然后使用std::list模板即可声明一个li…

使用pdf2image pdf转图片

安装poppler https://wenku.csdn.net/answer/1zxh8ckp6i from pdf2image import convert_from_path, convert_from_bytes import os# https://github.com/Belval/pdf2imageoutput_folder ./ dpi_value 600 pdf_start_page 1 # pdf显示的第一页 start_page 237 # 真实页码 p…

二叉树按二叉链表形式存储,试编写一个判别给定二叉树是否是完全二叉树的算法

完全二叉树&#xff1a;就是每层横着划过去是连起来的&#xff0c;中间不会断开 比如下面的左图就是完全二叉树 再比如下面的右图就是非完全二叉树 那我们可以采用层序遍历的方法&#xff0c;借助一个辅助队列 当辅助队列不空的时候&#xff0c;出队头元素&#xff0c;入队头…

在 CelebA 数据集上训练的 PyTorch 中的基本变分自动编码器

摩西西珀博士 一、说明 我最近发现自己需要一种方法将图像编码到潜在嵌入中&#xff0c;调整嵌入&#xff0c;然后生成新图像。有一些强大的方法可以创建嵌入或从嵌入生成。如果你想同时做到这两点&#xff0c;一种自然且相当简单的方法是使用变分自动编码器。 这样的深度网络不…

SparkSQL

1、Spark简介 2、Spark-Core核心算子 3、Spark-Core 4、SparkSQL 文章目录 一、概述1、简介2、DataFrame、DataSet3、SparkSQL特点 二、Spark SQL编程1、SparkSession新API2、DataFrame2.1 创建DataFrame2.2 SQL 语法2.3 DSL语法 3、DataSet4、RDD、DataFrame、DataSet相互转换…