IMV7.0

news2024/11/25 4:56:19

一、背景

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

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 main

import (
	"net"
	"strings"
)

type User struct {
	Name string
	Addr string
	C    chan string
	conn net.Conn

	server *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消息的goroutine
	go user.ListenMessage()

	return user
}

//用户的上线业务
func (this *User) Online() {

	//用户上线,将用户加入到onlineMap中
	this.server.mapLock.Lock()
	this.server.OnlineMap[this.Name] = this
	this.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] = this
			this.server.mapLock.Unlock()

			this.Name = newName
			this.SendMsg("您已经更新用户名:" + this.Name + "\n")
		}

	} else {
		this.server.BroadCast(this, msg)
	}
}

//监听当前User channel的 方法,一旦有消息,就直接发送给对端客户端
func (this *User) ListenMessage() {
	for {
		msg := <-this.C

		this.conn.Write([]byte(msg + "\n"))
	}
}

 2.2server.go

package main

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"
)

type Server struct {
	Ip   string
	Port int

	//在线用户的列表
	OnlineMap map[string]*User
	mapLock   sync.RWMutex

	//消息广播的channel
	Message 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发送给全部的在线User
		this.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 + ":" + msg

	this.Message <- sendMsg
}

func (this *Server) Handler(conn net.Conn) {
	//...当前链接的业务
	//fmt.Println("链接建立成功")

	user := NewUser(conn, this)

	user.Online()

	//监听用户是否活跃的channel
	isLive := 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()

			//退出当前Handler
			return //runtime.Goexit()
		}
	}
}

//启动服务器的接口
func (this *Server) Start() {
	//socket listen
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))
	if err != nil {
		fmt.Println("net.Listen err:", err)
		return
	}
	//close listen socket
	defer listener.Close()

	//启动监听Message的goroutine
	go this.ListenMessager()

	for {
		//accept
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("listener accept err:", err)
			continue
		}

		//do handler
		go this.Handler(conn)
	}
}

2.3main.go

package main

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

三、客户端

 

 

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

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

相关文章

Vue调用硬件 接口报错

谷歌浏览器调用硬件报错 报错原因 调用身份证读卡器&#xff0c;使用谷歌浏览器 读卡器硬件的接口 有几率是被谷歌拦截 所以报错 在谷歌地址栏输入 chrome://flags/ 搜索 block 找到这个选项 切换状态之后重启浏览器即可 当时找的这篇文章 解决问题 参考链接 如果大家的问题没…

2023集成电路产业发展与产教融合高峰论坛会议顺利举行

8月5日&#xff0c;由中国半导体行业协会和市政府共同主办&#xff0c;天水师范学院、天水华天科技股份有限公司、杭州加速科技有限公司承办的2023集成电路产业发展与产教融合高峰论坛在天水举行。天水市委书记冯文戈&#xff0c;教育部学生服务与素质发展中心副主任方伟&#…

untiy 连接两个UI或一段固定一段跟随鼠标移动的线段

注意&#xff0c;仅适用于UI&#xff0c;且Canvas必须是Camera模式&#xff0c;不能用在3D物体上&#xff0c;3D物体请使用LineRenender 先创建一个图片&#xff0c;将锚点固定在左边 然后在脚本中添加如下内容 public RectTransform startObj;//起点物体public RectTransfor…

软件测试基本准侧与方法摘录

软件测试基本准侧与方法的摘录&#xff08;应用实例待补充&#xff09; 写在最开始&#xff1a;“测试是为发现错误而执行程序的过程”。————《软件测试的艺术》 &#x1f929; 本文中很多概念描述摘抄自还有很多概念没有列举。已写的部分概念缺少相应的实例&#xff0c;尚…

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

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

