【第23节】windows网络编程模型(WSAEventSelect模型)

目录

引言

一、WSAEventSelect模型概述 

二、 WSAEventSelect模型的实现流程

2.1 创建一个事件对象,注册网络事件

2.2 等待网络事件发生

2.3 获取网络事件

2.4 手动设置信号量和释放资源

三、 WSAEventSelect模型伪代码示例

四、完整实践示例代码


引言

        在网络编程的复杂世界里,如何高效捕捉和响应网络事件是构建稳健应用的关键。WSAEventSelect模型作为一种强大的异步I/O模型,为开发者开辟了一条独特路径。它借助事件驱动机制,让应用程序能够敏锐感知套接字上的网络动态。与WSAAsyncSelect模型不同,它以事件而非消息形式传递通知,为网络编程带来别样灵活性。接下来,让我们深入探索WSAEventSelect模型的工作原理、实现流程以及实际应用示例。

一、WSAEventSelect模型概述 

        Windows Sockets异步事件选择模型,也就是WSAEventSelect模型,属于另一种异步I/O模型。利用这个模型,应用程序能够在单个或多个套接字上,基于事件接收网络方面的通知。

        WSAEventSelect模型和WSAAsyncSelect模型有所不同,主要区别就在于应用程序接收网络事件通知的方式。WSAEventSelect模型通过事件来告诉应用程序网络事件发生了,而WSAAsyncSelect模型是依靠消息来通知。但从根本上来说,在应用程序接收网络事件通知这件事上,这两个模型都是被动的。意思就是,只有网络事件真正发生的时候,系统才会向应用程序发出通知 。 

二、 WSAEventSelect模型的实现流程

初始化套接字、绑定端口及IP、监听这前三步在此略过。

2.1 创建一个事件对象,注册网络事件

        在应用程序调用WSAEventSelect函数之前,必须先创建一个事件对象。当获取到一个socket后,需使用WSACreateEvent函数来创建事件对象,之后再使用WSAEventSelect函数进行相关操作。

WSAEVENT WSACreateEvent(void);

该函数的返回值为事件对象句柄。

int WSAEventSelect(SOCKET s,             //当前服务端的SOCK句柄WSAEVENT hEventObject, //事件对象句柄long lNetworkEvents   //网络事件
);

         注:当调用WSAEventSelect函数后,套接字会被自动设置为非阻塞模式。若要将套接字设置为阻塞模式,则必须把参数lNetworkEvents设置为0。


示例:

