GO网络编程(五):海量用户通信系统3:整体框架与C/S通信总体流程【重要】

news2024/11/25 1:30:03

这个系统其实是尚硅谷的老韩讲的(尚硅谷网络编程项目),但是他讲得很碎片化,思路不够清晰,时间又长,所以要掌握还是挺难的。如果你听了他的视频,不去梳理系统业务流程,不去看代码就往下听,那是很容易懵圈的。好在本人做了登录模块和服务器处理消息的业务流程图,我相信这一定有助于大家整理思路和理解系统业务流程。另外,因为老韩之后把login函数的部分代码封装到单独的包里了,所以我就干脆从头开始快速讲一遍,当然之前讲过的知识不会再讲。

目录

    • 一、系统框架搭建
    • 二、C/S通信总体流程
      • 1.登录模块总体流程
      • 2.服务器处理消息总体流程

一、系统框架搭建

首先我们需要搭建好目录结构,目录结构如下

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

注意,模块名需要自定义,我的模块名叫MassUsrsCom。搭建好后,按如下步骤构建初步的代码:
1.在message.go中定义消息类型

package message

const (
	LoginMesType    = "LoginMes"
	LoginResMesType = "LoginResMes"
	RegisterMesType = "RegisterMes"
)

type Message struct {
	Type string `json:"type"` //消息类型
	Data string `json:"data"` //消息
}

// 定义两个消息..后面需要再增加
type LoginMes struct {
	UserID   int    `json:"userID"`   //用户id
	UserPwd  string `json:"userPwd"`  //用户密码
	UserName string `json:"userName"` //用户名
}

type LoginResMes struct {
	Code  int    `json:"code"`  //返回状态码 500表示该用户未注册 200表示登录成功
	Error string `json:"error"` //返回错误信息
}
type RegisterMes struct {
}

2.在utils.go中声明读数据包和写数据包的函数

package utils

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

// 读数据包
func ReadPkg(conn net.Conn) (mes message.Message, err error) {
	return
}

// 写数据包
func WritePkg(conn net.Conn, data []byte) (err error) {
	return
}

3.在login.go中声明 login函数

package main

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

func login(userID int, userPwd string) (err error) {
	return
}

4.在client包的main.go中写完整的代码

package main

import (
	"fmt"
)

// 定义两个变量,一个表示用户id,一个表示用户密码
var userID int
var userPwd string

func main() {
	//接收用户的选择
	var key int
	//判断是否还继续显示菜单
	var loop = true
	for loop {
		fmt.Println("------------------欢迎登录多人聊天系统")
		fmt.Println("\t\t\t 1 登录聊天室")
		fmt.Println("\t\t\t 2 注册用户")
		fmt.Println("\t\t\t 3 退出系统")
		fmt.Println("\t\t\t 请选择(1-3):")
		fmt.Scanln(&key)
		switch key {
		case 1:
			fmt.Println("登录聊天室")
			loop = false
		case 2:
			fmt.Println("注册用户")
			loop = false
		case 3:
			fmt.Println("退出系统")
			loop = false
		default:
			fmt.Println("你的输入有误,请重新输入")
		}
	}
	//根据用户输入显示新的提示信息
	if key == 1 {
		//说明用户要登录
		fmt.Printf("请输入用户的id号:")
		fmt.Scanf("%d\n", &userID)
		fmt.Printf("请输入用户的密码:")
		fmt.Scanf("%s\n", &userPwd)
		login(userID, userPwd)
	} else if key == 2 {
		fmt.Println("进行用户注册的逻辑...")
	}
}

5.在server包的main.go中写部分代码

package main

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

// 处理登录消息
func serverProcessLogin(conn net.Conn, mes *message.Message) (err error) {
	return
}

// 判断并处理不同种类的消息
func serverProcessMes(conn net.Conn, mes *message.Message) (err error) {
	switch mes.Type {
	case message.LoginMesType:
		//处理登录逻辑
		err = serverProcessLogin(conn, mes)
	case message.RegisterMesType:
		//处理注册
	default:
		fmt.Println("消息类型不存在,无法处理...")
	}
	return
}

