C/C++轻量级并发TCP服务器框架Zinx-游戏服务器开发002:框架学习-按照三层结构模式重构测试代码+Tcp数据适配+时间轮定时器

news2024/11/24 1:33:43

文章目录

  • 1 Zinx框架总览
  • 2 三层模式的分析
  • 3 三层重构原有的功能 - 头文件
    • 3.1 通道层Stdin和Stdout类
      • 3.1.2 StdInChannel
      • 3.1.2 StdOutChannel
    • 3.2 协议层CmdCheck和CmdMsg类
      • 3.2.1 CmdCheck单例模式
        • 3.2.1.1 单例模式
        • 3.2.1.2 * 命令识别类向业务层不同类别做分发
      • 3.2.2 CmdMsg自定义用户信息类,继承UserData
    • 3.3 业务层:回显类, 输出通道控制类, 日期前缀管理类
      • 3.3.1 回显对象EchoRole
      • 3.3.2 控制输入输出
      • 3.3.3 日期管理类
  • 4 Tcp数据适配
    • 4.1 工厂类 - 框架头文件分析
    • 4.2 tcp通道实现
      • 4.2.1 Tcp套接字通道通信类
      • 4.2.2 tcp数据套接字通道类的工厂类
  • 5 时间轮定时器
    • 5.1 timerfd产生超时事件
      • 5.1.1 测试代码
    • 5.2 时间轮设置
      • 5.2.1 时间轮的定义
      • 5.2.2 时间轮的移动
      • 5.2.3 添加和删除任务
        • 5.2.3.1 添加任务
        • 5.2.3.2 删除任务
    • 5.3 定时器设置
      • 5.3.1 定时器定义
      • 5.3.2 定时器初始化
      • 5.3.3 输出hello world

1 Zinx框架总览

在这里插入图片描述

2 三层模式的分析

在这里插入图片描述

3 三层重构原有的功能 - 头文件

三层结构重构原有功能

  1. 自定义消息类,继承UserData,添加一个成员变量szUserData
  2. 定义多个Role类继承Irole,重写ProcMsg函数,进行不同处理
  3. 定义protocol类,继承Iprotocol,重写四个函数,两个函数时原始
    数据和用户数据之间的转换;另两个用来找消息处理对象和消息发
    送对象。
  4. 定义channel类,继承Ichannel,在getnextinputstage函数中返回协
    议对象

3.1 通道层Stdin和Stdout类

通道类,派生自基础处理者类,提供基于系统调用的数据收发功能
一般地,用户应该根据处理的文件(信息源)不同而创建通道类的子类或选用合适的实用类(已经提供的通道类子类)来完成系统级文件IO
在这里插入图片描述

class StdInChannel :
	public Ichannel
{
public:
	StdInChannel();
	virtual ~StdInChannel();

	// 通过 Ichannel 继承
	virtual bool Init() override;
	virtual bool ReadFd(std::string& _input) override;
	virtual bool WriteFd(std::string& _output) override;
	virtual void Fini() override;
	virtual int GetFd() override;
	virtual std::string GetChannelInfo() override;
	virtual AZinxHandler* GetInputNextStage(BytesMsg& _oInput) override;
};

class StdOutChannel :public Ichannel
{
	// 通过 Ichannel 继承
	virtual bool Init() override;
	virtual bool ReadFd(std::string& _input) override;
	virtual bool WriteFd(std::string& _output) override;
	virtual void Fini() override;
	virtual int GetFd() override;
	virtual std::string GetChannelInfo() override;
	virtual AZinxHandler* GetInputNextStage(BytesMsg& _oInput) override;
};

3.1.2 StdInChannel

bool StdInChannel::ReadFd(std::string& _input)
{
	cin >> _input;
	return true;
}

bool StdInChannel::WriteFd(std::string& _output)
{
	return false;
}

int StdInChannel::GetFd()
{
	return 0;
}

std::string StdInChannel::GetChannelInfo()
{
	return "stdin";
}

AZinxHandler* StdInChannel::GetInputNextStage(BytesMsg& _oInput)
{
	/*返回协议对象*/
	return CmdCheck::GetInstance();
}

