Golang学习笔记:Zinx框架搭建轻量级TCP服务器02

2023-10-26

Golang学习笔记
参考学习视频地址:https://www.bilibili.com/video/BV1wE411d7th?p=1
(所有截图均来自于上述视频)
本人为自学整理的个人理解文档

0.3版本

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Zinx0.3版本新增两个新概念:请求和路由
V0.2版本的链接只封装了套接字,而请求是封装了链接和用户传输的数据,后续通过请求来识别具体要实现什么功能,然后通过路由来完成对应的功能处理。
V0.3实现的效果(目前是只支持单路由,所以服务器的路由属性不是集合的那种类型): 因为需要执行的操作功能封装成路由,所以新增一个添加路由的接口,路由内部是不会绑定任何东西的,是纯粹的执行某些功能,因此服务器端测试程序里包括生成服务器函数、添加路由函数和服务器工作函数,添加路由就是将一个路由对象赋给服务器的路由属性,接下来进入服务器工作函数:服务器工作函数----只有服务器启动函数,接下来我们看服务器启动函数部分:
开头依然是与客户端连接、等待客户端数据,然后收到客户端消息后,将套接字和要实现的功能路由绑定生成链接,最后是链接的工作函数:
链接工作函数里只有读取链接且执行函数:这个函数里包括读取套接字的数据,然后将数据和当前链接再封装成请求,最后执行设定的路由函数。

文件一:
myDemo-ZinxV0.3-ClientTest.go(和0.2版本的一样,没变化)
文件二:
myDemo-ZinxV0.3-ServerTest.go

package main

import (
	"fmt"
	"myItem/src/Zinx/ziface"
	"myItem/src/Zinx/znet"
)

/*基于Zinx框架来开发的 服务器端应用程序*/


//ping test 自定义路由
type PingRouter struct {
	znet.BaseRouter
}

//Test PreHandle
func (this *PingRouter) PreHandle(request ziface.IRequest)  {
	fmt.Println("Call Router PreHandle...")
	_,err:=request.GetConnection().GetTCPConnection().Write([]byte("before ping...\n"))
	if err!= nil{
		fmt.Println("Call back before ping error")
	}
}

//Test Handle
func (this *PingRouter) Handle(request ziface.IRequest)  {
	fmt.Println("Call Router Handle...")
	_,err:=request.GetConnection().GetTCPConnection().Write([]byte("ping..ping..ping...\n"))
	if err!= nil{
		fmt.Println("Call back ping..ping..ping.. error")
	}
}

//Test PostHandle
func (this *PingRouter) PostHandle(request ziface.IRequest)  {
	fmt.Println("Call Router PostHandle...")
	_,err:=request.GetConnection().GetTCPConnection().Write([]byte("after ping...\n"))
	if err!= nil{
		fmt.Println("Call back after ping error")
	}
}
func main() {
	//创建一个server句柄,使用Zinx的api
	s:=znet.NewServer("[zinx V0.3]")

	//给当前zinx框架添加一个自定义router
	s.AddRouter(&PingRouter{})

	//启动server
	s.Server_()
}

文件三:Zinx-ziface-IConnection.go(和0.2版本的一样,没变化)
文件四:Zinx-ziface-IRequest.go

package ziface

/*IRequest接口:实际上将用户端请求的链接消息和请求的数据包装成request包*/
type IRequest interface {
	//得到当前链接
	GetConnection() IConnection

	//得到请求的消息数据
	GetData() []byte
}

文件五:Zinx-ziface-IRouter.go

package ziface

/*路由的抽象接口:路由里的数据都是IRequest*/
type IRouter interface {
	//出来业务之前的钩子方法Hook
	PreHandle(request IRequest)

	//出来业务的主方法
	Handle(request IRequest)

	//出来业务之后的钩子方法
	PostHandle(request IRequest)
}

文件六:Zinx-ziface-IServer.go

package ziface