// 处理和客户端的通信
func process(conn net.Conn) {
}
func main() {
}

二、C/S通信总体流程

1.登录模块总体流程

流程图
在这里插入图片描述

代码

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部分
	err = utils.WritePkg(conn, data)
	if err != nil {
		fmt.Println("WritePkg(conn) error:", err)
		return
	}
	//8.接收服务器端返回的信息(消息mes或错误)
	mes, err = utils.ReadPkg(conn) //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
}

2.服务器处理消息总体流程

流程图
在这里插入图片描述

代码

// 处理和客户端的通信
func process(conn net.Conn) {
	//这里需要延时关闭conn
	defer conn.Close()

	//循环读取客户端发送的信息
	for {
		mes, err := utils.ReadPkg(conn) //读取客户端消息
		if err != nil {
			if err == io.EOF {
				fmt.Println("客户端退出,相关的服务器协程也退出...")
				return
			} else {
				fmt.Println(err)
				return
			}
		}
		err = serverProcessMes(conn, &mes) //处理客户端的消息
		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 process(conn)
	}
}

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

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

相关文章

云计算身份认证与访问控制(Cloud Computing Identity Authentication and Access Control)

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

支持向量机(SVM)基础教程

一、引言 支持向量机(Support Vector Machine,简称SVM)是一种高效的监督学习算法,广泛应用 于分类和回归分析。SVM以其强大的泛化能力、简洁的数学形式和优秀的分类效果而备受机器学 习领域的青睐。 二、SVM基本原理 2.1 最大间…

watch命令:周期执行指定命令

一、命令简介 ​watch ​命令用于周期性地执行指定的命令,并显示其输出结果。 ‍ 二、命令参数 2.1 命令格式 watch [选项] 命令2.2 选项 ​-n, --interval​: 指定更新间隔时间(以秒为单位)。默认间隔时间为 2 秒。​-d, --difference…

数学与生活

多学科交叉 信号处理 小波 经济 政策 计算机 统计 信号处理与市场分析 经济与数据分析 政策与统计 过去的数学家没有一个是纯粹的数学家;生活中各方面工程的,物理的,天文,地理的,赌博,政治的&#xff1b…

删除AlibabaProtect

首先管理员运行cmd 然后执行下行 sc delete AlibabaProtect重启电脑,再删除该文件夹C:\Program Files (x86)\AlibabaProtect

prometheus学习笔记之PromQL

prometheus学习笔记之PromQL 一、PromQL语句简介 官方文档:https://prometheus.io/docs/prometheus/latest/querying/basics/ Prometheus提供⼀个函数式的表达式语⾔PromQL (Prometheus Query Language),可以使⽤户实时 地查找和聚合时间序列数据&…

vSAN04:vSAN远程数据存储挂载、双节点集群介绍/安装/组件读写/高级配置/故障处理方式

目录 vSAN远程数据存储挂载双节点vSAN集群介绍双节点vSAN集群安装双节点vSAN集群的组件读写方式双节点vSAN的高级配置双节点vSAN故障处理方式 vSAN远程数据存储挂载 在同一个vCenter下的VSAN集群可以互相挂载对方VSAN存储,以达到提高资源利用率的目的。 一个集群最…

Docker系列-5种方案超详细讲解docker数据存储持久化(volume,bind mounts,NFS等)

文章目录 Docker的数据持久化是什么?1.数据卷(Data Volumes)使用Docker 创建数据卷创建数据卷创建一个容器,将数据卷挂载到容器中的 /data 目录。进入容器,查看数据卷内容停止并重新启动容器,数据卷中的数据…

打卡第四天 P1081 [NOIP2012 提高组] 开车旅行