3.1.2 StdOutChannel

bool StdOutChannel::ReadFd(std::string& _input)
{
	return false;
}

bool StdOutChannel::WriteFd(std::string& _output)
{
	cout << _output << endl;
	return true;
}

int StdOutChannel::GetFd()
{
	return 1;
}

std::string StdOutChannel::GetChannelInfo()
{
	return "stdout";
}

AZinxHandler* StdOutChannel::GetInputNextStage(BytesMsg& _oInput)
{
	return nullptr;
}

3.2 协议层CmdCheck和CmdMsg类

3.2.1 CmdCheck单例模式

  1. 原始数据和业务数据相互函数,开发者重写该函数,实现协议
  2. 获取处理角色对象函数,开发者应该重写该函数,用来指定当前产生的用户数据消
  3. 获取发送通道函数,开发者应该重写该函数,用来指定当前字节流应该由哪个通道对象发出
class CmdCheck :
	public Iprotocol
{
	CmdCheck();
	virtual ~CmdCheck();
	static CmdCheck *poSingle;
public:
	// 通过 Iprotocol 继承
	/*原始数据和业务数据相互函数,开发者重写该函数,实现协议*/
	virtual UserData * raw2request(std::string _szInput) override;
	virtual std::string * response2raw(UserData & _oUserData) override;
	/*获取处理角色对象函数,开发者应该重写该函数,用来指定当前产生的用户数据消息应该传递给哪个角色处理*/
	virtual Irole * GetMsgProcessor(UserDataMsg & _oUserDataMsg) override;
    /*获取发送通道函数,开发者应该重写该函数,用来指定当前字节流应该由哪个通道对象发出*/
	virtual Ichannel * GetMsgSender(BytesMsg & _oBytes) override;
	static CmdCheck *GetInstance() {
		return poSingle;
	}
	std::string szOutChannel;
};
3.2.1.1 单例模式

构造全局唯一的协议对象

#include "CmdCheck.h"
#include "CmdMsg.h"
#include "EchoRole.h"
using namespace std;

CmdCheck *CmdCheck::poSingle = new CmdCheck();

3.2.1.2 * 命令识别类向业务层不同类别做分发

通过是不是命令来进行区分:if (isCmd)

Irole * CmdCheck::GetMsgProcessor(UserDataMsg & _oUserDataMsg)
{
	szOutChannel = _oUserDataMsg.szInfo;
	if ("stdin" == szOutChannel)
	{
		szOutChannel = "stdout";
	}
	/*根据命令不同,交给不同的处理role对象*/
	auto rolelist = ZinxKernel::Zinx_GetAllRole();

	auto pCmdMsg = dynamic_cast<CmdMsg *>(_oUserDataMsg.poUserData);

	/*读取当前消息是否是命令*/
	bool isCmd = pCmdMsg->isCmd;

	Irole *pRetRole = NULL;

	for (Irole *prole : rolelist)
	{
		if (isCmd)
		{
			auto pOutCtrl = dynamic_cast<OutputCtrl *>(prole);
			if (NULL != pOutCtrl)
			{
				pRetRole = pOutCtrl;
				break;
			}
		}
		else
		{
			auto pDate = dynamic_cast<DatePreRole *>(prole);
			if (NULL != pDate)
			{
				pRetRole = pDate;
				break;
			}
		}
	}
	
	return pRetRole;
}

3.2.2 CmdMsg自定义用户信息类,继承UserData

class CmdMsg :
	public UserData
{
public:
	/*成员变量表示要回显的字符串*/
	std::string szUserData;
	/*开启输出标志*/
	bool isOpen = true;
	/*该消息是命令*/
	bool isCmd = false;
	/*要加前缀*/
	bool needDatePre = false;
	
	CmdMsg();
	virtual ~CmdMsg();
};

3.3 业务层:回显类, 输出通道控制类, 日期前缀管理类

3.3.1 回显对象EchoRole

主要有init, procmsg,fini三个函数

#pragma once
#include <zinx.h>

