Zinx框架学习 - 链接封装与业务绑定

news2024/11/22 23:52:26

Zinx - V0.2 链接封装与业务绑定

  • 之前的v0.1版本,已经实现了一个基础的Server框架,现在需要对客户端链接和不同的客户端链接锁处理的不同业务再做一层接口封装
  • 在ziface下创建一个属于链接的接口文件iconnection.go,znet下创建文件connection.go实现链接接口

连接模块实现思路

代码实现

新增iconnection链接接口:定义链接的方法和处理连接绑定业务的方法

package ziface

import "net"

//定义链接模块的抽象层
type IConneciton interface {
	//启动链接 让当前的链接准备开始工作
	Start()

	//停止链接 结束当前链接的工作
	Stop()

	//获取当前链接的绑定socket conn
	GetTCPConnection() *net.TCPConn

	//获取当前链接模块的链接ID
	GetConnID() uint32

	//获取远程客户端的 TCP状态 IP port
	RemoteAddr() net.Addr

	//发送数据, 将数据发送给远程的客户端
	Send(data []byte) error
}

//定义一个处理链接业务的方法
type HandleFunc func(*net.TCPConn, []byte, int) error
  • 关于HandFunc的函数说明:type HandleFunc func(*net.TCPConn, []byte, int) error

        这个HandFunc是一个函数类型,是所有conn链接在处理业务的函数接口
                第一个参数:socket原生链接
                第二个参数:客户端请求的数据
                第三个参数:客户端请求的数据长度
        这样如果我们想要指定一个conn的处理业务,只需要定义一个HandFunc类型的函数,就可以和该链接绑定了

新增connection链接实现:实现链接

然后开始实现连接的方法和属性,提供一个初始化连接模块的方法NewConnection(),需要注意的是当框架启动时调用Start方法尝试开启两个Goroutine,分别是从客户端读的Goroutine和从客户端写的Goroutine,我们这里先把读写业务写在一个Goroutine中,后面再分开

package znet

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

//链接模块
type Connection struct {
	//当前链接的socket TCP套接字
	Conn *net.TCPConn

	//链接的ID
	ConnID uint32

	//当前的链接状态
	isClosed bool

	//当前链接所绑定的处理业务方法API
	handleAPI ziface.HandleFunc

	//告知当前链接已经退出的/停止 channel
	ExitChan chan bool
}

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

//链接的读业务方法
func (c *Connection) StartReader() {
	fmt.Println(" Reader Goroutine is running...")
	defer fmt.Println("connID = ", c.ConnID, " Reader is exit, remote addr is ", c.RemoteAddr().String())
	defer c.Stop()

	for {
		//读取客户端的数据到buf中, 最大512字节
		buf := make([]byte, 512)
		cnt, err := c.Conn.Read(buf)
		if err != nil {
			fmt.Println("recv buf err", err)
			continue
		}

		//调用当前链接所绑定的HandleAPI
		if err := c.handleAPI(c.Conn, buf, cnt); err != nil {
			fmt.Println("ConnID ", c.ConnID, " handle is error", err)
			break
		}
	}
}

//启动链接 让当前的链接准备开始工作
func (c *Connection) Start() {
	fmt.Println("Conn Start() ... ConnID = ", c.ConnID)
	//启动从当前链接的读数据的业务
	go c.StartReader()
	//TODO 启动从当前链接写数据的业务
}

//停止链接 结束当前链接的工作
func (c *Connection) Stop() {
	fmt.Println("Conn Stop().. ConnID = ", c.ConnID)

	//如果当前链接已经关闭
	if c.isClosed == true {
		return
	}
	c.isClosed = true

	//关闭socket链接
	c.Conn.Close()

	//回收资源
	close(c.ExitChan)
}

//获取当前链接的绑定socket conn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

//获取当前链接模块的链接ID
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

//获取远程客户端的 TCP状态 IP port
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

//发送数据, 将数据发送给远程的客户端
func (c *Connection) Send(data []byte) error {
	return nil
}