/*定义一个服务器接口*/
type IServer interface {
	//启动服务器
	Start_()
	//停止服务器
	Stop_()
	//运行服务器
	Server_()

	//路由功能:给当前的服务注册一个路由方法,供用户端的链接处理使用
	AddRouter(router IRouter)
}

文件七:Zinx-znet-Connection.go

package znet

import (
	"fmt"
	"myItem/src/Zinx/ziface"
	"net"
)

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

	//连接ID
	ConnID uint32

	//当前的链接状态
	IsClosed bool

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

	//该链接处理的方法Router
	Router ziface.IRouter
}

//1.1 Strat_()里完成读数据的方法
func (c *Connection) StartReader() {
	fmt.Println("Reader Goroutne is running...")
	defer fmt.Println("connID=",c.ConnID,"Reader is exit,remote addr is ",c.GetRemoteAddr().String())
	defer c.Stop_()

	for {
		//读取用户端的数据到buf,最大512字节
		buf:=make([]byte,512)
		_,err:=c.Conn.Read(buf)
		if err!= nil {
			fmt.Println("Receive 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
		}*/
		//有了Router就不用调上面的内容了

		req:=Request{
			conn: c,
			data: buf,
		}
		//执行注册的路由方法
		go func(request ziface.IRequest) {
			c.Router.PreHandle(request)
			c.Router.Handle(request)
			c.Router.PostHandle(request)
		}(&req)
		//从路由中找到注册绑定的Conn对应的router调用

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

//停止链接,结束当前连接的工作
func (c *Connection) Stop_(){
	fmt.Println("Connect Stop...,ConnID=",c.ConnID)
	if c.IsClosed == true{
		return
	}
	c.IsClosed=true

	//关闭连接
	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) GetRemoteAddr() net.Addr{
	return c.Conn.RemoteAddr()
}

//发送数据,将数据发送给远程用户端
func (c *Connection) Send(data []byte) error{
	return nil
}
//初始化链接模块的方法
func NewConnection(conn *net.TCPConn,connID uint32,router ziface.IRouter)  *Connection{
	c:=&Connection{
		Conn: conn,
		ConnID: connID,
		Router: router,
		IsClosed: false,
		ExitChan: make(chan bool,1),
	}

	return c
}

文件八:Zinx-znet-Request.go

package znet

import "myItem/src/Zinx/ziface"

type Request struct {
	//已经和用户端建立好的链接
	conn ziface.IConnection
	//用户端请求的数据
	data []byte
}

//得到当前链接
func (r *Request) GetConnection() ziface.IConnection{
	return r.conn
}

//得到请求的消息数据
func (r *Request) GetData() []byte{
	return r.data
}

文件九:Zinx-znet-Router.go

package znet

import "myItem/src/Zinx/ziface"

/*实现router时,先嵌入这个BaseRouter基类,在根据需求对这个基类的方法进行重写,所以不实现基类方法*/
type BaseRouter struct {}
//出来业务之前的钩子方法Hook
func (br *BaseRouter) PreHandle(request ziface.IRequest){}

//出来业务的主方法
func (br *BaseRouter) Handle(request ziface.IRequest){}

//出来业务之后的钩子方法
func (br *BaseRouter) PostHandle(request ziface.IRequest){}

文件十:Zinx-znet-Server.go

package znet

import (
	"fmt"
	"myItem/src/Zinx/ziface"
	"net"
)

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

	//当前的server添加一个router,server注册的链接对应的处理业务
	Router ziface.IRouter
}

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 addr error:",err)
			return
		}

		//2.监听服务器的地址,等待用户端输入数据
		listenner,err:=net.ListenTCP(s.IPVersion,addr)
		if err!=nil {
			fmt.Println("listen",s.IPVersion,"error:",err)
			return
		}
		var cid uint32
		cid=0
		fmt.Println("Start Zinx server successfully,",s.Name,"successfully,Listenning...")
		//3.当监听到有客户端输数据给服务器,阻塞的等待客户端链接,获取该用户端与服务器端间的一个通道conn,通过conn处理客户端链接业务
		for {
			conn,err:=listenner.AcceptTCP()
			if err!=nil {
				fmt.Println("Accept err",err)
				continue
			}

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

			//启动当前的链接业务处理
			go dealConn.Start_()
		}
	}()
}

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

}

