GO网络编程(七):海量用户通信系统5:分层架构

P323开始(尚硅谷GO教程)老韩又改目录结构了,没办法,和之前一样,先说下目录结构,再给代码,部分代码在之前讲过,还有知识的话由于本人近期很忙,所以这些就不多赘述了,读者可自行查阅官方文档(GO中文标准库)或其他网站。

目录

    • 框架与目录结构
    • utils.go
    • userProcess.go
    • processor.go
    • server的main
    • login.go

框架与目录结构

老韩讲的是分层架构,就和MVC差不多,服务器框架图如下
在这里插入图片描述

目录结构

海量用户通信系统/
├── go.mod
├── client/
│   ├── main.go
│   └── login.go
├── server/
│   └── main.go
└── common/├── message/│   └── message.go└── utils/└── utils.go

utils.go

package utilsimport ("MassUsrsCom/common/message""encoding/binary""encoding/json""fmt""net"
)// 这里将这些方法关联到结构体中
type Transfer struct {//分析它应该有哪些字段Conn net.ConnBuf  [8096]byte //传输时使用的缓冲
}// 可以自定义错误变量err,即定义一个新的err,写入自定义的错误内容,
// 但考虑到上层可能要判断err的原本的类型,所以这里直接返回err
// 读数据包
func (tf *Transfer) ReadPkg() (mes message.Message, err error) {fmt.Println("读数据包...")buf := tf.Buf[:] //1.准备缓冲区//后续可能会读取更多字节,所以缓冲区大小一般都设置得比较大//2.读取消息头部并存入缓冲区n, err := tf.Conn.Read(buf[:4])//conn.Read 在conn没有被关闭的情况下,才会阻塞//如果客户端关闭了conn,则不会阻塞if n != 4 || err != nil {//因为服务端要通过判断err的类型来提示客户端关闭,所以这里什么都不做return}//3.将缓冲区的消息头部转换成消息长度var pkgLen = binary.BigEndian.Uint32(buf[:4])//4.根据消息长度读取消息内容n, err = tf.Conn.Read(buf[:pkgLen])if n != int(pkgLen) || err != nil {fmt.Println("conn.Read error")return}//5.将消息内容反序列化并返回err = json.Unmarshal(buf[:pkgLen], &mes)//由于 mes 是在函数签名中命名的返回值变量,Go 自动为它创建了一个//初始的 message.Message 类型实例,这样就无需显式声明if err != nil {fmt.Println("read pkg body error,json.Unmarshal error")return}return
}// 写数据包
func (tf *Transfer) WritePkg(data []byte) (err error) {fmt.Println("写数据包...")buf := tf.Buf[:4] //1.准备缓冲区//2.根据消息内容获取消息长度var pkgLen = uint32(len(data))//3.将消息长度存入缓冲区binary.BigEndian.PutUint32(buf, pkgLen)//4.发送消息长度n, err := tf.Conn.Write(buf)if n != 4 || err != nil {fmt.Println("conn.Write error")return}//5.发送消息内容n, err = tf.Conn.Write(data)if n != int(pkgLen) || err != nil {fmt.Println("conn.Write error")return}return
}

userProcess.go

smsProcess暂时声明一个process包,userProcess如下:

package processimport ("MassUsrsCom/common/message""MassUsrsCom/server/utils""encoding/json""fmt""net"
)type UserProcess struct {Conn net.Conn
}// 处理登录消息
func (up *UserProcess) ServerProcessLogin(mes *message.Message) (err error) {//1.先从mes中取出mes.Data,并直接反序列化成LoginMesvar loginMes message.LoginMeserr = json.Unmarshal([]byte(mes.Data), &loginMes)if err != nil {fmt.Println("json.Unmarshal error:")return}//1初始化一个Mes 结构体var resMes message.MessageresMes.Type = message.LoginResMesType//2创建一个LoginResMes 结构体var loginResMes message.LoginResMes//如果用户id=100,密码=123456,认为合法,否则不合法if loginMes.UserID == 100 && loginMes.UserPwd == "123456" {//合法loginResMes.Code = 200} else {//不合法loginResMes.Code = 500 //状态码,表示该用户不存在loginResMes.Error = "该用户不存在,请注册再使用"}//3 将loginResMes序列化data, err := json.Marshal(loginResMes)if err != nil {fmt.Println("json.Marshal error:")return}//4.将序列化后的loginResMes作为给resMes的dataresMes.Data = string(data)//5.对resMes进行序列化,准备发送data, err = json.Marshal(resMes)if err != nil {fmt.Println("json.Marshal error:")return}//6.发送消息//因为使用分层模式(mvc),我们先创建一个Transfer实例,然后读取tf := &utils.Transfer{Conn: up.Conn}err = tf.WritePkg(data)if err != nil {fmt.Println("WritePkg(conn) error:")return}return
}

processor.go

package mainimport ("MassUsrsCom/common/message""MassUsrsCom/server/process""MassUsrsCom/server/utils""fmt""io""net"
)type Processor struct {Conn net.Conn
}// 判断并处理不同种类的消息
func (proc *Processor) serverProcessMes(mes *message.Message) (err error) {switch mes.Type {case message.LoginMesType://处理登录逻辑//创建一个UserProcess实例up := &process.UserProcess{Conn: proc.Conn}err = up.ServerProcessLogin(mes)case message.RegisterMesType://处理注册default:fmt.Println("消息类型不存在,无法处理...")}return
}
func (proc *Processor) process2() (err error) {//循环读取客户端发送的信息for {//创建Transfer实例完成读包的任务tf := &utils.Transfer{Conn: proc.Conn}mes, err := tf.ReadPkg() //读取客户端消息if err != nil {if err == io.EOF {fmt.Println("客户端退出,相关的服务器协程也退出...")return err} else {fmt.Println(err)return err}}err = proc.serverProcessMes(&mes) //处理客户端的消息if err != nil {fmt.Println(err)return err}}
}

server的main

package mainimport ("fmt""net"
)// 处理和客户端的通信
func goroutine(conn net.Conn) {//这里需要延时关闭conndefer conn.Close()//这里调用总控,创建一个processor := &Processor{Conn: conn}err := processor.process2()if err != nil {fmt.Println("客户端和服务器通信协程错误:", err)return}
}
func main() {//提示信息fmt.Println("服务器在8889端口监听")listen, err := net.Listen("tcp", "0.0.0.0:8889")if err != nil {fmt.Println("服务器监听端口失败:", err)return}defer listen.Close()//一旦监听成功,就等待客户端来连接服务器for {fmt.Println("等待客户端来连接服务器......")conn, err := listen.Accept()if err != nil {fmt.Println("客户端连接服务器失败:", err)continue}//一旦连接成功,则启动一个协程和客户端保持通信go goroutine(conn)}
}

login.go

package mainimport ("MassUsrsCom/common/message""MassUsrsCom/server/utils""encoding/json""fmt""net"
)func login(userID int, userPwd string) (err error) {//下一个就要开始定协议// fmt.Printf("userId=%d pwd=%s\n", userId, pwd)// return nil//1.连接到服务器conn, err := net.Dial("tcp", "localhost:8889")if err != nil {fmt.Println("net.Dial error:", err)return}//延时关闭defer conn.Close()//2.初始化一个Mes 结构体var mes message.Messagemes.Type = message.LoginMesType//3.初始化一个LoginMes 结构体var loginMes message.LoginMesloginMes.UserID = userIDloginMes.UserPwd = userPwd//4.将loginMes 序列化data, err := json.Marshal(loginMes)if err != nil {fmt.Println("json.Marshal error:", err)return}//5.将序列化后的loginMes作为mes的Data部分mes.Data = string(data)//6.将mes序列化data, err = json.Marshal(mes)if err != nil {fmt.Println("json.Marshal error:", err)return}//7.发送消息,即mes的Data部分tf := utils.Transfer{Conn: conn}err = tf.WritePkg(data)if err != nil {fmt.Println("WritePkg(conn) error:", err)return}//8.接收服务器端返回的信息(消息mes或错误)mes, err = tf.ReadPkg() //mesif err != nil {fmt.Println("ReadPkg(conn) error:", err)return}//9.将mes的Data部分反序列化成LoginResMesvar loginResMes message.LoginResMeserr = json.Unmarshal([]byte(mes.Data), &loginResMes)if err != nil {fmt.Println("json.Unmarshal error:", err)return}//10.验证LoginResMesif loginResMes.Code == 200 {fmt.Println("登录成功")} else if loginResMes.Code == 500 {fmt.Println(loginResMes.Error)}return
}

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

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

相关文章

【C++】12.string类的使用

文章目录 1. 为什么学习string类?1.1 C语言中的字符串1.2 两个面试题(暂不做讲解) 2. 标准库中的string类2.1 string类(了解)2.2 auto和范围for 3. 查看技术文档4. string的访问5. 如何读取每个字符呢?6. auto语法糖(C11)7. 范围f…

浅析主流监控告警系统基本架构和原理

浅析主流监控告警系统基本架构和原理 一,监控系统的作用和目前主流监控系统 1,作用:监控系统一般有以下这几个作用 实时采集监控数据:包括硬件、操作系统、中间件、应用程序等各个维度的数据。实时反馈监控状态:通过…

【目标检测】集装箱缺陷检测数据集1476张5类缺陷VOC+YOLO格式

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):1476 标注数量(xml文件个数):1476 标注数量(txt文件个数):1476 标注…