class EchoRole :
	public Irole
{
public:
	EchoRole();
	virtual ~EchoRole();

	// 通过 Irole 继承
	virtual bool Init() override;
	virtual UserData * ProcMsg(UserData & _poUserData) override;
	virtual void Fini() override;
};
  • 容易出错的点:参数一必须是一个堆对象
UserData * EchoRole::ProcMsg(UserData & _poUserData)
{
	/*写出去*/
	GET_REF2DATA(CmdMsg, input, _poUserData);
	CmdMsg *pout = new CmdMsg(input);
	ZinxKernel::Zinx_SendOut(*pout, *(CmdCheck::GetInstance()));
	return nullptr;
}

3.3.2 控制输入输出

  1. 写一个关闭输出的角色类,摘除输出通道或添加输出通道
  2. 在CmdMsg用户数据类中添加开关标志,是否是命令标志
  3. 在协议类中,根据输入字符串,设置开关标志和是否是命令的标志
  4. 在协议类分发消息时,判断是否是命令,是命令则发给关闭输出角 色类,否则发给回显角色类
class OutputCtrl :public Irole {
	// 通过 Irole 继承
	virtual bool Init() override;
	virtual UserData * ProcMsg(UserData & _poUserData) override;
	virtual void Fini() override;
	Ichannel *pOut = NULL;
};

3.3.3 日期管理类

class DatePreRole :public Irole {
	// 通过 Irole 继承
	virtual bool Init() override;
	virtual UserData * ProcMsg(UserData & _poUserData) override;
	virtual void Fini() override;
	bool needAdd = false;
};

4 Tcp数据适配

4.1 工厂类 - 框架头文件分析

  • 产生tcp数据套接字通道类的抽象工厂类。
    • 开发者需要重写CreateTcpDataChannel函数,来返回一个tcp通道对象。
    • 般地,开发者应该同时创建一对tcp通道类和工厂类
class IZinxTcpConnFact {
public:
	virtual ZinxTcpData *CreateTcpDataChannel(int _fd) = 0;
};
  • tcp监听通道类,这是一个实体类(不建议继承该类)。
    • 开发者可以直接创建tcp监听通道对象,
    • 一般地,开发者应该在该类的构造函数中,指定一个tcp套接字通道类的工厂类,当有连接到来后,该工厂类的成员方法会被调用
class ZinxTCPListen :
	public Ichannel
{
private:
	unsigned short m_usPort = 0;
	int m_fd = -1;
	IZinxTcpConnFact *m_ConnFac = NULL;
	
public:
	ZinxTCPListen(unsigned short _usPort, IZinxTcpConnFact *_pConnFac) :m_usPort(_usPort), m_ConnFac(_pConnFac){}
	virtual ~ZinxTCPListen();

	virtual bool Init() override;
	virtual bool ReadFd(std::string & _input) override;
	virtual bool WriteFd(std::string & _output) override;
	virtual void Fini() override;
	virtual int GetFd() override;
	virtual std::string GetChannelInfo() override;
	virtual AZinxHandler * GetInputNextStage(BytesMsg & _oInput);
};

4.2 tcp通道实现

在这里插入图片描述

4.2.1 Tcp套接字通道通信类

  • tcp数据套接字通道类,继承通道类,该类也是一个抽象类,需要开发者继承该类,重写GetInputNextStage函数以指定读取到的字节流的处理方式
// h
class myTcpData :public ZinxTcpData {
public:
	myTcpData(int _fd) :ZinxTcpData(_fd) {}
	// 通过 ZinxTcpData 继承
	virtual AZinxHandler* GetInputNextStage(BytesMsg& _oInput) override;
};
  • Q: Ichannel对象读取到的数据给谁了?
    • 给该对象调用GetInputNextStage函数返回的对象
AZinxHandler* myTcpData::GetInputNextStage(BytesMsg& _oInput)
{
	/*返回协议对象*/
	return CmdCheck::GetInstance();
}
  • Q: Iprotocol对象转换出的用户请求给谁了?
    • 给该对象调用GetMsgProcessor函数返回的对象