今天是我打卡第四天,做个省选/NOI−题吧(#^.^#) 原题链接:[NOIP2012 提高组] 开车旅行 - 洛谷 题目描述 输入格式 输出格式 输入输出样例 输入 #1 4 2 3 1 4 3 4 1 3 2 3 3 3 4 3 输出 #1 1 1 1 2 0 0 0 0 0 输入 #2 10 4 5 6 1 …

k8s 中存储之 hostPath 卷

目录 1 hostPath 卷介绍 2 hostPath 卷实际应用操作 2.1 创建 pod 资源类型 2.2 修改清单文件增加 hostPath 对应的参数配置 2.3 查看是否创建 卷 和 pod 2.4 创建发布文件测试是否正常访问 1 hostPath 卷介绍 EmptyDir中数据不会被持久化,它会随着Pod的结束而销…

每日一题|2187. 完成旅途的最少时间|二分法、计数器

本题的一个思路是从小到大遍历全部可能的t,并分别计算当前每一辆车所能够行驶的最多trips数量。 但是如果从1开始,结束在最不理想的情况是max(time) * totalTrip,在数据很大的时候很容易time out。 所以不妨对每一个可能的t重新思考性质。随…

STL的位图:bitset

引言 在C标准模板库(STL)中,bitset是一种用于表示固定大小序列的位集合的容器。每个位(bit)可以被独立地设置或清除,即它可以单独地表示0或1。bitset在处理二进制数据时非常有用,尤其是在需要节…

linux安装百度网盘

版本20.04 下载 deb:debian系列,Ubuntu下载这个 rpm:redhat系列 安装 在Downloads中找到,打开终端,使用命令安装 sudo dpkg -i 名称 //dpkg:Debian package在所有文件中找到。

Linux相关概念和易错知识点(12)(命令行参数、环境变量、本地变量)

1.命令行参数 (1)main函数的参数int argc和char* argv[]是什么? main函数可以带参数,即int main(int argc, char* argv[]),(int argc, char* argv[])叫做命令行参数列表,int argc叫参数的个数&a…

【YOLO学习】YOLOv3详解

文章目录 1. 网络结构1.1 结构介绍1.2 改进 2. 训练与测试过程3. 总结 1. 网络结构 1.1 结构介绍 1. 与 YOLOv2 不同的是,YOLOv3 在 Darknet-19 里加入了 ResNet 残差连接,改进之后的模型叫 Darknet-53。在 ImageNet上 实验发现 Darknet-53 相对于 ResN…

VSCODE驯服日记(三):配置C++环境

1. 下载mingw64,解压后把bin并添加到环境变量 1>编译器介绍 mingw:专为windowsgcc:多平台msvc :windows,且配合vs使用更佳 注意与调试器gdb和lldb的区别 2. 安装vscode插件: 安装C/C插件 安装code ru…

力扣之1322.广告效果

题目: sql建表语句: Create table If Not Exists Ads (ad_id int,user_id int,action ENUM (Clicked, Viewed, Ignored) ); Truncate table Ads; insert into Ads (ad_id, user_id, action) values (1, 1, Clicked); insert into Ads (ad_id, use…

Sublime Text 下载地址分享

Sublime Text官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘123云盘为您提供Sublime Text最新版正式版官方版绿色版下载,Sublime Text安卓版手机版apk免费下载安装到手机,支持电脑端一键快捷安装https://www.123684.com/s/kPxoTd-dCnxHSublime Text官方版下载丨最新版下…

双十一可以买什么物品?重磅推荐五款好用品牌!

距离今年的双十一盛典仅剩数十日,您是否已将心爱商品添加至购物车中了呢?还在犹豫未满载的朋友也无需焦虑,特意为您精选了五款好用的宝贝推荐,旨在为您的购物清单增添几分灵感与便捷,期待能为您的双十一购物之旅增添一…

GPU 是否有朝一日可以取代 CPU?

讨论 GPU 是否能够取代 CPU,需要从两者的基本架构、设计目的、性能表现、应用领域等多个方面进行分析。虽然你提到的4060显卡的核心频率接近服务器 CPU 的频率,这看起来似乎有一些相似性,但 GPU 和 CPU 的设计思路和适用场景差异显著&#xf…