//创建一个事件对象
WSAEVENT wsaEvent = WSACreateEvent();
if (WSA_INVALID_EVENT == wsaEvent) {printf("创建一个事件对象失败!");closesocket(sSocket);WSACleanup();
}
//注册网络事件
if (WSAEventSelect(sSocket,  wsaEvent,  //当前服务端的SOCK句柄//事件对象句柄FD_ACCEPT | FD_CLOSE)) {    //网络事件printf("注册网络事件失败!");closesocket(sSocket);   //关闭套接字WSACleanup();//释放套接字资源return FALSE;
}

2.2 等待网络事件发生

        在WinSockets应用程序里,先用WSAEventSelect函数给套接字把网络事件注册好,紧接着就得调用WSAWaitForMultipleEvents函数,目的是等着网络事件发生。这个WSAWaitForMultipleEvents函数的作用就是,一直等到有一个事件对象或者所有事件对象进入“有信号量”状态,又或者是函数调用时间到了(超时),它才会返回结果 。 

DWORD WSAWaitForMultipleEvents(DWORD cEvents,           //事件对象句柄数量WSAEVENT FAR*lphEvents, //指向事件对象句柄的指针BOOL fWaitAll,           //等待事件句柄的数量DWORD dwTimeout,         //调用该函数的阻塞时间BOOL fAlertable          //完成例程后是否继续等待
);

        WSAWaitForMultipleEvents这个函数,最多能够处理64个对象。所以呢,基于这个情况,使用这个I/O模型的时候,在一个线程里,同一时刻最多也就只能支持64个套接字。要是你想用这个模型去管理超过64个套接字,那就得再创建一些额外的工作线程才行。 

        WSAWaitForMultipleEvents函数的作用就是等着网络事件发生。只要在规定的时间里,有网络事件出现了,那这个函数返回的值,就能告诉你是哪个事件对象导致函数返回的 。 

        注:要是fWaitAll这个参数设成true,那所有的事件对象都会被置为有信号量状态。要是fWaitAll被设成FALSE,那么只要众多事件句柄当中有一个变为有信号量状态就可以了。这个函数运行结束后会给出一个返回值,这个返回值其实是个索引。用这个索引减去WSA_WAIT_EVENT_0这个宏的值,就能够知道在事件数组里,哪个事件被触发了,也就是能找到被触发事件在数组中的位置 。 


示例:

//定义事件对象数组
EventArray[WSA_MAXIMUMWAIT_EVENTS] = {};
//等待网络事件的发生
DWORD dwIndex =
WSAWaitForMultipleEvents(uEventCount, EventArray,  FALSE,
WSA_INFINITE,
FALSE);        
//完成例程后是否继续等待
//返回该事件在EventArray数组中的位置(下标从0开始)
dwIndex = dwIndex - WSA_WAIT_EVENT_0;

2.3 获取网络事件

        利用WSAWaitForMultipleEvents函数的返回值,我们能知道哪个套接字发生了网络事件。不过,仅仅知道是哪个套接字还不够,应用程序还得弄清楚在这个套接字上具体发生了哪种网络事件。WSAEnumNetworkEvents函数就派上用场了,它能找出套接字上发生的网络事件,同时把系统里关于这个网络事件的记录清除掉,还会把事件对象重新设置回初始状态 。

int WSAEnumNetworkEvents(SOCKET s,                          //发生网络事件的套接字句柄WSAEVENT hEventObject,             //被重置的事件对象句柄LPWSANETWORKEVENTS lpNetworkEvents //网络事件的记录和相应错误码
);
typedef struct _WSANETWORKEVENTS {long lNetworkEvents;               //网络事件int iErrorCode[FD_MAX_EVENTS];     //错误码
}

WSAEnumNetworkEvents参数

 _WSANETWORKEVENTS 参数

         使用方式:当lNetworkEvents&FD_XX为TRUE时,即表示发生了此网络事件。


示例:

Socket SocketArray[WSA_MAXIMUM_WAIT_EVENTS] = {};
int uEventCount = 0;                   //记录当前事件和套接字的个数
WSAEnumNetworkEvents(SocketArray[dwIndex],//发生网络事件的套接字句柄
EventArray[dwIndex],//被重置的事件对象句柄
&NetworkEvents))     //网络事件的记录和相应错误码
//响应网络事件
if ((NetworkEvents.lNetworkEvents & FD_ACCEPT) &&0 == NetworkEvents.iErrorCode[FD_ACCEPT_BIT]) {// 处理逻辑
}

        这个函数创建的事件对象,有“手动重设”和“自动重设”这两种工作模式。咱们这里创建的事件对象,是按手动方式工作的,一开始它处于无信号状态。一旦网络事件发生,跟套接字相关联的这个事件对象,就会从无信号量的状态变成有信号量状态。因为是“手动重设”模式,所以应用程序把相关事件处理完之后,得把这个有信号量的事件对象,再变回无信号量状态。 有个MAX_NUM_SOCKET宏,它的值是64 ,一般来说,这代表一个线程最多能同时等待处理64个事件,也就是说一个线程最多只能同时盯着64个socket。要是超过了这个数量,就必须再开启新的线程来处理。 调用这个函数的时候,如果hEventObject参数不是NULL,那么这个事件对象就会被自动重置为“无信号”状态;要是hEventObject参数是NULL,那就得调用WSAResetEvent函数,把事件设置成“无信号”状态 。 