接下来就要将connection和server进行绑定,我们可以将server.go文件中Start()方法的第三步的conn句柄和封装好的connection模块进行绑定,我们可以将“已经与客户端建立连接,做一些业务,做一个最基本的最大的512字节长度的回显业务”这部分内存交给connection模块进行处理,在server.go提供一个HandlerFunc类型的callback_api函数CallBackToClient()方法,这个方法用来定义当前客户端链接的所绑定handle api(目前这个handle是写死的,以后优化应该由用户自定义handle方法)

		//3 阻塞的等待客户端链接,处理客户端链接业务(读写)
		for {
			//如果有客户端链接过来,阻塞会返回
			conn, err := listenner.AcceptTCP()
			if err != nil {
				fmt.Println("Accept err", err)
				continue
			}

			// 将处理新连接的业务方法 和 conn 进行绑定 得到我们的链接模块
			dealConn := NewConnection(conn, cid, CallBackToClient)
			cid++

			// 启动当前的链接业务处理
			go dealConn.Start()
		}
// 定义当前客户端链接的所绑定handle api(目前这个handle是写死的,以后优化应该由用户自定义handle方法)
func CallBackToClient(conn *net.TCPConn, data []byte, cnt int) error {
	//回显的业务
	fmt.Println("[Conn Handle] CallbackToClient ...")
	if _, err := conn.Write(data[:cnt]); err != nil {
		fmt.Println("write back buf err", err)
		return errors.New("CallBackToClient error")
	}
	return nil
}

整体思路

        我们在启动业务的时候调用server.go中的Start()方法,监听服务器地址,当客户端连接建立成功后会得到conn句柄,通过conn句柄和CallBackToClient()方法绑定在一起得到一个dealconn,然后开启一个Goroutine调用connection模块的Start()方法,开启StartReader()协程,这个协程首先会从客户端读512字节的数据,然后调用handleAPI处理数据,即在客户端定义好的CallBackToClient()方法

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

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

相关文章

异步利刃CompletableFuture

什么是CompletableFuture? CompletableFuture 类实现了 Future 和 CompletionStage 接口并且新增了许多方法,它支持 lambda,通过回调利用非阻塞方法,提升了异步编程模型。简单来说可以帮我们实现任务编排。【文中所有代码已上传码云】 Com…

程序员必修必炼的设计模式之工厂模式

本文首发自「慕课网」(www.imooc.com),想了解更多IT干货内容,程序员圈内热闻,欢迎关注"慕课网"或慕课网公众号! 作者:李一鸣 | 慕课网讲师 工厂模式是平时开发过程中最常见的设计模式…

15.3:最多做K个项目,初始资金是W,返回最大资金

输入正数数组costs、正数数组profits、正数K和正数M costs[i]表示i号项目的花费 profits[i]表示i号项目在扣除花费之后还能挣到的钱(利润) K表示你只能串行的最多做k个项目 M表示你初始的资金 说明:每做完一个项目,马上获得的收益,可以支持你…

FP独立站支付渠道市场逐渐向好!信用卡和AB轮询哪个好?

之前一篇文章写过品牌方使用ChatGPT技术检测FP网站,对FP独立站的收款起到了很大的影响。今天是6月的第一天,我为各位带来了一个好消息!那就是在过去的3-5月份,信用卡收款实行整顿,目前支付渠道都有所松动。例如&#x…

好孩子福利|Sup游戏机,一秒回到童年

这份六一礼物对儿童来说有点幼稚,但对程序员刚刚好~ ​ Sup 游戏机,一秒回到童年! 到底有多好玩呢?可以参考 B 站试玩视频! 太火鸟好物推荐——掌上游戏机sup 参加流程: STEP 1:扫…

以太网——MDIO(SMI)接口的FPGA实现

在 MAC 与 PHY 之间,有一个配置接口,即 MDIO(也称 SMI,Serial Management Interface),可以配置 PHY 的工作模式、获取 PHY 芯片的工作状态等。本文以 PHY 芯片 B50610 为例,实现 MDIO 接口&…

NUC972 Linux学习 NAND FLASH 制作系统

设备:NUC972DF61YC 使用的虚拟机环境:官方提供的NUC972DF61YC - Nuvoton 板载NAND FLASH,前期主要学习怎么uboot、ubootspl、uimage、env烧录。官方配置没有使用rootfs在flash中,所以数据会掉电丢失。即文件系统在RAM中。 这里仅…

基于Jackson实现API接口数据脱敏

一、背景 用户的一些敏感数据,例如手机号、邮箱、身份证等信息,在数据库以明文存储(加密存储见《基于Mybatis-Plus拦截器实现MySQL数据加解密》), 但在接口返回数据给浏览器(或三方客户端)时&a…