func (s *Server) Server_() {
	//启动server的服务功能
	s.Start_()

	//扩展功能

	//在这里需进入阻塞状态,避免结束服务器使用时,服务器直接关闭
	//注:不在Start_()里阻塞是因为在启动服务器之后还需要做一些扩展功能,不希望服务器提前阻塞了
	select {

	}
}

//注册路由
func (s *Server) AddRouter(router ziface.IRouter)  {
	s.Router = router
	fmt.Println("Add Router Successfully!")
}

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

	return s
}

0.4版本

在这里插入图片描述
0.4版本主要是完成一个全局配置的功能,通过新增一个全局变量,来完成对之前版本的一些写死的服务器信息进行替换,用可自定义的全局变量来完成服务器参数的灵活设置。
在这里插入图片描述
**V0.4版本的实现效果:**主要对服务器端的接口那部分进行修改,对新建服务器函数的那些参数换出全局变量,对Connectio里面的接收数据的长度改成全局变量设定的长度属性

0.5版本

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
**V0.5版本实现效果:**实际上消息从网络进行传输时,应该序列化,不能直接丢过去,因此采用TLV封包格式设置IMessage的格式,0.5版本主要针对之前的Request中的数据进行完善封装,在之前的版本中Request中包含自定义的链接和用户传输的数据,但是那个数据没有自带长度和消息ID的概念,而到后面的版本我们可以用消息ID来识别不同的功能操作,因此这个版本新增了Message的概念,完成对消息的进一步优化封装,至此Message成为数据处理层面的一个原子结构,下面是实现的数据结构:
TLV序列化
使用这样的格式可以解决TCP粘包问题
(粘包问题:假如客户端向服务器端发送两条数据,但底层不清楚数据数量,当服务器端接收数据时,就有可能将两条数据读取成一条数据)
而采用TLV格式后,服务器端可先读八个字节得到数据的长度和id信息,再读出前四个字节的消息长度,然后往后偏移八个字节,按消息长度读取完整数据
封包机制:创建一个存放bytes字节的缓冲(二进制文件),将数据以二进制格式存入其中,然后依次加上消息长度属性和消息ID属性,最后返回那个二进制文件(数据包)
在这里插入图片描述
拆包机制:创建一个读取二进制文件(数据包)的缓冲区,只解压head信息得到消息长度和消息ID,然后判断包的长度是否符合我们在全局变量设置的包长度,不符合就直接return加报错,符合就返回数据
在这里插入图片描述
在Zinx框架中,首先对Request的封装内容修改,由之前的数据+链接更新为消息(Message)+链接
在服务器测试程序上,还是生成服务器函数、添加路由函数和服务器工作函数,接下来进入服务器工作函数:服务器工作函数----只有服务器启动函数,接下来我们看服务器启动函数部分:
开头是与客户端连接、等待客户端数据,然后收到客户端消息后,将套接字和要实现的功能路由绑定生成链接,最后是链接的工作函数:
链接工作函数里只有读取链接且执行函数:这个函数以前的读取链接的套接字的数据,修改为先创建拆包对象,然后读取客户端发送来的数据包的消息长度和ID存放到Message中,再将数据读取出来且存放到Message,之后将Message和当前链接再封装成Request,最后执行设定的路由函数,路由函数(这里的路由函数就是回发一个消息给客户端)做出如下修改:
首先从Request读取消息ID和数据包(后续对会增加一些和数据有关的功能操作,这里就简单的回写一些自定义的字符给客户端了),然后再将一些字符封装成二进制文件(数据包),再通过链接里的套接字发送这个数据包给客户端。

0.6版本

