Zinx框架学习 - 构建最基础的Server

news2024/11/14 19:41:53

Zinx - V0.1 构建最基础的Server

Zinx的框架结构:

整体思路:

客户端发送请求到服务器端,服务端会有一个Goroutine专门处理listenner和监听这个过程,然后有客户端连接过来之后会启动一个客户端处理Goroutine,这个Goroutine会分别开启一个客户端读写Goroutine,Reader是用来从客户端把数据读到已经开辟好的工作池中,工作池处理业务会针对用户自已经注册好的自定义好的业务来取一些业务规则来去处理数据,当数据处理完之后会将数据再交给Write Handler回写给客户端

Zinx 的最基本的两个模块ziface和zent:

  • ziface主要是存放一些Zinx框架的全部模块的抽象层接口类

Zinx 框架的最基本的服务类接口iserver,定义在该模块中

  • znet模块是Zinx框架中网络相关功能的实现,所有网络相关模块定义在这里

基础的Server模块拥有的三个方法和相应属性

代码实现

在iserver.go中定义一个服务器接口,提供三个方法

package ziface

//定义一个服务器接口
type IServer interface {
	//启动服务器
	Start()
	//停止服务器
	Stop()
	//运行服务器
	Serve()
}

然后再server.go中将该接口实例化,定义一个Server结构体对象,继承服务器接口即实现接口中的方法

实现接口中的方法前我们需要提供一个初始化Server对象的方法NewServer(),该方法返回的是一个抽象层iserver,在该方法中创建一个server对象并将其返回

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

首先实现Serve方法,在其中一定会调用Start方法将其服务器启动,所以先要实现Start方法,开发一个单体服务器有以下三个步骤

  • 获取一个TCP的Addr,即创建一个套接字
  • 监听服务器的地址
  • 监听成功后阻塞的等待客户端链接,处理客户端链接业务(读写)

其中第三步中我们使用for循环不断遍历等待listenner.AcceptTCP()的返回,如果没有客户端连接就会一直阻塞,如果有连接过来就会返回一个conn句柄和客户端进行基本的读写操作

已经有客户端建立连接后,我们就可以起一个协程去承载一些业务,在这里做一个最基本的最大512字节长度的显业务,在这个协程中不断地从客户端获取数据,所以也要用一个for循环

// 1 获取一个TCP的Addr
addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))
if err != nil {
	fmt.Println("resolve tcp addt error : ", err)
	return
}

//2 监听服务器的地址
listenner, err := net.ListenTCP(s.IPVersion, addr)
if err != nil {
	fmt.Println("listen ", s.IPVersion, " err ", err)
	return
}

fmt.Println("start Zinx server succ, ", s.Name, " succ, Listenning...")

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

	//已经与客户端建立间接,做一些业务, 做一个最基本的最大512字节长度的回显业务
	go func() {
		for {
			buf := make([]byte, 512)
			cnt, err := conn.Read(buf)
			if err != nil {
				fmt.Println("recv buf err ", err)
				continue
			}

			fmt.Printf("recv client buf %s, cnt %d\n", buf, cnt)
			//回显功能
			if _, err := conn.Write(buf[:cnt]); err != nil {
				fmt.Println("write back buf err ", err)
				continue
			}
		}
	}()
}

但如果这样写,这个等待客户端连接listenner.AcceptTCP()的过程会阻塞,所以我们可以将整个Start业务用go func()来承载

然后再回到Serve()方法中,因为Start()本身是异步的,如果不在Serve()方法中进行阻塞,主线程在调用Start()后就会结束,服务器就没了,所以我们需要select阻塞

//运行服务器
func (s *Server) Serve() {
	//启动server的服务功能
	s.Start()

	//TODO 做一些启动服务器之后的额外业务

	//阻塞状态
	select {}
}

server.go完整代码

package znet

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

//iServer的接口实现,定义一个Server的服务器模块
type Server struct {
	//服务器的名称
	Name string
	//服务器绑定的ip版本
	IPVersion string
	//服务器监听的IP
	IP string
	//服务器监听的端口
	Port int
}

//启动服务器
func (s *Server) Start() {
	fmt.Printf("[Start] Server Listenner at IP :%s, Port %d, is starting\n", s.IP, s.Port)

	go func() {
		// 1 获取一个TCP的Addr
		addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))
		if err != nil {
			fmt.Println("resolve tcp addt error : ", err)
			return
		}

		//2 监听服务器的地址
		listenner, err := net.ListenTCP(s.IPVersion, addr)
		if err != nil {
			fmt.Println("listen ", s.IPVersion, " err ", err)
			return
		}

		fmt.Println("start Zinx server succ, ", s.Name, " succ, Listenning...")

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

			//已经与客户端建立间接,做一些业务, 做一个最基本的最大512字节长度的回显业务
			go func() {
				for {
					buf := make([]byte, 512)
					cnt, err := conn.Read(buf)
					if err != nil {
						fmt.Println("recv buf err ", err)
						continue
					}

					fmt.Printf("recv client buf %s, cnt %d\n", buf, cnt)
					//回显功能
					if _, err := conn.Write(buf[:cnt]); err != nil {
						fmt.Println("write back buf err ", err)
						continue
					}
				}
			}()
		}
	}()
}