ubuntu下打开摄像头

ubuntu下打开摄像头 在Ubuntu下,你可以使用cheese,这是一个开源的摄像头应用程序。如果你还没有安装它,可以通过以下命令安装: sudo apt-get updatesudo apt-get install cheese 安装完成后,你可以通过命令行启动它: cheese 或者,你也可以使用ffmpeg来打开摄像头并进…

MATLAB - 机器人机械臂设计轨迹规划器

系列文章目录 前言 本示例介绍了一种设计抓取和轨迹规划器的方法,该规划器可用于垃圾箱拣选系统。 在机器人技术中,垃圾箱拣选包括使用机械手从垃圾箱中取出物品。智能垃圾箱拣选是这一过程的高级版本,具有更强的自主性。使用摄像系统感知部件,规划器生成与场景相适应的无碰…

Telegram——Bot 机器人/小程序入门指南

一、Bot 介绍 在 TG 中,机器人可以用于接收和发送消息、管理群组(在有权限的情况下可以封禁用户、删除消息、置顶消息等)、通过API进行编程操作、使用 Inline 查询功能在不同的聊天室中提供查询服务、创建自定义键盘按钮、发出账单并收款、接入小程序游戏等。 然而,Bot 默…

智汇云舟亮相WAFI世界农业科技创新大会,并参编数字农业产业图谱

10月10日,2024WAFI世界农业科技创新大会农食行业创新与投资峰会在北京金海湖国际会展中心举行。中国农业大学MBA教育中心主任、教授付文阁、平谷区委常委、统战部部长刘堃、华为公共事业军团数字政府首席专家刘丹、荷兰瓦赫宁根大学前校长Aalt Dijkhuizen、牧原食品…

免费送源码:Java+Springboot+MySQL 水环境检测系统的设计与实现 计算机毕业设计原创定制

摘 要 在我国,水源的污染是不可忽视的问题。对于水质监测进行数据的采集工作,目前主要通过人工实现。因此,部分地区的采集工作,实施起来难度很大,比如恶劣环境和偏僻山区等地。所以,目前对于水质监测的研究,主导方向是建立更加高效完善,智能化的水质监测系统。近几年,无线传感器…

RWKV-CHN模型部署教程