在这里插入图片描述
V0.6版本实现效果:由于之前的版本只支持单路由,而我们希望能实现多种功能,因此这一版本新增了支持多路由的功能,主要是用消息管理模块来支持多路由业务api调度管理,修改的地方有服务器的属性,服务器之前的路由属性修改为消息管理模块属性,这个模块是消息ID和对应路由绑定的结果,这就用到之前拆包获得的消息ID(主要用map来绑定消息ID和对应路由的关系),测试程序的总体流程和上个版本基本一致,以前的版本是获得Request后,执行Request对应的路由函数,而这个版本是先根据Request里的消息ID去查找消息管理模块里对应的路由函数(这一步是新的,后面的和前面的处理流程一样),再执行路由函数
在这里插入图片描述

0.7版本

在这里插入图片描述
V0.7版本实现效果:之前的版本都是服务器端接收到来自客户端的数据包,然后拆包后开一个协程执行对应的路由函数,最后将执行路由后得到的数据打包成数据包反馈给客户端了,而我们希望读取数据、执行路由函数与回馈消息进行分离,所以在这个版本新增了一个媒介:channel,主要修改的地方是,服务器端首先开一个的 读协程 来完成客户端发送过来的数据包,将拆包后执行对应的路由功能得到数据,打包后发送到channel上,然后另一个 写协程 就持续从channel读数据,当channel上有数据时,写协程就获取上面的数据并发送给客户端,而且还新加另一条exit channel专门用来传输客户端与服务器端的连接消息,如果客户端与服务器端断开连接后,写协程结束,并在结束之前往exit channel传一个数据,当写函数从exit channel上读取到数据时,就将exit channel关闭,再将读写之间的channel关闭。
在这里插入图片描述

0.8版本

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
**0.8版本实现效果(考虑的是协程的个数,因为数量多的话,协程之间的切换太耗费资源了):**由于前面测试的客户端都是比较少量的,所以几个协程的切换对效率影响不大,如果考虑到有成千上万个用户来访问我们的服务器,按照之前的版本就是每个客户端和服务器端都会开几个协程,那么是否意味着成千上万个客户端就会开启成千上万个协程,这显然不是我们所希望的。
因此在这个版本我们新增工作池和消息队列机制,修改了之前收到Request后直接开goroutine处理的机制,变成默认开10个goroutine,每个协程都是一个读写之间的协程,服务器端然后先将Request发给工作池,由工作池分配到每个协程对应的消息队列(10个协程对应10条),而每个消息队列都是一个channel,而众多的Request都有其独有的链接ID,根据ID对工作池里的消息队列数量取余,即可做到对Request的均分。
主要的修改就是在读协程里拆包获得数据封装成Request,然后丢到工作池处理业务。
在这里插入图片描述

0.9版本

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
V0.9版本实现效果:服务器能开的连接都是有限的,之前的版本都是来一个客户端就允许链接,那如果有成千上万的用户来的话,服务器有可能就会因为内存不足或者其他原因导致崩溃了,这个版本新增链接管理模块,一方面设置限定数量的链接数量,不能来客户端请求就响应,另一方面在创建链接前/后可以扩展业务,可以自己增删连接,毕竟之前的版本是由于出错或者手动终止程序才会关闭连接。

1.0版本

