Zinx框架学习 - 多路由实现

news2024/11/27 0:38:34

Zinx - V0.6 多路由实现

  • 之前在已经给Zinx配置了路由模式,但是之前的Zinx只能绑定一个路由的处理业务方法
  • 显然这是无法满足基本的服务器需求,需要给Zinx添加多路由的方案
  • 查看之前的Server定义,路由Router只有一个,当我们想要再添加一个路由时就会将旧的路由替换掉

实现思路

消息管理模块实现 

imsgHandler.go

package ziface

//消息管理抽象层
type IMsgHandle interface {
	// 调度/执行对应的Router消息处理方法
	DoMsgHandler(request IRequest)
	// 为消息添加具体的处理逻辑
	AddRouter(msgID uint32, router IRouter)
}

msgHandler.go

package znet

import (
	"fmt"
	"strconv"
	"zinx/ziface"
)

//消息处理模块的实现

type MsgHandle struct {
	//存放每个MsgID 所对应的处理方法
	Apis map[uint32]ziface.IRouter
}

//初始化/创建MsgHandle方法
func NewMsgHandle() *MsgHandle {
	return &MsgHandle{
		Apis: make(map[uint32]ziface.IRouter),
	}
}

// 调度/执行对应的Router消息处理方法
func (mh *MsgHandle) DoMsgHandler(request ziface.IRequest) {
	//1 从Request中找到msgID
	handler, ok := mh.Apis[request.GetMsgID()]
	if !ok {
		fmt.Println("api msgID = ", request.GetMsgID(), " is NOT FOUND! Need Register!")
	}
	//2 根据MsgID 调度对应router业务即可
	handler.PreHandle(request)
	handler.Handle(request)
	handler.PostHandle(request)
}

// 为消息添加具体的处理逻辑
func (mh *MsgHandle) AddRouter(msgID uint32, router ziface.IRouter) {
	//1 判断 当前msg绑定的API处理方法是否已经存在
	if _, ok := mh.Apis[msgID]; ok {
		//id已经注册了
		panic("repeat api , msgID = " + strconv.Itoa(int(msgID)))
	}
	//2 添加msg与API的绑定关系
	mh.Apis[msgID] = router
	fmt.Println("Add api MsgID = ", msgID, " succ!")
}

Zinx集成消息管理模块

  • server.go

Server结构体将之前的Handle替换成MsgHandler

//iServer的接口实现,定义一个Server的服务器模块
type Server struct {
	//服务器的名称
	Name string
	//服务器绑定的ip版本
	IPVersion string
	//服务器监听的IP
	IP string
	//服务器监听的端口
	Port int
	//当前server的消息管理模块, 用来绑定MsgID和对应的处理业务API关系
	MsgHandler ziface.IMsgHandle
}

AddRouter方法修改成MsgHandler.AddRouter

//路由功能:给当前的服务注册一个路由方法,供客户端的链接处理使用
func (s *Server) AddRouter(msgID uint32, router ziface.IRouter) {
	s.MsgHandler.AddRouter(msgID, router)
	fmt.Println("Add Router Succ!!")
}

初始化Server模块方法修改

//初始化Server模块的方法
func NewServer(name string) ziface.IServer {
	s := &Server{
		Name:       utils.GlobalObject.Name,
		IPVersion:  "tcp4",
		IP:         utils.GlobalObject.Host,
		Port:       utils.GlobalObject.TcpPort,
		MsgHandler: NewMsgHandle(),
	}
	return s
}

Start中NewConnection调用的修改

//启动服务器
func (s *Server) Start() {
	...
	// 将处理新连接的业务方法 和 conn 进行绑定 得到我们的链接模块
	dealConn := NewConnection(conn, cid, s.MsgHandler)
	cid++
	...
}
  • iserver.go

修改对应的AddRouter定义

package ziface

//定义一个服务器接口
type IServer interface {
	//启动服务器
	Start()
	//停止服务器
	Stop()
	//运行服务器
	Serve()
	//路由功能:给当前的服务注册一个路由方法,供客户端的链接处理使用
	AddRouter(uint32, IRouter)
}
  • connection.go

Connection结构体定义修改

type Connection struct {
	...
	//消息的管理MsgID 和对应的处理业务API关系
	MsgHandler ziface.IMsgHandle
}

NewConnection方法修改