一、模型介绍 RWKV 语言模型(用纯 100%RNN 达到 GPT 能力,甚至更强),该项目旨在通过为您自动化所有事情来消除使用大型语言模型的障碍。您需要的是一个只有几兆字节的轻量级可执行程序。此外,该项目还提供了一个接口兼…

计算机网络——p2p

流媒体是指在网络上以流式传输技术实时播放的多媒体内容,如音频、视频等。以下是关于流媒体的详细介绍: 一、工作原理 数据分割与传输: 流媒体技术将多媒体文件分割成较小的数据包。这些数据包按照特定的顺序进行编号,然后通过网络…

[单master节点k8s部署]40.安装harbor

harbor 是私有镜像仓库,用来存储和分发镜像的 。docker 还有一个官方的镜像仓库 docker hub,免费用户只能简单的使用,创建一个私有镜像仓库,存储镜像,付费用户才可以拥有更多权限,默认 docker pull 拉取镜像…

网络学习第二篇

认识网关和路由器 这里大家先了解一下什么三层设备。 三层设备 三层设备是指在网络架构中能够工作在第三层(网络层)的设备,通常包括三层交换机和路由器。这些设备可以根据IP地址进行数据包的转发和路由选择,从而在不同的网络之间…

<<迷雾>> 第11章 全自动加法计算机(5)--顺序取数 示例电路

顺序地从存储器里取数的电路方案. info::操作说明 在开始之前, 地址计数器 AC 需要清零, 以指向地址 0000. 按一下开关 KAR, 将 AC 当前的地址锁存到 AR 地址寄存器. 按住 KRD, 不要松开(注: 系统中使用的是普通开关, 无需按住), 再按一下 KDR, 数据保存到寄存器 DR 中, 最后,…

Unity3D 观察者模式

Unity3D 泛型事件系统 观察者模式 观察者模式是一种行为设计模式,通过订阅机制,可以让对象触发事件时,通知多个其他对象。 在游戏逻辑中,UI 界面通常会监听一些事件,当数据层发生变化时,通过触发事件&am…

多人播客的生成#使用OpenAI Swarm框架

使用Swarm来写多智能体的代码,非常简洁高效。 什么是Swarm? Swarm是由OpenAI开发的一个实验性多代理系统框架,旨在探索多代理系统的高效接口。该框架注重轻量级、可控性高且易于测试,主要用于展示代理之间的交接与例行操作模式。S…

基于SpringBoot的校园兼职管理系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…

【第十六周】回顾线性回归与逻辑回归以及它们的详细推导过程

目录 摘要Abstract1.线性回归1.1.一元线性回归1.1.1.函数凹凸性判断 1.2.多元线性回归1.3.进一步理解梯度下降法 2.逻辑回归2.1.信息论角度推导交叉熵损失函数2.2.概率论角度推导交叉熵损失函数 3.额外阅读:Label Smoothing3.1.One-hot 和 Label Smoothing 的优缺点…

数字媒体技术基础:色度子采样(4:4:4、4:2:2 、4:2:0)

在数字视频处理中,色度子采样 Chroma Subsampling可以用于压缩视频文件的大小,同时在大多数情况下保持较高的视觉质量,它的原理基于人类视觉系统对亮度 Luminance比对色度 Chrominance更加敏感这一特点。 一、 采样格式的表示方法 色度子采样…

人工智能和机器学习之线性代数(一)

人工智能和机器学习之线性代数(一) 人工智能和机器学习之线性代数一将介绍向量和矩阵的基础知识以及开源的机器学习框架PyTorch。 文章目录 人工智能和机器学习之线性代数(一)基本定义标量(Scalar)向量&a…

arcpy总结

arcpy 一、是什么二、为什么三、怎么用1、在哪里打开2、基础术语3、代码组织4、案例(1)裁剪(2)土地变化特征分析(4)文件访问与检测(5)空间数据的查询、插入与更新(6&…