在这里插入图片描述
V1.0版本的实现效果:给链接添加可以自行配置属性的功能

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Golang学习笔记:Zinx框架搭建轻量级TCP服务器02 的相关文章

  • 阿里云2核4G服务器优惠价格30元3个月?小心坑

    2024年阿里云2核4G服务器优惠价格30元3个月 活动 https t aliyun com U bLynLC 配置为云服务器ECS经济型e实例ecs e c1m2 large 3M固定带宽 系统盘为40GB ESSD Entry 活动打
  • 第二节课内容学习

    监听远程端口 并映射到本地 先配置ssh的公私钥非对称加密 假设远程开放的端口为33090 在本地计算机终端执行 ssh CNg L 6006 127 0 0 1 6006 root ssh intern ai org cn p 33090
  • 网络基础面试题(二)

    11 什么是网桥 防火墙的端口防护是指什么 网桥是一种网络设备 用于连接两个或多个局域网 LAN 并转发数据包 它能够根据MAC地址来识别和转发数据 提高网络的传输效率和安全性 防火墙的端口防护是指对防火墙上的各个端口进行保护和限制 只允许
  • “性能压测揭密:关键指标分析!“

    在进行全链路压测和性能测试时 需要关注多个关键性能指标 KPIs 来评估系统的性能表现 以下是一些常见的性能测试指标 1 吞吐量 Throughput 系统在单位时间内能够处理的请求数量或事务数量 通常以每秒请求数 RPS TPS 来衡量
  • go cannot find package “github.com/gorilla/websocket“解读

    Go无法找到包 github com gorilla websocket 的解决方案 在Go开发过程中 我们经常会依赖第三方库来简化开发工作 而使用 go get 命令安装这些库时 有时候我们可能会遇到类似于以下错误的情况 plaintex
  • CMAKE_MAKE_PROGRAM is not set 解读

    目录 CMAKE MAKE PROGRAM 未设置 错误原因 解决方案 示例1 GNU Make 示例2 Ninja CMakeLists txt 的结构 示例 CMakeLists txt 文件 总结 CMAKE MAKE PROGRAM
  • Linux 系统日志及其归档

    主要记录Linux 系统需要关注的日志文件 以及日志归档服务 rsyslogd 系统日志服务 rsyslogd 日志服务 rsyslogd reliable and extended syslogd 可靠 可扩展的系统日志服务 Rsyslo
  • CTF之逆向入门

    逆向工程 Reverse Engineering 又称反向工程 是一种技术过程 即对一项目标产品进行逆向分析及研究 从而演绎并得出该产品的处理流程 组织结构 功能性能规格等设计要素 以制作出功能相近 但又不完全一样的产品 逆向工程源于商业及
  • Linux 软件安装以及管理

    本篇主要记录常用的软件安装和管理方式 主要是 yum rpm dnf apt pip 大致都是一样的 主要是部分软件提供了解决依赖的功能 内容不包括源码安装 源码安装情况相对比较复杂 后续有时间再补充 约定 案例所用模板软件均为 pytho
  • nohup - 后台执行

    nohup no hang up 语法 nohup Command Arg 使用示例 nohup python a py 日志将被保留在 当前文件夹下的 nohup out 将日志放到文件 不输出到终端 echo hello gt 1 tx
  • 服务器集群是如何提高计算性能的?

    服务器集群是一种将多台服务器连接起来协同工作的技术 通过集群配置 可以提高计算性能 可靠性和可扩展性 以下是服务器集群如何提高计算性能的详细解释 一 并行处理能力 服务器集群的核心优势在于其并行处理能力 通过将多个服务器组成一个集群 可以将
  • 自定义编写zabbix_agent脚本

    vi usr lib systemd system zabbix agent servicce Unit Description Zabbix Agent After syslog target After network target S
  • 如何解决Mybatis-plus与Mybatis不兼容的问题:An attempt was made to call a method that does not exist. The attempt

    博主猫头虎的技术世界 欢迎来到 猫头虎的博客 探索技术的无限可能 专栏链接 精选专栏 面试题大全 面试准备的宝典 IDEA开发秘籍 提升你的IDEA技能 100天精通Golang Go语言学习之旅 领域矩阵 猫头虎技术领域矩阵 深入探索各技
  • 用户数据中的幸存者偏差

    幸存者偏差 Survivorship bias 是一种常见的逻辑谬误 意思是没有考虑到筛选的过程 忽略了被筛选掉的关键信息 只看到经过筛选后而产生的结果 先讲个故事 二战时 无奈德国空防强大 盟军战机损毁严重 于是军方便找来科学家统计飞机受
  • 跨平台UI自动化框架:Airtest,游戏开发和应用测试的利器

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 一台java服务器可以跑多少个线程?

    一台java服务器可以跑多少个线程 一台java服务器能跑多少个线程 这个问题来自一次线上报警如下图 超过了我们的配置阈值 打出jstack文件 通过IBM Thread and Monitor Dump Analyzer for Java
  • 【go语言】结构体数据填充生成md错误码文件

    这里使用pongo2这个模版引擎库进行md文件渲染 GitHub flosch pongo2 Django syntax like template engine for Go package main import fmt github
  • 为什么我强烈推荐大学生打CTF!

    前言 写这个文章是因为我很多粉丝都是学生 经常有人问 感觉大一第一个学期忙忙碌碌的过去了 啥都会一点 但是自己很难系统的学习到整个知识体系 很迷茫 想知道要如何高效学习 这篇文章我主要就围绕两点 减少那些罗里吧嗦的废话 直接上干货 CTF如
  • 【js学习之路】遍历数组api之 `filter `和 `map`的区别

    一 前言 数组是我们在项目中经常使用的数据类型 今天我们主要简述作用于遍历数组的api filter 和 map 的区别 二 filter和map的共同点 首先 我们主要阐述一下 filter 和 map 的共同点 api的参数都是回调函数
  • 网工内推 | 上市公司同程、科达,五险一金,年终奖,最高12k*15薪

    01 同程旅行 招聘岗位 网络工程师 职责描述 1 负责职场 门店网络规划 建设 维护 2 负责网络安全及访问控制 上网行为管理和VPN设备的日常运维 3 负责内部相关网络自动化和系统化建设 4 优化与提升网络运行质量 制定应急预案 人员培