4.2.2 tcp数据套接字通道类的工厂类

  • 产生tcp数据套接字通道类的抽象工厂类,开发者需要重写CreateTcpDataChannel函数,来返回一个tcp通道对象
    一般地,开发者应该同时创建一对tcp通道类和工厂类
// h
class myFact :public IZinxTcpConnFact {
	// 通过 IZinxTcpConnFact 继承
	virtual ZinxTcpData* CreateTcpDataChannel(int _fd) override;
};
ZinxTcpData* myFact::CreateTcpDataChannel(int _fd)
{
	return new myTcpData(_fd);
}

5 时间轮定时器

5.1 timerfd产生超时事件

timerfd_create()返回定时器文件描述符
timerfd_settime()设置定时周期,立刻开始计时
read,读取当当前定时器超时的次数,没超时会阻塞.
一般地,会将定时器文件描述符结合IO多路复用使用

5.1.1 测试代码

#include<sys/timerfd.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
	int iTimerfd = timerfd_create(CLOCK_MONOTONIC, 0);
	struct itimerspec period
	{{5, 0},{5, 0}
	};
	timerfd_settime(iTimerfd,0, &period,NULL);
	__uint64_t count = 0;
	while(1) {
		read(iTimerfd, &count, sizeof(count));
		puts("time out");
	}
}

在这里插入图片描述

5.2 时间轮设置

单例模式

AZinxHandler * ZinxTimerChannel::GetInputNextStage(BytesMsg & _oInput)
{
	return &TimerOutMng::GetInstance();
}

TimerOutMng TimerOutMng::single;

5.2.1 时间轮的定义

// h
class TimerOutProc {
public:
	virtual void Proc() = 0;
	virtual int GetTimeSec() = 0;
	/*所剩圈数*/
	int iCount = -1;
};

  • vector存储轮的齿
  • 每个齿里用list存每个定时任务
  • 每个定时任务需要记录剩余圈数
  • 时间轮类中要有一个刻度,每秒进一步
TimerOutMng::TimerOutMng()
{
	/*创建10个齿*/
	for (int i = 0; i < 10; i++)
	{
		list<TimerOutProc *> tmp;
		m_timer_wheel.push_back(tmp);
	}
}

5.2.2 时间轮的移动

// h
class TimerOutMng :public AZinxHandler {
	std::vector<std::list<TimerOutProc *> > m_timer_wheel;
	int cur_index = 0;
	static TimerOutMng single;
	TimerOutMng();
public:
	/*处理超时事件,遍历所有超时任务*/
	virtual IZinxMsg * InternelHandle(IZinxMsg & _oInput) override;

	virtual AZinxHandler * GetNextHandler(IZinxMsg & _oNextMsg) override;
	void AddTask(TimerOutProc *_ptask);
	void DelTask(TimerOutProc *_ptask);
	static TimerOutMng &GetInstance() {
		return single;
	}
};
  • 移动当前刻度
  • 遍历当前齿中的任务列表
  • 若圈数为0,则执行处理函数,摘除本节点,重新添加
  • 否则,圈数–
IZinxMsg * TimerOutMng::InternelHandle(IZinxMsg & _oInput)
{
	unsigned long iTimeoutCount = 0;
	GET_REF2DATA(BytesMsg, obytes, _oInput);
	obytes.szData.copy((char *)&iTimeoutCount, sizeof(iTimeoutCount), 0);

	while (iTimeoutCount-- > 0)
	{
		/*移动刻度*/
		cur_index++;
		cur_index %= 10;
		list<TimerOutProc *> m_cache;
		/*遍历当前刻度所有节点,指向处理函数或圈数-1,*/
		for (auto itr = m_timer_wheel[cur_index].begin(); itr != m_timer_wheel[cur_index].end(); )
		{
			if ((*itr)->iCount <= 0)
			{
				/*缓存待处理的超时节点*/
				m_cache.push_back(*itr);
				auto ptmp = *itr;
				itr = m_timer_wheel[cur_index].erase(itr);
				AddTask(ptmp);
			}
			else
			{
				(*itr)->iCount--;
				++itr;
			}
		}

		/*统一待处理超时任务*/
		for (auto task : m_cache)
		{
			task->Proc();
		}
	}
	
	return nullptr;
}