设计一个支持并发的前端接口缓存

目录​​​​​​​ 缓存池 并发缓存 问题 思考 优化🤔 总结 最后 缓存池 缓存池不过就是一个map,存储接口数据的地方,将接口的路径和参数拼到一块作为key,数据作为value存起来罢了,这个咱谁都会。 const cach…

DTU和MQTT网关优缺点

目前市面上有两种设备实现Modbus转MQTT网关。网关式、DTU式。 钡铼技术网关内部进行转换 网关式 优点: 1、通讯模块和MCU分开,通讯模块只做通讯功能,协议转换有单独主控MCU,“硬转换”; 2、数据点是通过映射到主控…

【严重】GitLab 存在代码执行漏洞

漏洞描述 GitLab 是一款基于Git的代码托管、版本控制、协作开发平台。 GitLab CE/EE 15.4 至 15.9.6 版本,15.10 至 15.10.5 版本和 15.11 至 15.11.1 版本存在代码执行漏洞。在某些条件下,实例上的任何GitLab用户都可以使用GraphQL端点将恶意运行程序…

HTML框架-----标签(下)

目录 前言: 5.容器标签 效果:​编辑 6.列表标签 (1)无序 (2)有序 7.图片标签 8.超链接标签 (1)链接资源 (2)超链接锚点 前言: 今天我们接着来继续学习html的标签&am…

五重要性能测试指标揭秘!并发数、TPS、QPS、响应时间和资源利用率,了解性能瓶颈,优化系统高负载下的处理能力

目录 前言: 1. 并发数 2. TPS 3. QPS 4. 响应时间 5. 资源利用率 总结 前言: 在高并发的场景下,我们需要考虑如何优化我们的应用程序,以确保它可以承受大量的请求并且在给定时间内响应。对于这个问题,性能测试就…

字节码文件结构

目录 1、概述 2、JVM的两个无关性 3、Class字节码文件的结构 1、基本存储单位 2、字节码文件数据结构 3、Class文件格式 4、魔数与Class文件的版本 5、常量池 6、访问标志 7、类索引、父类索引与接口索引集合 8、字段表集合 9、方法表集合 10、属性表集合 11、总…

centos7.9升级rockylinux8.8

前言 查看centos的版本 ,我这台服务器是虚拟机,下面都是模拟实验 升级前一定要把服务器上配置文件,数据等进行备份 [rootlocalhost ~]#cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootlocalhost ~]#uname -a Linux jenkins_ser…

Ubuntu常见基本问题

系列文章目录 文章目录 系列文章目录一、复制粘贴问题二、无法全屏问题三、设置为中文四、时间同步问题1、选择时区2、同步网络时间 一、复制粘贴问题 开启终端:ctrlaltt卸载已有工具 sudo apt-get autoremove open-vm-tools安装工具open-vm-tools sudo apt-get …

echarts的y轴数据显示过长占不下,内容截取,鼠标hover上去显示全部

初始效果: 优化后的效果: 优化点:控制了y轴显示字数,鼠标hover上去显示全部 主要实现思路参考了这位同学的文章:https://www.cnblogs.com/liuboren/p/9040622.html 我是用vue实现的,因为我需要一个页面中…

各算法/协议知识理论笔记(fpga)

一、利用fifo对3行数据求和 需要2个fifo保存第0行和第1行的数据,如下图 比如有20行数据,则将一行一行的输给fifo2, fifo2出来的数据再给fifo1.当fifo和fifo1有数据时,在准备给 fifo2输入新的一行数据时,同时读出fifo2,…

Linux进程间通信(信号)

信号发送 信号是 Linux 系统响应某些条件而产生的一个事件,接收到该信号的进程会执行相应的操作。 信号的产生有三种方式: (1)由硬件产生,如从键盘输入 CtrlC 可以终止当前进程 (2)由其他进程发送,如可在 …

PostgreSQL修炼之道之高可用性方案设计(十七)

目录 20 高可用性方案设计(二) 20.2 基于共享存储的高可用方案 20.2.1 SAN存储的方案 20.2.2 DRBD的方案 20.3 WAL日志同步或流复制同步的方案 20.3.1 持续复制归档的standby的方法 20.3.2 异步流复制的方案 20.3.3 基于同步流复制方案 20.4 基于…