随机推荐

  • gitkraken免费版本6.5.1,Linux下载地址

    来源 https www kaisawind com 2020 03 04 2020 03 05 git gitkraken自6 5 3本地库开始收费 所以推荐下载旧版本下载地址 收费版本 version 6 5 3 url deb htt
  • 视觉架构大一统!港中文通过统一视角Container对Transformer, 深度卷积以及MLP-Mixer进行了大一统

    编辑 Happy 首发 AIWalker 前段时间MLP Mixer提出后 引发了视觉架构圈的一篇轰动 包含但不限于以下几篇文章 重参数宇宙 再添新成员 RepMLP 清华大学 旷视科技提出将重参数卷积嵌入到全连接层 新坑 谷歌提出MLP
  • 在Final Cut Pro X 中如何把音频和视频对齐?

    在拍摄的视频的时候 往往因为现场的环境嘈杂而使得录音不清晰 我们通常会单独用一个设备录音 获得单条音频 在用Final Cut Pro X剪辑上的时候 如何把音频和视频对齐呢 用Final Cut Pro X把音频和视频对齐教程 1 在事件
  • muduo_base库学习笔记6——互斥量、条件变量和倒计时门闩类

    muduo中对互斥量和条件变量的操作基本上都是调用它们对应的相关函数来实现的 例如MutexLock lock即调用pthread mutex lock Condition wait 即调用pthread cond wait等 互斥量 mu
  • C - Balanced Number HDU - 3709 (数位dp)

    题目链接 https cn vjudge net contest 278036 problem C 题目大意 手首先是T组数据 然后每一次输入两个数l r 求这个区间里面满足以某个数字为中心的两侧力矩和相等的个数 举个例子 4139 我们如
  • win10键盘锁住了怎么解决

    有win10系统用户在使用的时候 发现键盘被锁住了 导致无法使用 经过分析可能是不小心按到了键盘上的锁住键 锁定键盘的快捷键 笔记本电脑 Fn Numlock 键 第一种方法 1 外接键盘 是否按过 Numlock 键 如果按过 Numlo
  • vue2使用国际化语言vue-i18n

    1 安装 npm install vue i18n 或 yarn add vue i18n 2 引用 在main js中引用 import VueI18n from vue i18n Vue use VueI18n 运行项目 报错 由于使用
  • 网络流之最大流和最小割

    最大流问题 最大流 给定有向图中每条边的最大流量 容量 求从源点到汇点的最大流量 容量网络 括号左边代表容量 右边代表流量 残留网络 流网络中剩余可增加的流量 增广路 满足容量条件的一条流量不为零的路径 增广路定理 设容量网络G V E 的
  • 转: Windows 批处理大全

    批处理文件是无格式的文本文件 它包含一条或多条命令 它的文件扩展名为 bat 或 cmd 在命令提示下键入批处理文件的名称 或者双击该批处理文件 系统就会调用Cmd exe按照该文件中各个命令出现的顺序来逐个运行它们 使用批 处理文件 也被
  • 【云原生之Docker实战】使用Docker部署NodeBB社区平台

    云原生之Docker实战 使用Docker部署NodeBB社区平台 一 NodeBB 二 检查本地docker环境 1 检查docker版本 2 检查docker状态 3 检查docker compose版本 三 下载NodeBB镜像 四
  • 方差(variance)、标准差(Standard Deviation)、均方差、均方根值(RMS)、均方误差(MSE)、均方根误差(RMSE)

    方差 variance 衡量随机变量或一组数据时离散程度的度量 概率论中方差用来度量随机变量和其数学期望 即均值 之间的偏离程度 统计中的方差 样本方差 是每个样本值与全体样本值的平均数之差的平方值的平均数 概率论中的方差表示方法 样本方差
  • Javascript设计模式-07-观察者模式(发布订阅模式)

    Javascript设计模式 07 观察者模式 发布订阅模式 简介 观察者模式又叫发布订阅模式 他定义了一种一对多的关系 让多个观察者对象同时监听某一个主体对象 这个主体对象发生改变时就会通知所有观察者 使得他们能够自动更新自己 提供一种抽
  • 多维时序

    多维时序 MATLAB实现Attention LSTM 注意力机制长短期记忆神经网络 多输入单输出 目录 多维时序 MATLAB实现Attention LSTM 注意力机制长短期记忆神经网络 多输入单输出 基本介绍 模型背景 LSTM模型
  • 【SQLMap工具-1】SQLMap简介及简单应用实例

    1 SQLMap简介 SQLMap 是一个自动化的SQL注入工具 其主要功能是扫描 发现并利用给定URL的SQL注入漏洞 内置了很多绕过插件 支持的数据库是MySQL Oracle PostgreSQL Microsoft SQL Serv
  • fmt文本格式库的源码下载编译(Win10+VS2022)

    目录 fmt文件格式库 什么是fmt fmt下载方式 资源结构 编译fmt 查看编译成功lib和pdb fmt文件格式库 什么是fmt fmt 是一个先进的文本格式库 具有现代语言的特征 用来代替 C 的 stdio 和 C iostrea
  • MessagePack For C#在Unity中的应用(场景的保存和加载)

    最近项目里面有这样一个需求 就是把场景里面的东西保存下载 然后需要的时候读取数据 加载出来 这里用到了MessagePack For C 的Unity 版本 先放一个网址https www cnblogs com Leo wl p 8143
  • 在数据集上计算连续随机变量的信息熵和互信息--k-近邻估计方法

    写在前面 信息熵 entropy 的原始定义是离散 discrete 的 后来发展了在连续域上的微分熵 differential entropy 然而 通常在给定的数据集上 无法知道连续变量的概率分布 其概率密度函数也就无法获得 不能够用微
  • Py_buffer

    使用C C 扩展Python之一 Posted on Thu 12 November 2015 in Python 假设我们需要使用C C 实现一个翻转字符串的扩展功能 下面是C语言的实现 include
  • 公布一下工资管理系统中核心部分工资计算的代码

    终于完成了工资计算部分的代码 请大家指教一下 用户可以自行定义工资计算公式 例如 实发工资 应发工资 应扣工资个人所得税 tax 应发工资 CREATE TABLE pq unitgzxm unit id char 8 NOT NULL D
  • Golang学习笔记:Zinx框架搭建轻量级TCP服务器02

    Golang学习笔记 参考学习视频地址 https www bilibili com video BV1wE411d7th p 1 所有截图均来自于上述视频 本人为自学整理的个人理解文档 0 3版本 Zinx0 3版本新增两个新概念 请求和