2021年03月 Python(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 下列哪个操作不能退出IDLE环境&#xff1f; A&#xff1a;AltF4 B&#xff1a;CtrlQ C&#xff1a;按ESC键 D&#xff1a;exit() 正确的答案是&#xff1a;B&#xff1a;CtrlQ 解…

LM2596S-adj电源芯片应用电路

手边有两块降压电源模块&#xff0c;使用芯片为LM2596S-adj&#xff0c;手册中给的使用电路为&#xff1a; 市面上买到的模块为&#xff1a; 电路为&#xff1a; 对比后发现苏守坤博客中存在错误&#xff0c;原博客中实物图应为输出可调的电源模块&#xff0c;博主所画原理…

深入理解PyTorch中的NoamOpt优化器

深入理解PyTorch中的NoamOpt优化器 作者&#xff1a;安静到无声 个人主页 今天&#xff0c;我们将深入探讨一个在自然语言处理领域广泛使用的优化器——NoamOpt。这个优化器是基于PyTorch实现的&#xff0c;并且在"Attention is All You Need"这篇论文中首次提出。…

若依后台管理系统06

哈喽&#xff01;大家好&#xff0c;我是旷世奇才李先生 文章持续更新&#xff0c;可以微信搜索【小奇JAVA面试】第一时间阅读&#xff0c;回复【资料】更有我为大家准备的福利哟&#xff0c;回复【项目】获取我为大家准备的项目 最近打算把我手里之前做的项目分享给大家&#…

Rivian:一家已经在全球电动汽车领域站稳脚跟的热门电动汽车公司

来源&#xff1a;猛兽财经 作者 &#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;Rivian&#xff08;RIVN&#xff09;是全球电动汽车市场的主要参与者&#xff0c;由于全球电动汽车市场的不断增长和亚马逊&#xff08;AMZN&#xff09;的持续支持&#xf…

Blob的一些使用 - 下载、图片预览、切片上传

Blob Blob: 前端专门用于支持文件操作的二进制对象 File: 一种特殊的Blob对象 文件下载 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, in…

Qt应用开发(基础篇)——信号槽 Signals and Slots

一、前言 Qt成为我们今天拥有的灵活而舒适的工具&#xff0c;除了友好和能够快速开发设计师界面&#xff0c;信号槽机制是最大的核心特征&#xff0c;也是区别于其他开发框架最大的优势。 Qt的信号槽作用于两个对象之间的通信。当一个对象发生了改变&#xff0c;它希望其他关心…

Windows下安装 Redis

目录 1.下载 1.1.Redis官网 1.2.github下载地址 2.安装步骤 2.1.解压Redis压缩包 2.2.创建临时服务 2.3.启动客户端 2.4.注册Redis服务 3.总结 4.致谢 1.下载 1.1.Redis官网 Download | Redis Redis 官方网站没有提供 Windows 版的安装包&#xff0c;但可以通过…

Django实现音乐网站 ⑻

使用Python Django框架制作一个音乐网站&#xff0c; 本篇主要是后台对单曲原有功能的基础上进行部分功能实现和显示优化。 目录 新增编辑 歌手下拉显示修改 设置歌曲时长 安装eyed3库 获取mp3时长 歌曲时长字段修改 重写save方法 增加歌手单曲数量 查询歌手单曲数量 …

算法通关村——二分查找在拓展中的应用

1. 山脉数组的峰顶索引 山脉数组的峰顶索引 符合下列属性的数组 arr 称为 山脉数组 &#xff1a; arr.length > 3 存在 i&#xff08;0 < i < arr.length - 1&#xff09;使得&#xff1a; arr[0] < arr[1] < … arr[i-1] < arr[i] arr[i] > arr[i1] >…

企业计算机被_locked勒索病毒攻击如何处理及后期如何做好预防工作

随着信息技术的普及和不断发展&#xff0c;勒索病毒攻击已成为企业面临的一项严峻挑战。近期&#xff0c;山西某医药公司的计算机服务器被_locked勒索病毒攻击&#xff0c;导致企业的许多重要数据被加密&#xff0c;当发现企业的服务器被该勒索病毒攻击时&#xff0c;该企业的做…

初识mysql数据库之引入mysql客户端库

目录 一、下载第三方库 1. 准备工作 1. 使用mysql官网提供的库 2. yum源安装 二、测试第三方库是否可用 三、mysql常用接口介绍 1. 查看官方文档 2. 初始化 3. 关闭mysql 4. 连接mysql 5. 下达sql指令 四、一个简单的C客户端库连接mysql程序 1. 头文件 2. 初始化…

maven 删除下载失败的包

本文介绍了当Maven包报红时&#xff0c;使用删除相关文件的方法来解决该问题。文章详细说明了_remote.repositories、.lastUpdated和_maven.repositories文件的作用&#xff0c;以及如何使用命令行删除这些文件。这些方法可以帮助开发者解决Maven包报红的问题&#xff0c;确保项…

赛码网-圈地运动 100%AC代码(C)

———————————————————————————————————— ⏩ 大家好哇&#xff01;我是小光&#xff0c;嵌入式爱好者&#xff0c;一个想要成为系统架构师的大三学生。 ⏩最近在准备秋招&#xff0c;一直在练习编程。 ⏩本篇文章对赛码网的圈地运动 题目做一…

LeetCode 热题 100 JavaScript -- 74. 搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非递减顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 …