5.2.3 添加和删除任务

5.2.3.1 添加任务
  • 计算当前任务在哪个齿上
  • 添加该任务到该齿对应的list里
  • 计算所需圈数记录到任务中
void TimerOutMng::AddTask(TimerOutProc * _ptask)
{
	/*计算当前任务需要放到哪个齿上*/
	int index = (_ptask->GetTimeSec() + cur_index) % 10;
	/*把任务存到该齿上*/
	m_timer_wheel[index].push_back(_ptask);
	/*计算所需圈数*/
	_ptask->iCount = _ptask->GetTimeSec() / 10;
}
5.2.3.2 删除任务
  • 遍历所有齿
  • 在每个齿中遍历所有节点
  • 若找到则删除并返回
void TimerOutMng::DelTask(TimerOutProc * _ptask)
{
	/*遍历时间轮所有齿,删掉任务*/
	for (list<TimerOutProc *> &chi : m_timer_wheel)
	{
		for (auto task : chi)
		{
			if (task == _ptask)
			{
				chi.remove(_ptask);
				return;
			}
		}
	}
}

5.3 定时器设置

5.3.1 定时器定义

class ZinxTimerChannel :
	public Ichannel
{
	int m_TimerFd = -1;

public:
	ZinxTimerChannel();
	virtual ~ZinxTimerChannel();

	// 通过 Ichannel 继承
	virtual bool Init() override;
	virtual bool ReadFd(std::string & _input) override;
	virtual bool WriteFd(std::string & _output) override;
	virtual void Fini() override;
	virtual int GetFd() override;
	virtual std::string GetChannelInfo() override;
	virtual AZinxHandler * GetInputNextStage(BytesMsg & _oInput) override;
};

5.3.2 定时器初始化

/*创建定时器文件描述符*/
bool ZinxTimerChannel::Init()
{
	bool bRet = false; //判断成功或者失败
	/*创建文件描述符*/
	int iFd = timerfd_create(CLOCK_MONOTONIC, 0);
	if (0 <= iFd)
	{
		/*设置定时周期*/
		struct itimerspec period = {
			{1,0}, {1,0}
		};
		if (0 == timerfd_settime(iFd, 0, &period, NULL))
		{
			bRet = true;
			m_TimerFd = iFd;  
		}
	}
	return bRet;
}
/*读取超时次数*/
bool ZinxTimerChannel::ReadFd(std::string & _input)
{
	bool bRet = false;
	char buff[8] = { 0 };

	if (sizeof(buff) == read(m_TimerFd, buff, sizeof(buff)))
	{
		bRet = true;
		_input.assign(buff, sizeof(buff));
	}
	return bRet;
}

bool ZinxTimerChannel::WriteFd(std::string & _output)
{
	return false;
}

/*关闭文件描述符*/
void ZinxTimerChannel::Fini()
{
	close(m_TimerFd);
	m_TimerFd = -1;
}

/*返回当前的定时器文件描述符*/
int ZinxTimerChannel::GetFd()
{
	return m_TimerFd;
}


std::string ZinxTimerChannel::GetChannelInfo()
{
	return "TimerFd"; // 名字随便起的
}

5.3.3 输出hello world

class output_hello :public AZinxHandler {
	// 通过 AZinxHandler 继承
	virtual IZinxMsg * InternelHandle(IZinxMsg & _oInput) override
	{
		auto pchannel = ZinxKernel::Zinx_GetChannel_ByInfo("stdout");
		std::string output = "hello world";
		ZinxKernel::Zinx_SendOut(output, *pchannel);
		return nullptr;
	}
	virtual AZinxHandler * GetNextHandler(IZinxMsg & _oNextMsg) override
	{
		return nullptr;
	}
} *pout_hello = new output_hello();

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

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

相关文章

传统库分表麻烦查询慢?TDengine 如何解决“搜狐基金”的应用难题