2.4 手动设置信号量和释放资源

BOOL WSAResetEvent(WSAEVENT hEvent //要设置为无信号量的事件对象句柄
);


WSAResetEvent函数用于将事件对象从“有信号量”设置为“无信号量”。

BOOL WSACloseEvent(WSAEVENT hEvent //要释放资源的事件对象句柄
);

        应用程序完成网络事件的处理后,需要使用WSACloseEvent函数释放事件对象所占用的系统资源。

三、 WSAEventSelect模型伪代码示例

#include <Winsock2.h>
#pragma comment(lib,"Ws2_32.lib")
typedef struct _EVENT_SOCKET_INFO {WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];SOCKET SocketArray[WSA_MAXIMUM_WAIT_EVENTS];
}EVENT_SOCKET_INFO, *PEVENT_SOCKET_INFO;
BOOL SetSocket() {//1.初始化套接字WSADATA  stcData;int nResult;nResult = WSAStartup(MAKEWORD(2, 2), &stcData);if (nResult == SOCKET_ERROR)return FALSE;//2.创建套接字// 此处代码省略//3.初始化地址定址sockaddr_in sAddr = {0};sAddr.sin_family = AF_INET;sAddr.sin_port = htons(1234);sAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");int nSaddrLen = sizeof(sockaddr_in);//4.绑定int nRet = 0;nRet = bind(sSocket,(sockaddr*)&sAddr,sizeof  (sockaddr_in));if (SOCKET_ERROR == nRet) {printf("绑定IP定址失败");closesocket(sSocket);WSACleanup();}//接收返回信息//当前客户端SOCK句柄//IP定址//IP定址结构体大小//关闭套接字//释放套接字资源//WSANETWORKEVENTS NetworkEvents = {0};//网络事件的记录和相应错误码SOCKET                            sClientSocket           = 0;  //当前发送事件客户端的SOCK句柄UINT                                 uEventCount           = 0;  //事件对象句柄数量CLIENTINFO                  ClientInfo              = {0};//当前发送事件的客户端EVENT_SOCKET_INFO       EventSocketInfo = {0};//保存事件和Sock信息//5.监听if (listen(sSocket, SOMAXCONN)) {printf("    监听失败!");closesocket(sSocket);WSACleanup();}//6.创建一个事件对象WSAEVENT wsaEvent = WSACreateEvent();if (WSA_INVALID_EVENT == wsaEvent) {printf("创建一个事件对象失败!");closesocket(sSocket);WSACleanup();}//关闭套接字//释放套接字资源//7.注册网络事件if (WSAEventSelect(sSocket,wsaEvent,//当前服务端的SOCK句柄//事件对象句柄FD_ACCEPT | FD_CLOSE))//网络事件{printf("注册网络事件失败!");closesocket(sSocket);WSACleanup();//关闭套接字//释放套接字资源return FALSE;}//保存事件对象和套接字EventSocketInfo.EventArray[uEventCount]      = wsaEvent;EventSocketInfo.SocketArray[uEventCount++] = sSocket;while (TRUE) {WSAEVENT       EventArray[WSA_MAXIMUM_WAIT_EVENTS];//8.等待网络事件的发生DWORD dwIndex = WSAWaitForMultipleEvents(uEventCount,EventSocketInfo.EventArray,FALSE,WSA_INFINITE,FALSE);if (WSA_WAIT_FAILED == dwIndex)continue;//手动设置无信号//WSAResetEvent(EventSocketInfo.EventArray);//9.找到有信号的对象的标号//WSAWaitForMultipleEvent       函数的返回值-WSA_WAIT_EVENT_0dwIndex    = dwIndex - WSA_WAIT_EVENT_0;if (WSAEnumNetworkEvents(EventSocketInfo.SocketArray[dwIndex],//               发生网络事件的套接字句柄EventSocketInfo.EventArray                 [dwIndex],//被重置的事件对象句柄&NetworkEvents))                                               //网络事件的记录和相应错误码{printf("     调用WSAEnumNetworkEvents失败!");closesocket(sSocket);                                      //关闭套接字WSACleanup();                                                         //释放套接字资源}//10.响应网络事件//10.1连接if ((NetworkEvents.lNetworkEvents & FD_ACCEPT) &&0 == NetworkEvents.iErrorCode[FD_ACCEPT_BIT]) {sClientSocket       = accept(sSocket, (sockaddr*)&sAddr, &nSaddrLen);if (INVALID_SOCKET        == sClientSocket) {printf("     连接客户端失败!");continue;}//为新客户端创建网络事件//1.为刚连接进来的客户端创建事件对象EventSocketInfo.EventArray[uEventCount] = WSACreateEvent();//2.保存当前连接进来的客户端Socket 套接字EventSocketInfo.SocketArray[uEventCount] = sClientSocket;//3.为该客户端注册网络事件WSAEventSelect(sClientSocket,EventSocketInfo.EventArray[uEventCount],FD_READ | FD_WRITE                    | FD_CLOSE);uEventCount++;ClientInfo.ClientSock                  = sClientSocket;g_ClientInfo.push_back(ClientInfo);continue;}//10.2接收消息if ((NetworkEvents.lNetworkEvents & FD_READ) &&0        == NetworkEvents.iErrorCode[FD_READ_BIT]) {// 接收数据处理逻辑continue;}//10.3关闭事件if ((NetworkEvents.lNetworkEvents & FD_CLOSE) &&(0 == NetworkEvents.iErrorCode[FD   CLOSE_BIT])) {//关闭Socket套接字和释放事件对象占有的资源closesocket(EventSocketInfo.SocketArray[dwIndex]);WSACloseEvent(EventSocketInfo.EventArray[dwIndex]);//将退出的客户端从事件数组中删除,并将之后的数据向前移动for (int i = dwIndex; i < uEventCount; i++) {  //线性表EventSocketInfo.EventArray[dwIndex]= EventSocketInfo.EventArray[dwIndex + 1];EventSocketInfo.SocketArray[dwIndex]= EventSocketInfo.SocketArray[dwIndex + 1];}uEventCount--;continue;}}return TRUE;
}

        WSAEventSelect模型作为一种异步I/O模型,通过事件机制实现网络事件的通知与处理,在网络编程中为应用程序提供了一种有效的处理网络操作的方式,了解其原理和实现流程有助于开发者编写出更高效、稳定的网络应用程序。