//初始化链接模块的方法
func NewConnection(conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {
	c := &Connection{
		Conn:       conn,
		ConnID:     connID,
		MsgHandler: msgHandler,
		isClosed:   false,
		ExitChan:   make(chan bool, 1),
	}
	return c
}

StartReader方法修改

//链接的读业务方法
func (c *Connection) StartReader() {
	...
		//从路由中,找到注册绑定的Conn对应的router调用
		//根据绑定好的MsgID 找到对应处理api业务 执行
		go c.MsgHandler.DoMsgHandler(&req)
	}
}

Zinx框架开发

  • Client0 发送ID为0的消息
package main

import (
	"fmt"
	"io"
	"net"
	"time"
	"zinx/znet"
)

//模拟客户端
func main() {
	fmt.Println("client0 start...")

	time.Sleep(1 * time.Second)

	//1 直接链接远程服务器,得到一个conn链接
	conn, err := net.Dial("tcp", "127.0.0.1:8999")
	if err != nil {
		fmt.Println("client start err, exit!")
		return
	}

	for {
		//发送封包的message消息  MsgID:0
		dp := znet.NewDataPack()
		binaryMsg, err := dp.Pack(znet.NewMsgPackage(0, []byte("Zinx client0 Test Message")))
		if err != nil {
			fmt.Println("Pack error:", err)
			return
		}
		if _, err := conn.Write(binaryMsg); err != nil {
			fmt.Println("write error", err)
			return
		}

		//服务器就应该给我们回复一个message数据, MsgID:1 pingpingping

		// 1 先读取流中的head部分 得到ID 和 dataLen

		binaryHead := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(conn, binaryHead); err != nil {
			fmt.Println("read head error ", err)
			break
		}
		// 将二进制的head拆包到msg 结构体中
		msgHead, err := dp.Unpack(binaryHead)
		if err != nil {
			fmt.Println("client unpack msgHead error ", err)
			break
		}

		if msgHead.GetMsgLen() > 0 {
			// 2再根据DataLen进行第二次读取,将data读出来
			msg := msgHead.(*znet.Message)
			msg.Data = make([]byte, msg.GetMsgLen())

			if _, err := io.ReadFull(conn, msg.Data); err != nil {
				fmt.Println("read msg data error , ", err)
				return
			}

			fmt.Println("---> Recv Server Msg : ID = ", msg.Id, ", len = ", msg.DataLen, ", data = ", string(msg.Data))
		}

		//cpu阻塞
		time.Sleep(1 * time.Second)
	}

}

  • Client1 发送ID为1的消息
package main

import (
	"fmt"
	"io"
	"net"
	"time"
	"zinx/znet"
)

//模拟客户端
func main() {
	fmt.Println("client1 start...")

	time.Sleep(1 * time.Second)

	//1 直接链接远程服务器,得到一个conn链接
	conn, err := net.Dial("tcp", "127.0.0.1:8999")
	if err != nil {
		fmt.Println("client start err, exit!")
		return
	}

	for {
		//发送封包的message消息  MsgID:0
		dp := znet.NewDataPack()
		binaryMsg, err := dp.Pack(znet.NewMsgPackage(1, []byte("Zinx client1 Test Message")))
		if err != nil {
			fmt.Println("Pack error:", err)
			return
		}
		if _, err := conn.Write(binaryMsg); err != nil {
			fmt.Println("write error", err)
			return
		}

		//服务器就应该给我们回复一个message数据, MsgID:1 pingpingping

		// 1 先读取流中的head部分 得到ID 和 dataLen
		binaryHead := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(conn, binaryHead); err != nil {
			fmt.Println("read head error ", err)
			break
		}
		// 将二进制的head拆包到msg 结构体中
		msgHead, err := dp.Unpack(binaryHead)
		if err != nil {
			fmt.Println("client unpack msgHead error ", err)
			break
		}

		if msgHead.GetMsgLen() > 0 {
			// 2再根据DataLen进行第二次读取,将data读出来
			msg := msgHead.(*znet.Message)
			msg.Data = make([]byte, msg.GetMsgLen())

			if _, err := io.ReadFull(conn, msg.Data); err != nil {
				fmt.Println("read msg data error , ", err)
				return
			}

			fmt.Println("---> Recv Server Msg : ID = ", msg.Id, ", len = ", msg.DataLen, ", data = ", string(msg.Data))
		}

		//cpu阻塞
		time.Sleep(1 * time.Second)
	}

}

  • server.go 多Router:0 - PingRouter;1 - HelloZinxRouter
