当前位置: 首页 > news >正文

【Zinx】Day5-Part4:Zinx 的连接属性设置

目录

  • Day5-Part4:Zinx 的连接属性设置
    • 给连接添加连接配置的接口
    • 连接属性方法的实现
  • 测试 Zinx-v1.0
  • 总结

Day5-Part4:Zinx 的连接属性设置

在 Zinx 当中,我们使用 Server 来开启服务并监听指定的端口,当接收到来自客户端的连接请求之后,Zinx 新建 Connection 来管理与 client 的连接,Connection 负责读取 client 发送给 Server 的数据(当然,需要拆包),通过 Router 进行业务处理,可能需要通过 Writer 将数据写回给 client,在这个过程中,client 可能有一些用户的数据或者参数,可以与 Connection 相绑定。

根据上述分析,我们现在将连接属性的绑定集成到 Zinx 框架当中。

给连接添加连接配置的接口

首先修改 iconnection.go 下的 IConnection 接口:

type IConnection interface {Start()                                      // 启动连接Stop()                                       // 停止连接GetConnID() uint32                           // 获取远程客户端地址信息GetTCPConnection() *net.TCPConn              // 从当前连接获取原始的 socket TCPConnRemoteAddr() net.Addr                        // 获取远程客户端地址信息SendMsg(msgId uint32, data []byte) error     // 直接将 Message 数据发给远程的 TCP 客户端SendBuffMsg(msgId uint32, data []byte) error // 添加带缓冲的发送消息接口SetProperty(key string, value interface{})   // 设置连接属性GetProperty(key string) (interface{}, error) // 获取连接属性RemoveProperty(key string)                   // 移除连接属性
}

新增了三个方法,分别是 SetProperty、GetProperty 和 RemoveProperty,分别用于添加、获取和移除属性。

连接属性方法的实现

现在将属性加入到 Connection 结构当中,并实现 IConnection 新增的方法:

type Connection struct {TCPServer    ziface.IServer    // 标记当前 Conn 属于哪个 ServerConn         *net.TCPConn      // 当前连接的 socket TCP 套接字ConnID       uint32            // 当前连接的 ID, 也可称为 SessionID, 全局唯一isClosed     bool              // 当前连接的开启/关闭状态Msghandler   ziface.IMsgHandle // 将 Router 替换为消息管理模块ExitBuffChan chan bool         // 告知该连接一经退出/停止的 channelmsgChan      chan []byte       // 无缓冲 channel, 用于读/写两个 goroutine 之间的消息通信msgBuffChan  chan []byte       // 定义 msgBuffChanproperty     map[string]interface{} // 连接属性propertyLock sync.RWMutex           // 保护连接属性修改的锁
}// NewConnection 创建新的连接
func NewConnection(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {c := &Connection{TCPServer:    server,Conn:         conn,ConnID:       connID,isClosed:     false,Msghandler:   msgHandler,ExitBuffChan: make(chan bool, 1),msgChan:      make(chan []byte), // msgChan 初始化msgBuffChan:  make(chan []byte, settings.Conf.MaxMsgChanLen),property:     make(map[string]interface{}),}// 将新创建的 Conn 添加到连接管理器中c.TCPServer.GetConnMgr().Add(c)return c
}// SetProperty 用于设置连接属性
func (c *Connection) SetProperty(key string, value interface{}) {c.propertyLock.Lock()defer c.propertyLock.Unlock()c.property[key] = value
}// GetProperty 获取连接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {c.propertyLock.RLock()defer c.propertyLock.RUnlock()if value, ok := c.property[key]; ok {return value, nil} else {return nil, errors.New("No property found")}
}// RemoveProperty 移除连接属性
func (c *Connection) RemoveProperty(key string) {c.propertyLock.Lock()defer c.propertyLock.Unlock()delete(c.property, key)
}

测试 Zinx-v1.0

现在我们正式完成了 Zinx-v1.0,开两个 main 函数,一个模拟 Server 一个模拟 Client,来对 Zinx 进行测试:

// zinx/server/main.go
package mainimport ("fmt""zinx/settings""zinx/ziface""zinx/znet"
)// ping test 自定义路由
type PingRouter struct {znet.BaseRouter
}// Ping Handle
func (this *PingRouter) Handle(request ziface.IRequest) {fmt.Println("Call PingRouter Handle")//先读取客户端的数据,再回写ping...ping...pingfmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))err := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping"))if err != nil {fmt.Println(err)}
}type HelloZinxRouter struct {znet.BaseRouter
}// HelloZinxRouter Handle
func (this *HelloZinxRouter) Handle(request ziface.IRequest) {fmt.Println("Call HelloZinxRouter Handle")//先读取客户端的数据,再回写ping...ping...pingfmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))err := request.GetConnection().SendBuffMsg(1, []byte("Hello Zinx Router V0.10"))if err != nil {fmt.Println(err)}
}// 创建连接的时候执行
func DoConnectionBegin(conn ziface.IConnection) {fmt.Println("DoConnecionBegin is Called ... ")//=============设置两个链接属性,在连接创建之后===========fmt.Println("Set conn Name, Home done!")conn.SetProperty("Name", "yggp")conn.SetProperty("Home", "Learning Zinx")//===================================================err := conn.SendMsg(2, []byte("DoConnection BEGIN..."))if err != nil {fmt.Println(err)}
}// 连接断开的时候执行
func DoConnectionLost(conn ziface.IConnection) {//============在连接销毁之前,查询conn的Name,Home属性=====if name, err := conn.GetProperty("Name"); err == nil {fmt.Println("Conn Property Name = ", name)}if home, err := conn.GetProperty("Home"); err == nil {fmt.Println("Conn Property Home = ", home)}//===================================================fmt.Println("DoConnectionLost is Called ... ")
}func main() {//创建一个server句柄err := settings.Init()if err != nil {panic(err)}s := znet.NewServer()//注册链接hook回调函数s.SetOnConnStart(DoConnectionBegin)s.SetOnConnStop(DoConnectionLost)//配置路由s.AddRouter(0, &PingRouter{})s.AddRouter(1, &HelloZinxRouter{})//开启服务s.Serve()
}
// zinx/client/main.go
package mainimport ("fmt""io""net""time""zinx/znet"
)/*
模拟客户端
*/
func main() {fmt.Println("Client Test ... start")//3秒之后发起测试请求,给服务端开启服务的机会time.Sleep(3 * time.Second)conn, err := net.Dial("tcp", "127.0.0.1:7777")if err != nil {fmt.Println("client start err, exit!")return}for {//发封包message消息dp := znet.NewDataPack()msg, _ := dp.Pack(znet.NewMsgPackage(1, []byte("Zinx V1.0 Client1 Test Message")))_, err := conn.Write(msg)if err != nil {fmt.Println("write error err ", err)return}//先读出流中的head部分headData := make([]byte, dp.GetHeadLen())_, err = io.ReadFull(conn, headData) //ReadFull 会把msg填充满为止if err != nil {fmt.Println("read head error")break}//将headData字节流 拆包到msg中msgHead, err := dp.Unpack(headData)if err != nil {fmt.Println("server unpack err:", err)return}if msgHead.GetDataLen() > 0 {//msg 是有data数据的,需要再次读取data数据msg := msgHead.(*znet.Message)msg.Data = make([]byte, msg.GetDataLen())//根据dataLen从io中读取字节流_, err := io.ReadFull(conn, msg.Data)if err != nil {fmt.Println("server unpack data err:", err)return}fmt.Println("==> Recv Msg: ID=", msg.Id, ", len=", msg.DataLen, ", data=", string(msg.Data))}time.Sleep(1 * time.Second)}
}

Server 的 main.go 在执行过程中,Terminal 显示:

Add api msgId =  0
Add Router succ! msgId =  0
Add api msgId =  1
Add Router succ! msgId =  1
[START] Server listenner at IP: 127.0.0.1, Port 7777, is starting
[Zinx] Version: v1.0, MaxConn: 3, MaxPacketSize: 0
Worker ID =  0  is started.
Worker ID =  4  is started.
Worker ID =  5  is started.
Worker ID =  1  is started.
Worker ID =  6  is started.
Worker ID =  2  is started.
Worker ID =  3  is started.
Worker ID =  7  is started.
Worker ID =  8  is started.
Worker ID =  9  is started.
start Zinx server   zinx server  succ, now listenning...
connection add to ConnManager successfully: conn num =  1
---> CallOnConnStart ...
DoConnecionBegin is Called ...
Set conn Name, Home done!
[Writer Goroutine is running]
Reader Goroutine is running
Add ConnID =  0  request msgID =  1 to workerID =  0
Call HelloZinxRouter Handle
recv from client : msgId= 1 , data= Zinx V1.0 Client1 Test Message
Add ConnID =  0  request msgID =  1 to workerID =  0
Call HelloZinxRouter Handle
recv from client : msgId= 1 , data= Zinx V1.0 Client1 Test Message
Add ConnID =  0  request msgID =  1 to workerID =  0
Call HelloZinxRouter Handle
recv from client : msgId= 1 , data= Zinx V1.0 Client1 Test Message
Add ConnID =  0  request msgID =  1 to workerID =  0
Call HelloZinxRouter Handle
recv from client : msgId= 1 , data= Zinx V1.0 Client1 Test Message
Add ConnID =  0  request msgID =  1 to workerID =  0
... ... ...

当客户端退出时,显示:

... ... ...
recv from client : msgId= 1 , data= Zinx V1.0 Client1 Test Message
read msg head error read tcp4 127.0.0.1:7777->127.0.0.1:56961: wsarecv: An existing connection was forcibly closed by the remote host.
Conn Stop()... ConnID =  0
---> CallOnConnStop ...
Conn Property Name =  yggp
Conn Property Home =  Learning Zinx
DoConnectionLost is Called ...
connection Remove connID =  0  successfully: conn num =  0
127.0.0.1:56961  conn reader exit !
127.0.0.1:56961 [conn Writer exit!]

证明 hook 函数被成功调用,且可以成功读取 Connection 保存的 property。

Client 的 main.go 在 Terminal 显示:

Client Test ... start
==> Recv Msg: ID= 2 , len= 21 , data= DoConnection BEGIN...
==> Recv Msg: ID= 1 , len= 23 , data= Hello Zinx Router V0.10
==> Recv Msg: ID= 1 , len= 23 , data= Hello Zinx Router V0.10
==> Recv Msg: ID= 1 , len= 23 , data= Hello Zinx Router V0.10
==> Recv Msg: ID= 1 , len= 23 , data= Hello Zinx Router V0.10
==> Recv Msg: ID= 1 , len= 23 , data= Hello Zinx Router V0.10
... ... ...

至此,我们已经成功完成了 Zinx 项目的基本框架。

总结

前前后后花了 5 天时间来完成 Zinx-v1.0 框架,收获颇丰。Zinx 是一个轻量级的面向长连接的并发服务器框架,v1.0 版本只是它的一个基本雏形,在具备所有功能的基础上,仍然可以进一步升级。明天我会从头到尾对 Zinx 框架进行总结,并根据目前 github 上最新的 Zinx 项目谈一谈我未来会进一步做哪些改动,欢迎持续关注。

相关文章:

【Zinx】Day5-Part4:Zinx 的连接属性设置

目录 Day5-Part4:Zinx 的连接属性设置给连接添加连接配置的接口连接属性方法的实现 测试 Zinx-v1.0总结 Day5-Part4:Zinx 的连接属性设置 在 Zinx 当中,我们使用 Server 来开启服务并监听指定的端口,当接收到来自客户端的连接请求…...

【英伟达AI论文】多模态大型语言模型的高效长视频理解

摘要:近年来,基于视频的多模态大型语言模型(Video-LLMs)通过将视频处理为图像帧序列,显著提升了视频理解能力。然而,许多现有方法在视觉主干网络中独立处理各帧,缺乏显式的时序建模,…...

小程序事件系统 —— 32 事件系统 - 事件分类以及阻止事件冒泡

在微信小程序中,事件分为 冒泡事件 和 非冒泡事件 : 冒泡事件:当一个组件的事件被触发后,该事件会向父节点传递;(如果父节点中也绑定了一个事件,父节点事件也会被触发,也就是说子组…...

全球首款 5G-A 人形机器人发布

全球首款 5G-A 人形机器人于世界移动通信大会(MWC2025)上由中国移动、华为、乐聚联合发布。以下是关于这款机器人的详细介绍: 名称与背景 名称9:这款人形机器人名为 “夸父”,是中国移动、华为与乐聚机器人在 GTI 平台…...

Tomcat 新手入门指南

Tomcat 新手入门指南 Apache Tomcat 是一个开源的 Java Servlet 容器和 Web 服务器,广泛用于部署和运行 Java Web 应用程序。以下是 Tomcat 的入门指南,帮助你快速上手。 1. 安装 Tomcat 步骤 1: 下载 Tomcat 访问 Apache Tomcat 官网。选择适合的版…...

Flink-DataStreamAPI-生成水印

下面我们将学习Flink提供的用于处理事件时间戳和水印的API,也会介绍有关事件时间、流转时长和摄取时间,下面就让我们跟着官网来学习吧 一、水印策略介绍 为了处理事件时间,Flink需要知道事件时间戳,这意味着流中的每个元素都需要…...

【单片机】ARM 处理器简介

ARM 公司简介 ARM(Advanced RISC Machine) 是英国 ARM 公司(原 Acorn RISC Machine) 开发的一种精简指令集(RISC) 处理器架构。ARM 处理器因其低功耗、高性能、广泛适用性,成为嵌入式系统、移动…...

Flutter——最详细原生交互(MethodChannel、EventChannel、BasicMessageChannel)使用教程

MethodChannel(方法通道) 用途:实现 双向通信,用于调用原生平台提供的 API 并获取返回结果。 场景:适合一次性操作,如调用相机、获取设备信息等。 使用步骤: Flutter 端:通过 Meth…...

Kafka常用指令(详细)

Kafka常用指令(详细) 启停命令 前台启动 前台启动命令 ./bin/kafka-server-start.sh config/server.properties 后台启动方式1 后台启动命令加上参数-daemon,窗口关闭之后kafka后台程序继续运行 ./bin/kafka-server-start.sh -daemon co…...

供应链工作效率如何提升

提升供应链工作效率可以从以下几个关键方面入手: 1. 优化供应链管理 数据驱动决策:利用AI和大数据分析,提高预测准确性,优化库存管理。供应链可视化:采用ERP(企业资源计划)和SCM(供…...

1-001:MySQL的存储引擎有哪些?它们之间有什么区别?

MySQL 存储引擎 ├── InnoDB(默认引擎) │ ├── 事务支持:支持 ACID 和事务(事务日志、回滚、崩溃恢复) │ ├── 锁机制:支持行级锁,提高并发性能 │ ├── 外键支持:支持外键…...

Java/Kotlin逆向基础与Smali语法精解

1. 法律警示与道德边界 1.1 司法判例深度剖析 案例一:2021年某游戏外挂团伙刑事案 犯罪手法:逆向《王者荣耀》通信协议,修改战斗数据包 技术细节:Hook libil2cpp.so的SendPacket函数 量刑依据:非法经营罪&#xff…...

用AI学编程4——swift学习1

一个swift文件, 整理出swift从入门到精通的所有知识点, 给出注释, 给出这样的swift文件 以下是一个整合了 Swift 从入门到精通的主要知识点的 Swift 文件,包含了基础语法、面向对象编程、异常处理、集合框架、多线程、Lambda 表达…...

数据集构建与训练前准备

训练数据集目录结构与格式 作者笨蛋学法,先将其公式化,后面逐步自己进行修改,读者觉得看不懂可以理解成,由结果去推过程,下面的这个yaml文件就是结果,我们去推需要的文件夹(名字可以不固定,但是…...

在大型语言模型的提示词设计中,system、user和assistant三个角色的区别与联系

在大型语言模型的提示词设计中,system、user和assistant三个角色承担不同的功能,其区别与联系如下: 1. 角色定义与功能 system(系统指令) 作用:设定模型的整体行为、角色定位和任务框架。例如,“你是一位专业的科技作家”或“仅回答与医疗相关的问题”。特点:在多轮对话…...

Zabbix监控进程报警(Zabbix Monitoring Process Alarm)

zabbix监控进程占cpu、内存、磁盘RAID情况 1、cpu达到90%时报警 名称: cpu user percent gt 90% 表达式&#xff1a;{Template OS Linux:system.cpu.util[,idle].avg(1m)}<10 2、内存达到80%时报警 配置—主机(选择监控主机)—监控项—创建监控项 1、创建监控项 名称&…...

p5.js:sound(音乐)可视化,动画显示音频高低变化

本文通过4个案例介绍了使用 p5.js 进行音乐可视化的实践&#xff0c;包括将音频振幅转化为图形、生成波形图。 承上一篇&#xff1a;vite&#xff1a;初学 p5.js demo 画圆圈 cd p5-demo copy .\node_modules\p5\lib\p5.min.js . copy .\node_modules\p5\lib\addons\p5.soun…...

HAL库常用函数

一、通用函数 系统初始化&#xff1a; HAL_Init(): 初始化HAL库和系统时钟&#xff08;调用前需配置系统时钟源&#xff09;。 HAL_Delay(uint32_t Delay): 毫秒级阻塞延时&#xff08;基于SysTick定时器&#xff09;。 HAL_GetTick(): 获取系统运行时间&#xff08;毫秒计数…...

【Zinx】Day5-Part3:Zinx 的连接管理

目录 Day5-Part3&#xff1a;Zinx 的连接管理创建连接管理模块将连接管理模块集成到 Zinx 当中将 ConnManager 集成到 Server 当中在 Connection 的工厂函数中将连接添加到 ConnManagerServer 中连接数量的判断连接的删除 补充&#xff1a;连接的带缓冲发包方式补充&#xff1a…...

C语言:6.20字符型数据练习题

编写程序,输人一行数字字符(用回车结束),每个数字字符 的前后都有空格。 把这一行中的数字转换成一个整数。 例如,若输入(<CR>代表 Enter键):2 4 8 3<CR>则输出 整数:2483。 #include <stdio.h>int main() {char ch;int number 0;printf("请输入一行…...

SpringBoot Test详解

目录 spring-boot-starter-test 1、概述2、常用注解 2.1、配置类型的注解2.2、Mock类型的注解2.3、自动配置类型的注解2.4、启动测试类型的注解2.5、相似注解的区别和联系 3、SpringBootTest和Junit的使用 3.1、单元测试3.2、集成测试 4、MockMvc 4.1、简单示例4.2、自动配置4…...

CDefView::_GetPIDL函数分析之ListView_GetItem函数的参数item的item.mask 为LVIF_PARAM

CDefView::_GetPIDL函数分析之ListView_GetItem函数的参数item的item.mask 为LVIF_PARAM 第一部分&#xff1a; 1: kd> t SHELL32!CDefView::_GetPIDL: 001b:77308013 55 push ebp 1: kd> dv this 0x00000015 i 0n21 …...

Android Retrofit 框架注解定义与解析模块深度剖析(一)

一、引言 在现代 Android 和 Java 开发中&#xff0c;网络请求是不可或缺的一部分。Retrofit 作为 Square 公司开源的一款强大的类型安全的 HTTP 客户端&#xff0c;凭借其简洁易用的 API 和高效的性能&#xff0c;在开发者社区中广受欢迎。Retrofit 的核心特性之一便是通过注…...

项目上传到Gitee过程

在gitee上新建一个仓库 点击“克隆/下载”获取仓库地址 电脑上要装好git 在电脑本地文件夹右键“Git Bash Here” 依次执行如下命令 git init git remote add origin https://gitee.com/qlexcel/stm32-simple.git git pull origin master git add . git commit -m ‘init’…...

DeepSeek R1在医学领域的应用与技术分析(Discuss V1版)

DeepSeek R1作为一款高性能、低成本的国产开源大模型,正在深刻重塑医学软件工程的开发逻辑与应用场景。其技术特性,如混合专家架构(MoE)和参数高效微调(PEFT),与医疗行业的实际需求紧密结合,推动医疗AI从“技术驱动”向“场景驱动”转型。以下从具体业务领域需求出发,…...

数学之快速幂-数的幂次

题目描述 给定三个正整数 N,M,P&#xff0c;求 输入描述 第 1 行为一个整数 T&#xff0c;表示测试数据数量。 接下来的 T 行每行包含三个正整数 N,M,P。 输出描述 输出共 T 行&#xff0c;每行包含一个整数&#xff0c;表示答案。 输入输出样例 示例 1 输入 3 2 3 7 4…...

git subtree管理的仓库怎么删除子仓库

要删除通过 git subtree 管理的子仓库&#xff0c;可以按照以下步骤操作&#xff1a; 1. 确认子仓库路径 首先确认要删除的子仓库的路径&#xff0c;假设子仓库路径为 <subtree-path>。 2. 从主仓库中移除子仓库目录 使用 git rm 命令删除子仓库的目录&#xff1a; …...

学习资料电子版 免费下载的网盘网站(非常全!)

我分享一个私人收藏的电子书免费下载的网盘网站&#xff08;学习资料为主&#xff09;&#xff1a; link3.cc/sbook123 所有资料都保存在网盘了&#xff0c;直接转存即可&#xff0c;非常的便利&#xff01; 包括了少儿&#xff0c;小学&#xff0c;初中&#xff0c;中职&am…...

SpringMVC-全局异常处理

文章目录 1. 全局异常处理2. 项目异常处理方案2.1 异常分类2.2 异常解决方案2.3 异常解决方案具体实现 1. 全局异常处理 问题&#xff1a;当我们在SpingMVC代码中没有对异常进行处理时&#xff0c;三层架构的默认处理异常方案是将异常抛给上级调用者。也就是说Mapper层报错会将…...

基于Spring Boot的宠物健康顾问系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...