四、完整实践示例代码

头文件initsock.h:

#include <winsock2.h>
#pragma comment(lib, "WS2_32")  // 链接到 WS2_32.libclass CInitSock
{
public:/*CInitSock 的构造器*/CInitSock(BYTE minorVer = 2, BYTE majorVer = 2){// 初始化WS2_32.dllWSADATA wsaData;WORD sockVersion = MAKEWORD(minorVer, majorVer);if (::WSAStartup(sockVersion, &wsaData) != 0){exit(0);}}/*CInitSock 的析构器*/~CInitSock(){::WSACleanup();}
};

 服务端代码:

#include "initsock.h"
#include <iostream>
using namespace std;// 初始化Winsock库
CInitSock theSock;int main()
{// 事件句柄和套节字句柄表WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];SOCKET    sockArray[WSA_MAXIMUM_WAIT_EVENTS];int nEventTotal = 0;// 创建监听套节字SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(4567);    // 此服务器监听的端口号sin.sin_addr.S_un.S_addr = INADDR_ANY;if (::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR){cout << " Failed bind()" << endl;return -1;}// 进入监听模式if (::listen(sListen, 5) == SOCKET_ERROR){cout << " Failed listen()" << endl;return 0;}cout << "服务器已启动监听,可以接收连接!" << endl;// 创建事件对象,并关联到新的套节字WSAEVENT event = ::WSACreateEvent();::WSAEventSelect(sListen, event, FD_ACCEPT | FD_CLOSE);// 添加到表中eventArray[nEventTotal] = event;sockArray[nEventTotal] = sListen;nEventTotal++;// 处理网络事件while (TRUE){// 在所有事件对象上等待int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);// 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态nIndex = nIndex - WSA_WAIT_EVENT_0;for (int i = nIndex; i < nEventTotal; i++){nIndex = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE);if (nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT){continue;}else{// 获取到来的通知消息,WSAEnumNetworkEvents函数会自动重置受信事件WSANETWORKEVENTS event;::WSAEnumNetworkEvents(sockArray[i], eventArray[i], &event);if (event.lNetworkEvents & FD_ACCEPT)                // 处理FD_ACCEPT通知消息{if (event.iErrorCode[FD_ACCEPT_BIT] == 0){if (nEventTotal > WSA_MAXIMUM_WAIT_EVENTS){cout << " Too many connections!" << endl;continue;}sockaddr_in addrRemote;int nAddrLen = sizeof(addrRemote);SOCKET sNew = ::accept(sockArray[i], (SOCKADDR*)&addrRemote, &nAddrLen);cout << "\n与主机" << ::inet_ntoa(addrRemote.sin_addr) << "建立连接" << endl;WSAEVENT event = ::WSACreateEvent();::WSAEventSelect(sNew, event, FD_READ | FD_CLOSE | FD_WRITE);// 添加到表中eventArray[nEventTotal] = event;sockArray[nEventTotal] = sNew;nEventTotal++;}}else if (event.lNetworkEvents & FD_READ)         // 处理FD_READ通知消息{if (event.iErrorCode[FD_READ_BIT] == 0){char szText[256];int nRecv = ::recv(sockArray[i], szText, strlen(szText), 0);if (nRecv > 0){szText[nRecv] = '\0';cout << "  接收到数据:" << szText << endl;}// 向客户端发送数据char sendText[] = "你好,客户端!";if (::send(sockArray[i], sendText, strlen(sendText), 0) > 0){cout << "  向客户端发送数据:" << sendText << endl;}}}else if (event.lNetworkEvents & FD_CLOSE)        // 处理FD_CLOSE通知消息{if (event.iErrorCode[FD_CLOSE_BIT] == 0){::closesocket(sockArray[i]);for (int j = i; j < nEventTotal - 1; j++){eventArray[j] = eventArray[j + 1];sockArray[j] =  sockArray[j + 1];}nEventTotal--;}}}}}return 0;
}

客户端代码:

#include "InitSock.h"
#include <iostream>
using namespace std;CInitSock initSock;     // 初始化Winsock库int main()
{// 创建套节字SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (s == INVALID_SOCKET){cout << " Failed socket()" << endl;return 0;}// 也可以在这里调用bind函数绑定一个本地地址// 否则系统将会自动安排char address[20] = "127.0.0.1";// 填写远程地址信息sockaddr_in servAddr;servAddr.sin_family = AF_INET;servAddr.sin_port = htons(4567);// 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址// 如果你的计算机没有联网,直接使用127.0.0.1即可servAddr.sin_addr.S_un.S_addr = inet_addr(address);if (::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1){cout << " Failed connect() " << endl;return 0;}else {cout << "与服务器 " << address << "建立连接" << endl;}char szText[] = "你好,服务器!";if (::send(s, szText, strlen(szText), 0) > 0){cout << "  发送数据:" << szText << endl;}// 接收数据char buff[256];int nRecv = ::recv(s, buff, 256, 0);if (nRecv > 0){buff[nRecv] = '\0';cout << "  接收到数据:" << buff << endl;}// 关闭套节字::closesocket(s);return 0;
}

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

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

相关文章

概率预测之NGBoost(Natural Gradient Boosting)回归和分位数(Quantile Regression)回归

概率预测之NGBoost(Natural Gradient Boosting)回归和线性分位数回归 NGBoostNGBoost超参数解释NGBoost.fitscore(X, Y)staged_predict(X)feature_importances_pred_dist 方法来获取概率分布对象分位数回归(Quantile Regression)smf.quantreg 对多变量数据进行分位数回归分…

手撕算法——链表

算法基础——链表-CSDN博客 一、排队顺序 题⽬来源&#xff1a;洛⾕ 题⽬链接&#xff1a;B3630 排队顺序 - 洛谷 难度系数&#xff1a;★ 1. 题目描述 2. 算法原理 本题相当于告诉了我们每⼀个点的后继&#xff0c;使⽤静态链表的存储⽅式能够很好的还原这个队列。 数组中 [1,…

RAG优化:python从零实现[吃一堑长一智]循环反馈Feedback

本文将介绍一种有反馈循环机制的RAG系统,让当AI学会"吃一堑长一智",给传统RAG装了个"后悔"系统,让AI能记住哪些回答被用户点赞/拍砖,从此告别金鱼记忆: 每次回答都像在玩roguelike:失败结局会强化下次冒险悄悄把优质问答变成新知识卡牌,实现"以…

kotlin init执行顺序

一 代码 kotlin: package test.fclass Test1 { }class TestInit(s: String, i: Int) {var name: String? nullvar age 0private var a :Int 1init {this.name sthis.age iprintln("init代码块: $name, $age")}}转成java // Test1.java package test.f;import…

使用cursor开发java案例——springboot整合elasticsearch

安装elasticsearch 打开cursor&#xff0c;输入如下提示词 使用springboot整合elasticsearch。其中elasticsearch服务器ip&#xff1a;192.168.236.134 管理员用户名elastic 管理员密码 PdQy_xfR2yLhpok*MK_ 监听端口9200点Accept all 使用idea打开生成的项目 &#xff0…

Java Collection API增强功能系列之一 Arrays.asList()

在Java编程中&#xff0c;Arrays.asList() 是一个高频使用却又容易引发陷阱的工具方法。它能够快速将数组转换为列表&#xff0c;但其特殊行为常常让开发者踩坑。本文将深入剖析该方法的本质特性&#xff0c;并揭示其使用时的注意事项。一、方法定义与基础用法 1. 方法签名 p…

vue3 项目的最新eslint9 + prettier 配置

注意&#xff1a;eslint目前升级到9版本了 在 ESLint v9 中&#xff0c;配置文件已经从 .eslintrc 迁移到了 eslint.config.js 配置的方式和之前的方式不太一样了&#xff01;&#xff01;&#xff01;&#xff01; 详见自己的语雀文档&#xff1a;5、新版eslint9prettier 配…

基于FPGA的16QAM+帧同步系统verilog开发,包含testbench,高斯信道,误码统计,可设置SNR

目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1 16QAM调制解调原理 2.2 帧同步 3.Verilog核心程序 4.完整算法代码文件获得 1.算法仿真效果 vivado2019.2仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 设置SNR12db 将FPGA数据导入到MATLAB显…

[学成在线]06-视频分片上传

上传视频 需求分析 教学机构人员进入媒资管理列表查询自己上传的媒资文件。 点击“媒资管理” 进入媒资管理列表页面查询本机构上传的媒资文件。 教育机构用户在"媒资管理"页面中点击 "上传视频" 按钮。 点击“上传视频”打开上传页面 选择要上传的文件…

Maven安装与环境配置

首先我们先介绍一些关于Maven的知识&#xff0c;如果着急直接看下面的安装教程。 目录 Maven介绍 Maven模型 Maven仓库 Maven安装 下载 安装步骤 Maven介绍 Apache Maven是一个项目管理和构建工具&#xff0c;它基于项目对象模型(Project Object Model , 简称: POM)的概念…

【新能源汽车温度采集与控制系统设计深度解析】

面向汽车行业研发与测试测量设备从业者的技术指南 一、硬件架构设计 新能源汽车的温度采集与控制系统是保障电池、电机、电控等核心部件安全运行的核心技术之一。其硬件架构需兼顾高精度、抗干扰、可靠性与集成化&#xff0c;以下从信号调理电路、ADC模块、隔离设计三个维度展…

AI Tokenization

AI Tokenization 人工智能分词初步了解 类似现在这个&#xff0c;一格子 一格子&#xff0c;拼接出来的&#xff0c;一行或者一句&#xff0c;像不像&#xff0c;我们人类思考的时候组装出来的话&#xff0c;并用嘴说出来了呢。

React(四)setState原理-性能优化-ref

setState详解 实现原理 开发中我们并不能直接修改State来重新渲染界面&#xff1a; 因为修改State之后&#xff0c;希望React根据最新的State来重新渲染界面&#xff0c;但这种方式的修改React并不知道数据发生了变化&#xff1b; React并没有类似于Vue2中的Object.defineP…

SSH密钥认证 + 文件系统权限控制 + Git仓库配置+封存与解封GIT仓库

在本地服务器上实现多个用户仅通过git push操作修改仓库、禁止其他改写方式的需求&#xff0c;可以通过以下步骤实现&#xff1a; 方法概述 通过SSH密钥认证 文件系统权限控制 Git仓库配置&#xff0c;确保用户仅能通过git push命令提交修改&#xff0c;而无法通过直接操作服…

全文通读:126页华为IPD集成产品开发与DFX实战【文末附可编辑PPT下载链接】

绑定资料内容: 12023华为流程体系及落地实施【108页 PPT】.pptx22024版基于华为IPD与质量管理体系融合的研发质量管理【63页】.pptx

//TODO 动态代理的本质?

待解决 //TODO 面试题 为啥mybatis的mapper只有接口没有实现类&#xff0c;但它却能工作&#xff1f;?(ai参考,待深究源码) 1. 动态代理生成代理对象 MyBatis 使用 JDK 动态代理 为每个 Mapper 接口生成代理对象&#xff1a; • 核心类&#xff1a;MapperProxy&#xff08;…

C++11中智能指针的使用(shared_ptr、unique_ptr、weak_ptr)

C11中智能指针的使用(shared_ptr、unique_ptr、weak_ptr) 一、shared_ptr原理 shared_ptr 是另一种智能指针&#xff0c;用于实现多个 shared_ptr 实例共享同一个对象的所有权。它通过内部的控制块&#xff08;通常是一个包含计数器和指向对象的指针的结构&#xff09;来管理…

2024年认证杯SPSSPRO杯数学建模B题(第二阶段)神经外科手术的定位与导航全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 B题 神经外科手术的定位与导航 原题再现&#xff1a; 人的大脑结构非常复杂&#xff0c;内部交织密布着神经和血管&#xff0c;所以在大脑内做手术具有非常高的精细和复杂程度。例如神经外科的肿瘤切除手术或血肿清除手术&#xff0c;通常需要…

尝试在软考62天前开始成为软件设计师-信息系统安全

安全属性 保密性:最小授权原则(能干活的最小权限)、防暴露(隐藏)、信息加密、物理保密完整性(防篡改):安全协议、校验码、密码校验、数字签名、公证 可用性:综合保障( IP过滤、业务流控制、路由选择控制、审计跟踪)不可抵赖性:数字签名 对称加密 DES :替换移位 3重DESAESR…

Rocky9.5基于sealos快速部署k8s集群

首先需要下载 Sealos 命令行工具&#xff0c;sealos 是一个简单的 Golang 二进制文件&#xff0c;可以安装在大多数 Linux 操作系统中。 以下是一些基本的安装要求&#xff1a; 每个集群节点应该有不同的主机名。主机名不要带下划线。 所有节点的时间需要同步。 需要在 K8s …