package main

import (
	"fmt"
	"zinx/ziface"
	"zinx/znet"
)

//基于Zinx框架来开发的 服务器端应用程序
//ping test 自定义路由
type PingRouter struct {
	znet.BaseRouter
}

//Test Handle
func (this *PingRouter) Handle(request ziface.IRequest) {
	fmt.Println("Call PingRouter Handle...")
	//先读取客户端的数据,再回写ping..ping...ping
	fmt.Println("recv from client: msgID = ", request.GetMsgID(),
		", data = ", string(request.GetData()))

	err := request.GetConnection().SendMsg(200, []byte("ping...ping...ping"))
	if err != nil {
		fmt.Println(err)
	}
}

//hello Zinx test 自定义路由
type HelloZinxRouter struct {
	znet.BaseRouter
}

//Test Handle
func (this *HelloZinxRouter) Handle(request ziface.IRequest) {
	fmt.Println("Call HelloZinxRouter Handle...")
	//先读取客户端的数据,再回写ping..ping...ping
	fmt.Println("recv from client: msgID = ", request.GetMsgID(),
		", data = ", string(request.GetData()))

	err := request.GetConnection().SendMsg(201, []byte("Hello Welcome to Zinx!!"))
	if err != nil {
		fmt.Println(err)
	}
}

func main() {
	//1 创建一个server句柄,使用Zinx的api
	s := znet.NewServer("[zinx V0.6]")

	//2 给当前zinx框架添加自定义的router
	s.AddRouter(0, &PingRouter{})
	s.AddRouter(1, &HelloZinxRouter{})

	//3 启动server
	s.Serve()
}

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

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

相关文章

七个很好的 Python 工具,让你的生活更轻松

有一句谚语“你不必重新发明轮子”。工具就是最好的例子。它可以帮助您以简单的方式实现复杂且耗时的功能。在我看来,为了提高生产力和效率,我们需要使用一些可用的最佳工具。在这里,我整理了 7 个工具,可以帮助您完成开发之旅。 …

【网络】TCP通讯(三次握手、四次挥手;滑动窗口;TCP状态转换;端口复用;TCP心跳检测机制)

前言:建议看着图片,根据文字描述走一遍TCP通讯过程,加深理解。 目录 TCP通信时序: 1)建立连接(三次握手)的过程: 2)数据传输的过程: 3)关闭连…

java的IP组播

文章目录 1. 简介2. 组播地址和组3. 客户端和服务器4. 路由器和路由5. 使用组播Socket6. 构造函数7. 与组播组通信8. 案例实战 1. 简介 前面介绍的Socket都是单播Socket,它们提供点对点的通信。单播Socket在两个明确的端点之间创建一个连接,有一个发送方…

LNMP平台搭建

文章目录 安装 Nginx 服务安装 MySQL 服务安装配置 PHP 解析环境 安装 Nginx 服务 systemctl stop firewalld systemctl disable firewalld setenforce 0安装依赖包 yum -y install pcre-devel zlib-devel gcc gcc-c make创建运行用户 useradd -M -s /sbin/nologin nginx编译…

ArduPilot之H743+BMI270x2+First Normal Takeoff

ArduPilot之H743BMI270x2First Normal Takeoff 1. 源由2. 正常起飞3. 问题汇总3.1 机架构型3.2 IMU对齐3.3 接收机3.4 GPS3.5 VTX3.6 电调3.7 PID 4. 总结5. 参考资料6. 附录6.1 补充AcroTrainer视频6.2 补充Acro视频 1. 源由 鉴于目前该飞控板子在ArduPilot开源社区尚未得到官…

Photoshop 批量照片转格式

Photoshop 批量照片转格式 文章目录 Photoshop 批量照片转格式前言一、打开Photoshop软件二、打开图像处理器三、参数设置四、运行 前言 在工作和学习中,我们可能会遇到需要处理多张图片、更改多张图片格式的情况,如果一张一张的进行处理是很麻烦浪费时…

一步一步从功能测试到测试开发,我这一路的坎坷谁能懂?

读者提问: 测试开发工程师到底是测试,还是开发 ? 鱼鱼回答: 既是测试,也是开发。 首先,测试开发是测试工程师,他们是服务于业务测试同学的,目标是解决业务测试工程师的具体问题。…

