• 6-zinx基于Golang-多路由实现


    前言

    • 之前在已经给Zinx配置了路由模式,但是之前的Zinx只能绑定一个路由的处理业务方法
    • 显然这是无法满足基本的服务器需求,需要给Zinx添加多路由的方案
    • 查看之前的Server定义,路由Router只有一个
    //iServer的接口实现,定义一个Server的服务器模块
    type Server struct {
    	//服务器的名称
    	Name string
    	//服务器绑定的ip版本
    	IPVersion string
    	//服务器监听的IP
    	IP string
    	//服务器监听的端口
    	Port int
    	//当前的Server添加一个router,server注册的链接对应的处理业务
    	Router ziface.IRouter
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    一、实现思路

    在这里插入图片描述

    二、消息管理模块实现

    1 - 添加接口ziface/imsgHandler.go

    package ziface
    
    /*
     消息管理抽象层
    */
    type IMsgHandle interface {
    	// 调度/执行对应的Router消息处理方法
    	DoMsgHandler(request IRequest)
    	// 为消息添加具体的处理逻辑
    	AddRouter(msgID uint32, router IRouter)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2 - 实现接口znet/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!")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    三、Zinx集成消息管理模块

    1 - znet/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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • AddRouter方法修改成MsgHandler.AddRouter
    //路由功能:给当前的服务注册一个路由方法,供客户端的链接处理使用
    func (s *Server) AddRouter(msgID uint32, router ziface.IRouter) {
    	s.MsgHandler.AddRouter(msgID, router)
    	fmt.Println("Add Router Succ!!")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 初始化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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • Start中NewConnection调用的修改
    //启动服务器
    func (s *Server) Start() {
    	...
    	// 将处理新连接的业务方法 和 conn 进行绑定 得到我们的链接模块
    	dealConn := NewConnection(conn, cid, s.MsgHandler)
    	cid++
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2 - ziface/iserver.go

    • 修改对应的AddRouter定义
    package ziface
    
    //定义一个服务器接口
    type IServer interface {
    	//启动服务器
    	Start()
    	//停止服务器
    	Stop()
    	//运行服务器
    	Serve()
    	//路由功能:给当前的服务注册一个路由方法,供客户端的链接处理使用
    	AddRouter(uint32, IRouter)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3 - znet/connection.go

    • Connection结构体定义修改
    type Connection struct {
    	...
    	//消息的管理MsgID 和对应的处理业务API关系
    	MsgHandler ziface.IMsgHandle
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • StartReader方法修改
    //链接的读业务方法
    func (c *Connection) StartReader() {
    	...
    		//从路由中,找到注册绑定的Conn对应的router调用
    		//根据绑定好的MsgID 找到对应处理api业务 执行
    		go c.MsgHandler.DoMsgHandler(&req)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    四、测试类实现

    1 - 测试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()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    2 - 测试Client

    • 多Client:Client0.go —— 发送0消息;Client1 —— 发送1消息
    • Client0
    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)
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • Client1
    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)
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    3 - 测试截图

    在这里插入图片描述

    五、项目结构

    在这里插入图片描述

    六、完整源码

    点击下载zinxV0.6

  • 相关阅读:
    时间复杂度与空间复杂度
    Python | 数学计算那点事儿不完全总结 - 计算平均值、几何平均值等等
    真心求问:拼夕夕的链接到底是怎么搞的?能用其他大厂域名?
    公司专利技术交底书撰写及申请完全流程
    Springboot绿色生鲜5954z计算机毕业设计-课程设计-期末作业-毕设程序代做
    视频质量主观评价介绍
    EasyNVR平台级联到EasyCVR,视频播放一会就无法播放是什么原因?
    【轨迹跟踪】基于matlab拓展卡尔曼滤波时序四旋翼无人机状态跟踪【含Matlab源码 2246期】
    C# 结构体:定义、示例
    uniapp-vue3-vite 搭建小程序、H5 项目模板
  • 原文地址:https://blog.csdn.net/qq23001186/article/details/125009591