该项目需要实时展示国内基金的净值和收益&#xff08;货币基金&#xff09;&#xff0c;在保证满足折线图展示的功能基础上&#xff0c;还需要加入统计排行、分页展示等功能&#xff0c;为用户提供最全面实时的查询服务。此前搜狐基金团队使用的 MySQL 数据库在面对海量数据时存…

linux粘滞位的介绍及使用

文章目录 1.粘滞位的引入2.粘滞位的使用 1.粘滞位的引入 首先看一个场景 已知 对目录无w权限时 无法进行目录中的文件的创建/删除操作但是普通用户通过sudo命令 以root身份创建一个文件 rw- r-- r-- 普通用户此时是other 没有w权限 但却可以删除 [root和普通用户在一个目录下时…

【LittleXi】ICPC2023 南京站 总结

【LittleXi】ICPC2023 南京站 总结 赛前&#xff1a; 一周两次的vp训练 cf div2正常打、补题、沉淀&#xff0c;顺便vp了两把&#xff08;网瘾了&#xff09; 热身赛 熟悉了一下键盘&#xff0c;顺便交了一下A题的随机算法 测试评测了bitset、map、hashmap、打印时间&…

立冬问候语,立冬祝福语图片大全,温暖您整个冬天

立冬了&#xff0c;花落的声音冬知道&#xff0c;关爱的感觉心知道&#xff0c;立冬快乐&#xff0c;天凉保重&#xff01; 立冬寒气从天降&#xff0c;保重身心体健康&#xff0c;出入平安鸿运照&#xff0c;幸福快乐又吉祥&#xff0c;立冬&#xff0c;祝朋友们冬天幸福快乐安…

SpringBoot整合Canal+RabbitMQ监听数据变更(对rabbit进行模块封装)

SpringBootCanal(监听MySQL的binlog)RabbitMQ&#xff08;处理保存变更记录&#xff09; 在SpringBoot中采用一种与业务代码解耦合的方式&#xff0c;来实现数据的变更记录&#xff0c;记录的内容是新数据&#xff0c;如果是更新操作还得有旧数据内容。 使用Canal来监听MySQL的…

埃隆·马斯克旗下xAI推出PromptIDE工具,加速提示工程和可解释性研究

&#x1f989; AI新闻 &#x1f680; 埃隆马斯克旗下xAI推出PromptIDE工具&#xff0c;加速提示工程和可解释性研究 摘要&#xff1a;埃隆马斯克旗下人工智能初创公司xAI推出了PromptIDE工具&#xff0c;该工具是一个用于提示工程和可解释性研究的集成开发环境。通过该工具&a…

MATLAB|怎么将散点图替换成图片

目录 效果图 工具箱函数 输入参数: 输出参数 使用教程 案例1 案例3 案例4 案例5 案例6 案例7 案例8 扫一扫关注公众号&#xff1a; 今天教大家怎么把散点图替换成自己喜欢的图片&#xff0c;喜欢此推文的小伙伴们记得点赞关注分享&#xff01; 效果图 看了slandare…

【BUG解决】服务器没报警但是应用接口崩了....

最近遇到一个突发问题&#xff1a;服务器没报警但是应用接口崩了… 为其他业务系统提供一个接口&#xff0c;平时好好的&#xff0c;突然就嚷嚷反馈说访问不了了&#xff0c;吓得我赶紧跳起来&#xff01; 正常情况下在系统崩溃前&#xff0c;我会收到很多系统报警&#xff0…

【Unity】光照烘培-基础参数-基础设置

光照烘培 一级目录二级目录 问题目录烘焙光照在手机不起作用 一级目录 二级目录 Unity 2020.3.25 打开灯光面板 Wingdow -》 Rendering -> Lighting Lighting Settings 灯光设置文件 Realtime Lighting Realtime Global lllumin Realtime Environme Mixed Lighting Ba…

欧科云链:成本与规模之辨——合规科技如何赋能香港Web3生态?

作为国际金融中心&#xff0c;香港近两年来在虚拟资产及Web3领域频频发力。秉持着“稳步创新”的基本逻辑&#xff0c;香港在虚拟资产与Web3领域已建立一定优势&#xff0c;但近期各类风险事件的发生则让业界的关注焦点再次转向“安全”与“合规”。 在香港FinTech Week前夕&a…

【Hugging Face】如何下载模型文件

参考文章&#xff1a; 1、mac安装Homebrew - 知乎 2、 ssh连接 git lfs install git clone githf.co:bert-base-uncased -- 安装Homebrew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" -- 配置文件生效 source /Use…

创邻科技再获人行旗下《金融电子化》2023年度大奖

11月3日&#xff0c;由中国人民银行核心金融期刊《金融电子化》与第五届数字金融应用博览会共同主办的第14届金融科技大会在江苏苏州举行。 大会现场揭晓了2023年度“金融科技创新奖”&#xff0c;创邻科技的“Galaxybase全行级图应用平台”项目&#xff0c;从全国各金融机构送…

支持企业微信集成和登录!镭速传输新版本带来多项升级

近日&#xff0c;镭速发布了最新版本V.6.7.8.0&#xff0c;增加了一些新的功能和优化&#xff0c;为用户带来更好的体验。以下是本次更新的主要内容&#xff1a; 01 企业微信集成 企业微信登录功能为企业提供了更加便捷的用户管理和权限控制。在镭速的新版本中&#xff0c;通…

查出来这个表中evaluation_num字段中以2023开头的最大的尾数是几,instr用法

查出来这个表中evaluation_num字段中以2023开头的最大的尾数是几&#xff0c; sql如下&#xff1a; select max(to_number(substr(evaluation_num,instr(evaluation_num,-,1,2)1))) evaluation_num from tbl_lawsuit_index_assess_rec where to_char(create_time,yyyy)2023我…

某相亲网站白合网js逆向解析,★

文章目录 前言网址:参数加密确定加密位置和加密的参数解析加密方法完结撒花前言 可以关注我哟,一起学习,主页有更多练习例子 如果哪个练习我没有写清楚,可以留言我会补充 如果有加密的网站可以留言发给我,一起学习共享学习路程 如侵权,联系我删除 此文仅用于学习交流,请…

队列(定义,基本操作,顺序存储,链式存储)

目录 1.队列的定义1.重要术语2.基本操作 2.队列的顺序存储1.基本操作1.初始化2.判空3.入队&#xff08;循环队列&#xff09;4.出队5.读队头 2.判断队列已满/已空 3.队列的链式存储1.基本操作&#xff08;带头结点&#xff09;1.初始化2.判空3.入队4.出队5.队满条件 1.队列的定…

腾讯广告RACE曝光归因模型

今天我们以腾讯广告RACE曝光归因模型为例&#xff08;以下简称RACE模型&#xff09;&#xff0c;来聊聊行业在衡量广告效果上的努力与成效。 第一类&#xff1a;衡量转化以及转化过程的归因 如同前面所讲&#xff0c;如果只是衡量ROI&#xff0c;对广告投放的效果衡量就只有一…

idea配置插件JRebel and XRebel

激活 点击Help—>JRebel—>Activation 激活地址: http://server.52zhaoyue.cn/b56c9b61-2e80-4e31-82b4-15271a93e8c7 b56c9b61-2e80-4e31-82b4-15271a93e8c7生成地址https://www.guidgen.com/ 激活邮箱: 自由 激活及确认 方案1&#xff1a;激活后直接显示 激活信息…

第二证券:北交所30%的涨跌幅限制?

随着我国股市的不断发展&#xff0c;股市生意的涨跌幅束缚也成为了一个备受注重的论题。在北交所&#xff0c;股票的涨跌幅束缚为30%&#xff0c;这一束缚是否合理呢&#xff1f;本文将从多个角度进行剖析。 首先&#xff0c;涨跌幅束缚对于股市的安稳起着重要的效果。股票价格…

CSS实现透明度效果的两种方法—— opacity 和 rgba()

在实际开发过程中&#xff0c;为了给用户呈现一些效果&#xff0c;我们需要控制元素的透明度。CSS 提供了 opacity 属性和 rgba() 函数给我们控制透明度&#xff0c;接下来通过一个例子来感受一下两种方法的区别。 <style>.transparentBox {display: inline-block;width…