基于flask的web应用开发——接受post请求

目录 0. 前言1. 了解post方法2. 在flask中实现3. 具体讲解 0. 前言 操作系统:Windows10 家庭版 开发环境:Pycahrm Comunity 2022.3 Python解释器版本:Python3.8 第三方库:flask 1. 了解post方法 POST是HTTP协议定义的一种请…

尚硅谷JUC极速版笔记

尚硅谷JUC极速版笔记 1、JUC概述1.1 进程和线程1.2 线程的状态(6个)1.3 wait和sleep1.4 并发与并行1.5 管程(锁)1.6 用户线程和守护线程 2、Lock接口2.1 复习synchronized(java内置同步锁)2.2 什么是Lock接…

03使用IDEA快速开发一个WEB应用的具体流程

使用集成开发环境实现web开发 集成开发工具很多,其中目前使用比较多的是IntelliJ IDEA和Eclipse IntelliJ IDEA(居多): JetBrain公司开发的收费软件, IDEA在提示功能方面要强于Eclipse使用起来更加智能更好用Eclipse(较少):Eclipse是IBM团队开发的, Eclipse寓意是…

ChatGPT 国内镜像网站独家汇总:发现最优秀的人工智能对话体验!

欢迎来到我们的 ChatGPT 镜像网站汇总博客!在这个令人激动的人工智能时代,ChatGPT 作为一款顶尖的语言模型,已经引起了全球范围内的热议。但是,您是否曾经为了找到最佳的 ChatGPT 使用体验而苦苦搜寻?别担心&#xff0…

电商业务逻辑总结

一、后台模块:商品管理 1. 基本概念 ① spu: 标准化产品单元 不是一件具体的商品 eg iphone14 ② sku: 库存量单元 指的就是一件具体的商品 eg iphone14 128G 蓝色 ③ 销售属性 出现了商品详情页右侧的商品属性信息 ④ 平台属性 出现了商品详情页下…

vue 实现微信扫码登录的方法

一、准备工作: 1.微信公众号,扫码登录 2.域名,也就是域名解析(public_domain) 3.微信登录验证 4.配置微信扫码登录页面的代码,有了上面的准备工作,下面就可以开始编码了。 二、开发环境&#xf…

pinia的用法,一篇文章教你搞懂vuex的继任者pinia

一:pinia是什么? Pinia 是一个轻量级的、易于使用的 Vue.js 状态管理库。它是 Vuex 的一个替代方案,专为 Vue 3 设计,提供了更简单的 API 和更好的 TypeScript 支持。在你提供的代码中,Pinia 被用于管理应用程序的状态…

VAO、VBO、EBO简介

1.顶点缓冲对象(Vertex Buffer Objects, VBO) 顶点缓冲对象(VBO)的作用就是管理这个在GPU上创建的显存。使用这些缓冲对象的好处是我们可以一次性的发送一大批数据到显卡上,而不是每个顶点发送一次。从CPU把数据发送到显卡相对较慢&#xff…

java.security.MessageDigest的用法

java.security.MessageDigest MessageDigest的含义 message含义是:消息,信息 digest的含义是 digest 必应词典 n.摘要;文摘;概要;汇编 v.消化;领会;领悟;理解 海词 n. 摘要 vt. 消化;理解 vi…

进制转换(及规律)

Java变量命名规则和前端一样 约束 接口使用大驼峰 变量方法小托福 常量全大写 数值类型的 整型 byte a 1 所占空间1字节(-128-127) short a 1 所占空间2字节(-32768-32767)2^15-2^15-1 int a 1 所占空间4…

2023 华为 Datacom-HCIE 真题题库 11/12--含解析

单项选择题 1.[试题编号:190685] (单选题)通过iMasterNCE-Campus部署的虚拟化园区网络场景中,以下关于“添加设备”的描述中,错误的是哪一项? A、IMaster NCE-Campus支持通过设备角色添加设备 B、IMaster …

装饰器Python】进阶知识点

要明白装饰器首先得知道闭包 闭包:是内部函数对外部函数作用域的引用,并且一般外部函数函数的返回值是内部函数的函数名 def outer(x): # 外部函数 a x * 2 def inter(b) # 内部函数 …

手撕数据结构—单链表

✅作者:简单^不简单 🔥系列专栏:C语言数据结构 💖如果文章有错误,时刻欢迎大家的指正。当然觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝 💬格言:希望我…