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

news2025/1/16 19:04:48

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 utils

import (
	"MassUsrsCom/common/message"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
)

// 这里将这些方法关联到结构体中
type Transfer struct {
	//分析它应该有哪些字段
	Conn net.Conn
	Buf  [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 process

import (
	"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,并直接反序列化成LoginMes
	var loginMes message.LoginMes
	err = json.Unmarshal([]byte(mes.Data), &loginMes)
	if err != nil {
		fmt.Println("json.Unmarshal error:")
		return
	}
	//1初始化一个Mes 结构体
	var resMes message.Message
	resMes.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的data
	resMes.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 main

import (
	"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 main

import (
	"fmt"
	"net"
)

// 处理和客户端的通信
func goroutine(conn net.Conn) {
	//这里需要延时关闭conn
	defer 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 main

import (
	"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.Message
	mes.Type = message.LoginMesType
	//3.初始化一个LoginMes 结构体
	var loginMes message.LoginMes
	loginMes.UserID = userID
	loginMes.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() //mes
	if err != nil {
		fmt.Println("ReadPkg(conn) error:", err)
		return
	}
	//9.将mes的Data部分反序列化成LoginResMes
	var loginResMes message.LoginResMes
	err = json.Unmarshal([]byte(mes.Data), &loginResMes)
	if err != nil {
		fmt.Println("json.Unmarshal error:", err)
		return
	}
	//10.验证LoginResMes
	if loginResMes.Code == 200 {
		fmt.Println("登录成功")
	} else if loginResMes.Code == 500 {
		fmt.Println(loginResMes.Error)
	}
	return
}

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

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

相关文章

【源码+文档】基于SpringBoot+Vue的健康宝个人健康管理平台

一、项目技术架构: 本项目是一款基于SpringBootVue的个人健康信息管理系统。后端采用SpringBoot架构,前端采用VueElementUI实现页面的快速开发,并使用关系型数据库MySQL存储系统运行数据。本系统分为两种角色,分别是系统管理员&a…

DS入门引言(0)

文章目录 前言一、什么是数据结构?二、什么是算法?三、数据结构与算法的重要性?四、如何学习它们?总结 前言 是的,我又开了一个专栏,数据结构(Data Structure),其实数据结…

【人工智能】AI人工智能的重要组成部分,深入解析CNN与RNN两种神经网络的异同与应用场景和区别

文章目录 一、卷积神经网络(CNN)详解1. 特征与结构CNN的基本结构 2. 应用场景3. 代码示例 二、循环神经网络(RNN)详解1. 网络结构与特点RNN的基本结构 2. 应用场景3. 代码示例 三、CNN与RNN的异同点1. 相同点2. 不同点 四、CNN与R…

javascript 阿里云,如何返回某个目录的文件

一、效果图 二、实际代码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>阿里云 OSS 文件列表&…

合约测试用例详解

合约测试的综合案例 第一部分 压力测试(使用caliper工具测试) 1.环境要求 配置基本环境 部署Caliper的计算机需要有外网权限;操作系统版本需要满足以下要求:Ubuntu >= 16.04、CentOS >= 7或MacOS >= 10.14;部署Caliper的计算机需要安装有以下软件:python 2.7…

PMP--冲刺题--解题--91-100

文章目录 11.风险管理--4.实施定量风险分析--题干场景中提到了“专家”&#xff0c;同时即将开始“量化风险”&#xff0c;因此对应的就是“定量风险分析”中的“专家判断”技术。项目经理应先征求各位专家们的意见&#xff0c;以获取最佳的量化风险实施方案。谋定而后动91、 […

wordpress发邮件SMTP服务器配置步骤指南?

wordpress发邮件功能如何优化&#xff1f;怎么用wordpress发信&#xff1f; 由于WordPress默认的邮件发送功能可能不够稳定&#xff0c;配置SMTP服务器成为了许多网站管理员的选择。AokSend将详细介绍如何在WordPress中配置SMTP服务器&#xff0c;以确保邮件能够顺利发送。 w…

Linux-磁盘优化的几个思路

优化前先看看性能指标 I/O 基准测试 fio&#xff08;Flexible I/O Tester&#xff09;正是最常用的文件系统和磁盘 I/O 性能基准测试工具。这类工具非常用&#xff0c;了解即可 应用程序优化 应用程序处于整个 I/O 栈的最上端&#xff0c;它可以通过系统调用&#xff0c;来调…

可持续边缘计算:挑战与未来方向

论文标题&#xff1a;Sustainable edge computing: Challenges and future directions 中文标题&#xff1a;可持续边缘计算&#xff1a;挑战与未来方向 作者信息&#xff1a; Patricia Arroba, Department of Electronic Engineering, Universidad Politcnica de Madrid, Ma…

CANLogFD-ER2数据记录仪数据记录与导出教程

CANLogFD-ER2数据记录仪数据记录与导出教程 VBDSP下载地址&#xff1a; www.njlike.com 数据下载操作视频&#xff1a; 一、引脚定义与指示灯状态 二、设备接线 电脑可以通过网口或WiFi与设备连接&#xff1b;短接通道CAN_H引脚和CAN_R引脚可使能终端电阻。设备接入CAN总线…

PGMP-02项目集管理绩效域

目录 1.概要 2.defintions定义 3.Program Management Performance Domain interactions 交互 4. Organizational Strategy, Portfolio Management, and Program Management Linkage 5. Portfolio and Program Distinctions 区别 6. Program and Project Distinctions区别 …

HCIP——GRE和MGRE

目录 VPN GRE GRE环境的搭建 GRE的报文结构 GRE封装和解封装报文的过程 GRE配置​编辑 R1 R2 GRE实验​​​​​​​​编辑 MGRE 原理 MGRE的配置 R1 R2 R3 R4 查看映射表 抓包 MGRE环境下的RIP网络 综合练习​编辑 VPN 说到GRE&#xff0c;我们先来说个大…

行业预测 60TB 硬盘将于 2028 年到来

在硬盘容量增长停滞了一段时间后&#xff0c;在短短四年内从目前的 30TB 增长到 60TB 将是一个巨大的增长。 然而&#xff0c;这正是 IEEE 最新发布的《海量数据存储设备和系统国际路线图》报告所预测的。 该路线图预计 2028 年市场上将出现 60TB 的硬盘驱动器。 这一增长将由一…

MoonBit 双周报 Vol.57:AI助手功能增强、表达式优先级调整、JS 交互优化、标准库与实验库API多项更新!

2024-10-08 IDE更新 AI Codelens支持 /generate 和 /fix 命令 /generate 命令能够提供一个通用的用以生成代码的聊天界面。 /fix 命令能够读取当前函数的错误信息给出修复建议。 MoonBit更新 调整中缀表达式和if、match、loop、while、for、try表达式的优先级, 后者这些控制…

DAMA数据管理知识体系(第11章 数据仓库和商务智能)

课本内容 11.1 引言 概要 数据仓库被公认为企业数据管理的核心语境关系图 图11-1 语境关系图&#xff1a;数据仓库和商务智能业务驱动因素 运营支持职能合规需求商务智能活动目标和原则 目标 一个组织建设数据仓库的目标通常有&#xff1a; 1&#xff09;支持商务智能活动。 2&…

VNC轻松连接远程Linux桌面

Linux配置VNC&#xff08;以RedHat、CentOS为例&#xff09; 说明&#xff1a; Linux平台安装VNCServer Windows平台使用VNC-Viewer 1.在Linux平台安装VNCServer服务端软件包。 yum -y install vnc *vnc-server*2.修改VNCServer主配置文件 vi /etc/sysconfig/vncservers复制…

Python库matplotlib之七

Python库matplotlib之七 饼图标注楔形图自动标注楔形图楔形图彩色设置改变楔形图标注和autopct文本位置 饼图 词法&#xff1a;Axes.pie(x, explodeNone, labelsNone, colorsNone, autopctNone, pctdistance0.6, shadowFalse, labeldistance1.1, startangle0, radius1, counte…

408模拟卷

尽管Floyd算法在处理带有负权边的图时表现出色,但它却无法处理包含负权回路的图。这主要是因为负权回路的存在会导致最短路径问题变得无意义或无法求解。

Springboot——使用poi实现excel动态图片导入解析

文章目录 前言依赖引入导入实现方式一方式二前言 最近要实现一个导入导出的功能点,需要能将带图片的列表数据导出到excel中,且可以导入带图片的excel列表数据。 考虑到低代码平台的表头与数据的不确定性,技术框架上暂定使用Apache-POI。 依赖引入 由于POI的包很多种,为…

数据结构-排序1

1.排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序…