//停止服务器
func (s *Server) Stop() {
	//TODO 将一些服务器的资源、状态或者一些已经开辟的链接信息 进行停止或者回收
}

//运行服务器
func (s *Server) Serve() {
	//启动server的服务功能
	s.Start()

	//TODO 做一些启动服务器之后的额外业务

	//阻塞状态
	select {}
}

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

服务端测试

为了我们这个框架能够边写边使用,我们创建一个myDemo模块来测试Zinx框架,其中创建一个Server.go文件,该文件是基于Zinx框架开发的服务器端应用程序,在main函数中创建一个server句柄,使用Zinx的api,然后启动server

package main

import "zinx/znet"

//基于Zinx框架来开发的 服务器端应用程序
func main() {
	//1 创建一个server句柄,使用Zinx的api
	s := znet.NewServer("[zinx V0.1]")
	//2 启动server
	s.Serve()
}

客户端测试

  • 直接链接远程服务器,得到一个conn链接
  • 链接调用Write 写数据(for循环不断写),(服务器有将字符串返回的功能)

需要注意的是,我们要在客户端的for循环中将cpu阻塞,否则会将cpu性能跑满

package main

import (
	"fmt"
	"net"
	"time"
)

//模拟客户端
func main() {
	fmt.Println("client 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 {
		//2 链接调用Write 写数据
		_, err := conn.Write([]byte("Hello Zinx V0.1.."))
		if err != nil {
			fmt.Println("write conn err", err)
			return
		}

		buf := make([]byte, 512)
		cnt, err := conn.Read(buf)
		if err != nil {
			fmt.Println("read buf error")
			return
		}

		fmt.Printf(" server call back: %s, cnt = %d\n", buf, cnt)

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

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

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

相关文章

深度学习 - 52.推荐场景的多样性与 MMR [Maximal Marginal Relevance] 简介与 Python 实现

目录 一.引言 二.多样性 三.MMR 流程 1.标准 MMR 2.窗口 MMR 四.基于向量内积相似度的 MMR Python 实现 1.模拟用户 rank 结果 2.向量内积计算 MRi 2.1 获取向量计算 max sim 2.2 argmax 获取最优 MRi item 3.MMR 测试 4.MMR 完整代码 五.总结 一.引言 MMR - Ma…

记一次k8s节点上出现node.kubernetes.io/disk-pressure污点的问题

目录 问题描述 原因分析: 解决方案: 其他问题 问题描述 k8s部署时pod一直属于Pending状态,也就是说pod未调度到k8s节点上 原因分析: 通过以下命令查看下pod kubectl get pod 以上命令可以看到各个pod的状态&#xff0c…

【商城后台管理系统】项目初始化(UmiJS)

目录 一、运行时配置 1.1 配置方式 1.2 常用配置项 1.3 关于运行时配置说明 二、使用Umi UI 2.1 Umi UI的特性 2.2 项目中安装Umi UI 2.3 使用Umi UI 三、Umi JS总结 3.1 路由状态管理 3.2 配置代理 3.3 封装requset 一、运行时配置 运行时配置和配置的区别是他跑…

Geohash算法原理及实现

最近需要实现一个功能,查找车辆附近的加油站,如果车和加油站距离在200米以内,则查找成功。 加油站数量肯定不小,能否缩小查找范围,否则以遍历形式,效率肯定高不了。 Geohash算法就是将经纬度编码&#xf…

又名管道和无名管道

一、进程间通信(IPC,InterProcess Communication) 概念:就是进程和进程之间交换信息。 常用通信方式 无名管道(pipe) 有名管道 (fifo) 信号(signal) 共…

数字化时代,低代码+进销存管理系统让你省时省力

进销存系统是一种用于管理企业物资流动和库存的软件系统,可以帮助企业优化物资管理过程,提高效率,减少成本,从而提升企业的盈利能力。本文将详细介绍进销存系统的定义、功能、好处以及如何选择适合自己企业的进销存系统&#xff0…

【嵌入式烧录/刷写文件】-2.6-剪切/保留Intel Hex文件中指定地址范围内的数据

案例背景: 有如下一段HEX文件,保留地址范围0x9140-0x91BF内的数据,删除地址范围0x9140-0x91BF外的数据。 :2091000058595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576775F :2091200078797A7B7C7D7E7F808182838485868788898A…

「ACL 2023」发榜!火山语音推出业内首个借助视频信息的端到端语音翻译模型

日前 ACL 2023的论文录用结果公布,火山语音团队多篇论文成功入选,内容涵盖音频合成、歌声合成以及语音翻译等多个前沿技术领域的创新突破。ACL(Annual Meeting of the Association for Computational Linguistics)每年由国际计算语…

Nginx服务器及其配置与应用

目录 一、Nginx的特点 1.高并发 2.低消耗 3.低消耗 4.高可用 5.高扩展 6.Nginx与Apache的差异 7.Nginx与Apache的区别 二、Linux中的I/O 1.I/O介绍 2.同步/异步 3.阻塞/非阻塞:关注调用者在等待结果返回之前所处的状态 三、编译安装Nginx服务 1.关闭防火墙&#…

SOLIDWORKS安装使用说明网络版

安装准备 系统要求:参考https://www.solidworks.com/sw/support/SystemRequirements.htmlSolidWorks 2017 是最蕞后一个支持win server 2008 R2 sp1的软件。 SolidWorks 2018支持win server 2012及以上的系统,但不支持win server 2019 SolidWorks 2019…

HNU-计算机系统-CSAPP作业答案

计算机系统CSAPP课后作业答案 计科210X wolf 202108010XXX 第2章 2.61 解: (!~x) || (!x) || (!~(x|0x00ffffff)) || (!(x&0x000000ff)) 或者: (!~x) || (!x) || (!~(x>>24)) || (!(x<<24)) 2.71 A. 实现的是逻辑位移,扩展后前面全是0,不符合符号扩…

Linux常见命令学习

目录 1.ls2.pwd3.cd (change directory)4.touch&&cat&&echo5.mkdir&&rm6.cp&&mv7.man8.less&&vim 1.ls 列出当前目录中包含的文件和目录~ 类似于在windows上双击某个目录&#xff0c;把他打开&#xff0c;看看目录里有啥~ ls -> l…

企业内容管理丨如何解决企业客户签收回执慢,缩短回款周期?

方案应用领域及行业 本方案适用于快消品行业的供应链管理和财务管理 方案应用背景 由于动产物权的政策变化&#xff0c;物权转移从交付时才开始发生效力。也就是说&#xff0c;新政之前&#xff0c;企业发出商品&#xff0c;开出销售发票&#xff0c;即可申请货物回款&#…

C# webapi接口传输byte[]数据,报错:415 Unsupported Media Type

最近需要做上传文件操作。 由于历史原因&#xff0c;以前的接口使用了这样的入参&#xff1a; 代码如下&#xff1a; /// <summary> /// 上传文件 /// </summary> [HttpPost] public Result<UploadImageResult> UploadFile(byte[] bytes, string extName, s…

html框架-----标签(上)

目录 前言&#xff1a; 标签简介 1. HTML的基本结构 (1)html标签 (2)head标签 &#xff08;3&#xff09;body标签 2. 标题标签 3. 段落标签 4. 文本格式化标签 前言&#xff1a; 现在学前端工程师的都很难找工作&#xff0c;懂的都懂了&#xff0c;因为学前端一般去做那…

console.log是异步还是同步?为什么console.log有时候不准

console.log是异步还是同步 在前端开发中&#xff0c;控制台console.log通常是同步的。这意味着&#xff0c;当代码执行到console.log语句时&#xff0c;它会立即写入到控制台中&#xff0c;并且JavaScript代码执行会在console.log完成后继续进行。 但是&#xff0c;在某些情况…

# croc用法实践(设备间文件或文件夹传输)

croc用法实践&#xff08;设备间文件或文件夹传输&#xff09; 文章目录 croc用法实践&#xff08;设备间文件或文件夹传输&#xff09;1 安装2 使用示例2.1 发送文件2.2 发送文件夹2.3 发送文本字符串2.4 发送时指定code&#xff0c;接收时自动【Y】&#xff0c;并保存到指定目…

《MySQL(一):基础篇-MySQL概述》

文章目录 1. MySQL概述1.1 数据库相关概念1.2 MySQL数据库1.2.1 版本1.2.2 下载1.2.3 安装1.2.4 启动停止1.2.5 客户端连接1.2.6 数据模型1. MySQL概述 1.1 数据库相关概念 先来了解三个概念:数据库、数据库管理系统、SQL。 而目前主流的关系型数据库管理系统的市场占有率…

ic验证的主要工作流程和验证工具是什么?

验证其实是一个“证伪”的过程&#xff0c;从流程到工具&#xff0c;验证工程师的终极目的都只有一个&#xff1a; 发现所有BUG&#xff0c;或者证明没有BUG&#xff0c;以保证芯片功能性能的正确性和可靠性。 验证环节对于一颗芯片的重要性也是不言而喻的&#xff1a; 从项…

VMware16安装Linux CentOS7完整教程(附下载地址)

一、准备工作 1.安装VMware Workstation Pro 16 &#xff08;1&#xff09;百度网盘下载地址 链接:https://pan.baidu.com/s/1jv0kQ26TLMt9MzofImRzgA?pwds0m1 提取码&#xff1a;s0m1 &#xff08;2&#xff09;VMware官网 https://www.vmware.com/cn &#xff08;2&#x…