IMV7.0

一、背景

经历了多个版本,基础内容在前面,可以使用之前的基础环境:

v1:
https://blog.csdn.net/wtt234/article/details/132139454

v2:
https://blog.csdn.net/wtt234/article/details/132144907

v3:
https://blog.csdn.net/wtt234/article/details/132148572

v4:
https://blog.csdn.net/wtt234/article/details/132169338

v5:
https://blog.csdn.net/wtt234/article/details/132169651

v6:
https://blog.csdn.net/wtt234/article/details/132170959

 

 

 

 

二、代码

2.1user.go

package mainimport ("net""strings"
)type User struct {Name stringAddr stringC    chan stringconn net.Connserver *Server
}//创建一个用户的API
func NewUser(conn net.Conn, server *Server) *User {userAddr := conn.RemoteAddr().String()user := &User{Name: userAddr,Addr: userAddr,C:    make(chan string),conn: conn,server: server,}//启动监听当前user channel消息的goroutinego user.ListenMessage()return user
}//用户的上线业务
func (this *User) Online() {//用户上线,将用户加入到onlineMap中this.server.mapLock.Lock()this.server.OnlineMap[this.Name] = thisthis.server.mapLock.Unlock()//广播当前用户上线消息this.server.BroadCast(this, "已上线")
}//用户的下线业务
func (this *User) Offline() {//用户下线,将用户从onlineMap中删除this.server.mapLock.Lock()delete(this.server.OnlineMap, this.Name)this.server.mapLock.Unlock()//广播当前用户上线消息this.server.BroadCast(this, "下线")}//给当前User对应的客户端发送消息
func (this *User) SendMsg(msg string) {this.conn.Write([]byte(msg))
}//用户处理消息的业务
func (this *User) DoMessage(msg string) {if msg == "who" {//查询当前在线用户都有哪些this.server.mapLock.Lock()for _, user := range this.server.OnlineMap {onlineMsg := "[" + user.Addr + "]" + user.Name + ":" + "在线...\n"this.SendMsg(onlineMsg)}this.server.mapLock.Unlock()} else if len(msg) > 7 && msg[:7] == "rename|" {//消息格式: rename|张三newName := strings.Split(msg, "|")[1]//判断name是否存在_, ok := this.server.OnlineMap[newName]if ok {this.SendMsg("当前用户名被使用\n")} else {this.server.mapLock.Lock()delete(this.server.OnlineMap, this.Name)this.server.OnlineMap[newName] = thisthis.server.mapLock.Unlock()this.Name = newNamethis.SendMsg("您已经更新用户名:" + this.Name + "\n")}} else {this.server.BroadCast(this, msg)}
}//监听当前User channel的 方法,一旦有消息,就直接发送给对端客户端
func (this *User) ListenMessage() {for {msg := <-this.Cthis.conn.Write([]byte(msg + "\n"))}
}

 2.2server.go

package mainimport ("fmt""io""net""sync""time"
)type Server struct {Ip   stringPort int//在线用户的列表OnlineMap map[string]*UsermapLock   sync.RWMutex//消息广播的channelMessage chan string
}//创建一个server的接口
func NewServer(ip string, port int) *Server {server := &Server{Ip:        ip,Port:      port,OnlineMap: make(map[string]*User),Message:   make(chan string),}return server
}//监听Message广播消息channel的goroutine,一旦有消息就发送给全部的在线User
func (this *Server) ListenMessager() {for {msg := <-this.Message//将msg发送给全部的在线Userthis.mapLock.Lock()for _, cli := range this.OnlineMap {cli.C <- msg}this.mapLock.Unlock()}
}//广播消息的方法
func (this *Server) BroadCast(user *User, msg string) {sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msgthis.Message <- sendMsg
}func (this *Server) Handler(conn net.Conn) {//...当前链接的业务//fmt.Println("链接建立成功")user := NewUser(conn, this)user.Online()//监听用户是否活跃的channelisLive := make(chan bool)//接受客户端发送的消息go func() {buf := make([]byte, 4096)for {n, err := conn.Read(buf)if n == 0 {user.Offline()return}if err != nil && err != io.EOF {fmt.Println("Conn Read err:", err)return}//提取用户的消息(去除'\n')msg := string(buf[:n-1])//用户针对msg进行消息处理user.DoMessage(msg)//用户的任意消息,代表当前用户是一个活跃的isLive <- true}}()//当前handler阻塞for {select {case <-isLive://当前用户是活跃的,应该重置定时器//不做任何事情,为了激活select,更新下面的定时器case <-time.After(time.Second * 10)://已经超时//将当前的User强制的关闭user.SendMsg("你被踢了")//销毁用的资源close(user.C)//关闭连接conn.Close()//退出当前Handlerreturn //runtime.Goexit()}}
}//启动服务器的接口
func (this *Server) Start() {//socket listenlistener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))if err != nil {fmt.Println("net.Listen err:", err)return}//close listen socketdefer listener.Close()//启动监听Message的goroutinego this.ListenMessager()for {//acceptconn, err := listener.Accept()if err != nil {fmt.Println("listener accept err:", err)continue}//do handlergo this.Handler(conn)}
}

2.3main.go

package mainfunc main() {server := NewServer("127.0.0.1", 8888)server.Start()
}

三、客户端

 

 

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

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

相关文章

centos7 部署Tomcat和jpress应用

目录 一、静态、动态、伪静态 二、Web 1.0 和 Web 2.0 三、centos7 部署Tomcat 3.1 安装、配置jdk 3.2 安装 Tomcat 3.3 配置服务启动脚本 3.3.1 创建用户和组 3.3.2 创建tomcat.conf文件 3.3.3 创建服务脚本(tomcat.service) 3.3.4 重新加载守护进程并且测试 四、部…

试图将更改推送到 GitHub,但是远程仓库已经包含了您本地没有的工作(可能是其他人提交的修改)

这通常是由于其他人或其他仓库推送到了相同的分支上&#xff0c;导致您的本地仓库和远程仓库之间存在冲突。 错误信息&#xff1a; To github.com:8upersaiyan/CKmuduo.git ! [rejected] main -> main (fetch first) error: failed to push some refs to github.com:8upers…

【干货】商城系统的重要功能特性介绍

电子商务的快速发展&#xff0c;商城系统成为了企业开展线上销售的重要工具。一款功能强大、用户友好的商城系统能够有效提升企业的销售业绩&#xff0c;提供良好的购物体验。下面就商城系统的重要功能特性作一些简单介绍&#xff0c;帮助企业选择合适的系统&#xff0c;打造成…

etcd

文章目录 etcd单机安装设置键值对watch操作读取键过往版本的值压缩修订版本lease租约&#xff08;过期机制&#xff09;授予租约撤销租约keepAlive续约获取租约信息 事务基于etcd实现分布式锁原生实现官方 concurrency 包实现 服务注册与发现Go 操作 Etcd 参考 etcd etcd 是一…

Java课题笔记~ Spring事务的程序举例环境搭建

举例&#xff1a;购买商品 trans_sale 项目 本例要实现购买商品&#xff0c;模拟用户下订单&#xff0c;向订单表添加销售记录&#xff0c;从商品表减少库存。 实现步骤&#xff1a; Step0&#xff1a;创建数据库表 创建两个数据库表 sale , goods sale 销售表&#xff1a;…

访问器模式(C++)

定义 表示一个作用于某对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。 应用场景 在软件构建过程中&#xff0c;由于需求的改变&#xff0c;某些类层次结构中常常需要增加新的行为(方法)&#xff0c;如果直接…

途乐证券:沪指强势拉升涨0.63%,券商等板块走强,传媒板块活跃

31日早盘&#xff0c;两市股指全线走高&#xff0c;沪指一度涨超1%收复3300点&#xff0c;上证50指数盘中涨逾2%&#xff1b;随后涨幅有所收窄&#xff1b;两市成交额显着放大&#xff0c;北向资金净买入超90亿元。 到午间收盘&#xff0c;沪指涨0.63%报3296.58点&#xff0c;深…

SQL分类及通用语法数据类型(超详细版)

一、SQL分类 SQL是结构化查询语言&#xff08;Structured Query Language&#xff09;的缩写。它是一种用于管理和操作关系型数据库系统的标准化语言。SQL分类如下&#xff1a; DDL: 数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库、表、字段&#xff09;DML:…

信息安全:认证技术原理与应用.

信息安全&#xff1a;认证技术原理与应用. 认证机制是网络安全的基础性保护措施&#xff0c;是实施访问控制的前提&#xff0c;认证是一个实体向另外一个实体证明其所声称的身份的过程。在认证过程中&#xff0c;需要被证实的实体是声称者&#xff0c;负责检查确认声称者的实体…

如何使用Word转PDF转换器在线工具?在线Word转PDF使用方法

Word转PDF转换器在线&#xff0c;是一种方便快捷的工具&#xff0c;可帮助您在不需要下载任何软件的情况下完成此任务。无论您是需要在工作中共享文档&#xff0c;还是将文件以PDF格式保存以确保格式不变&#xff0c;都可以依靠这款在线工具轻松完成转换。那么如何使用Word转PD…

QGIS二次开发三:显示Shapefile

Shapefile 为 OGR 所支持的最重要的数据格式之一&#xff0c;自然可以被 QGIS 加载。那么该如何显示Shapefile呢&#xff1f; 一、先上代码 #include <qgsapplication.h> #include <qgsproviderregistry.h> #include <qgsmapcanvas.h> #include <qgsvec…

【Spring】Bean的作用域和生命周期

目录 一、引入案例来探讨Bean的作用域 二、Bean的作用域 2.1、Bean的6种作用域 2.2、设置Bean的作用域 三、Spring的执行流程 四、Bean的声明周期 1、生命周期演示 一、引入案例来探讨Bean的作用域 首先我们创建一个User类&#xff0c;定义一个用户信息&#xff0c;在定义…

fabric.js里toDataURL后,画布内容展示不全?

复现场景&#xff1a; 用fabric生成画布后&#xff0c;转成图片&#xff0c;然后直接在浏览器里打开&#xff0c;画布展示内容缺失 画布原图&#xff1a; toDataURL后链接在浏览器打开&#xff1a; 原因解析&#xff1a; base64链接太长&#xff0c;输入浏览器链接被截断&…

尚品汇总结九:RabbitMQ在项目的应用(面试专用)

项目中的问题 1.搜索与商品服务的问题 商品服务修改了 商品的上架状态,商品就可以被搜索到.采用消息通知,商品服务修改完商品上架状态,发送消息 给 搜索服务,搜索服务消费消息,进行商品数据ES保存.下架也是一样. 2.订单服务取消订单问题 延迟队里 保存订单之后 开始计时,…

参考RabbitMQ实现一个消息队列

文章目录 前言小小消息管家1.项目介绍2. 需求分析2.1 API2.2 消息应答2.3 网络通信协议设计 3. 开发环境4. 项目结构介绍4.1 配置信息 5. 项目演示 前言 消息队列的本质就是阻塞队列&#xff0c;它的最大用途就是用来实现生产者消费者模型&#xff0c;从而实现解耦合以及削峰填…

如何将视频转成gif图?视频怎么转gif高清图片?

在看电视或是短视频的时候&#xff0c;总能发现一些有趣的片段&#xff0c;当想把这些视频转gif图片发送给朋友的时候该怎么处理呢&#xff1f;其实可以试试专业的视频转gif工具&#xff0c;本文介绍一个视频在线转gif的方法&#xff0c;一起来了解一下吧。 打开首页&#xff…

重发布选路问题

一、思路 &#xff1b; 1.增加不优选路开销解决选路不佳问题 2.用增加开销的方式使R1 不将ASBR传的R7传给另一台ASBR解决R1、R2、R3、R4pingR7环回环路 二、操作 ------IP地址配置如图 1.解决环路 [r2] ip ip-prefix a permit 7.7.7.0 24 [r2]route-policy huawei per…

c++ boost circular_buffer

boost库中的 circular_buffer顾名思义是一个循环缓冲器&#xff0c;其 capcity是固定的当容量满了以后&#xff0c;插入一个元素时&#xff0c;会在容器的开头或结尾处删除一个元素。 circular_buffer为了效率考虑&#xff0c;使用了连续内存块保存元素 使用固定内存&#x…

The Sandbox 与 D.OASIS 联手打造 D.OASIS 城市

我们非常高兴地宣布与 D.OASIS 建立合作伙伴关系&#xff0c;共同打造无与伦比的娱乐体验&#xff1a;The Sandbox 中的 D.OASIS 城市&#xff01; 作为合作的一部分&#xff0c;The Sandbox 和D.OASIS将共同打造 D.OASIS 城市&#xff0c;一座充满无限可能的大都市&#xff0…

CSS基础介绍笔记1

官方文档 CSS指的是层叠样式&#xff08;Cascading Style Sheets&#xff09;地址&#xff1a;CSS 教程离线文档&#xff1a;放大放小&#xff1a;ctrl鼠标滚动为什么需要css&#xff1a;简化修改HTML元素的样式&#xff1b;将html页面的内容与样式分离提高web开发的工作效率&…