【第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.rhkb.cn/news/41749.html

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

相关文章

LlamaFactory部署及模型微调【win10环境】

1.Llama-Factory简介 LLaMA-Factory&#xff0c;全称 Large Language Model Factory&#xff0c;旨在简化大模型的微调过程&#xff0c;帮助开发者快速适应特定任务需求&#xff0c;提升模型表现。它支持多种预训练模型和微调算法&#xff0c;适用于智能客服、语音识别、机器翻…

Jmeter简介、学习目标及安装启动

1. 简介 JMeter 是 Apache 组织使用 Java 开发的一款测试工具&#xff1a;可以用于对服务器、网络或对象模拟巨大的负载&#xff1b;通过创建带有断言的脚本来验证程序是否能返回期望的结果。 1&#xff09;优点&#xff1a;开源、免费&#xff1b;跨平台&#xff1b;支持多协…

无参数读文件和RCE

什么是无参数&#xff1f; 无参数&#xff08;No-Argument&#xff09;的概念&#xff0c;顾名思义&#xff0c;就是在PHP中调用函数时&#xff0c;不传递任何参数。我们需要利用仅靠函数本身的返回值或嵌套无参数函数的方式&#xff0c;达到读取文件或远程命令执行&#xff0…

细胞内与细胞间网络整合分析!神经网络+细胞通讯,这个单细胞分析工具一箭双雕了(scTenifoldXct)

生信碱移 细胞间-细胞内通讯网络分析 scTenifoldXct&#xff0c;一种结合了细胞内和细胞间基因网络的计算工具&#xff0c;利用 scRNA-seq 数据检测细胞间相互作用。 单细胞 RNA 测序&#xff08;scRNA-seq&#xff09;能够以稳健且可重复的方式同时收集数万个细胞的转录组信息…

怎么处理 Vue 项目中的错误的?

一、错误类型 任何一个框架,对于错误的处理都是一种必备的能力 在Vue 中,则是定义了一套对应的错误处理规则给到使用者,且在源代码级别,对部分必要的过程做了一定的错误处理。 主要的错误来源包括: 后端接口错误代码中本身逻辑错误二、如何处理 后端接口错误 通过axi…

05.AI搭建preparationの(transformers01)BertTokenizer实现分词编码

一、下载 bert-base-chinese镜像下载 二、简介作用&#xff1a; 模型每个参数占用的字节大小模型大小模型大小层数头数GPT-14 个字节的 FP32 精度浮点数117M446MB1212GPT-22 个字节的 FP161.5亿到1.75亿0.5GB到1.5GB4816GPT-32 个字节的 FP161.75万亿&#xff08;17500亿&a…

工业4G路由器赋能智慧停车场高效管理

工业4G路由器作为智慧停车场管理系统通信核心&#xff0c;将停车场内的各个子系统连接起来&#xff0c;包括车牌识别系统、道闸控制系统、车位检测系统、收费系统以及监控系统等。通过4G网络&#xff0c;将这些系统采集到的数据传输到云端服务器或管理中心&#xff0c;实现信息…

git 基础操作

1. git 的安装 与 卸载 1.1. git 的安装 判断是否安装 git git --version 安装 git: centos: sudo yum -y install git ubuntu: sudo apt-get install git -y windows: 3.安装git和图形化界面工具_哔哩哔哩_bilibili 1.2. git 的卸载 判断是否安装 git git --version…

【计算机网络】计算机网络协议、接口与服务全面解析——结合生活化案例与图文详解

协议、接口与服务 导读一、协议1.1 定义1.2 组成 二、接口三、服务3.1 定义3.2 服务与协议的区别3.3 分类3.3.1 面向连接服务于无连接服务3.3.2 可靠服务和不可靠服务3.3.3 有应答服务和无应答服务 结语 导读 大家好&#xff0c;很高兴又和大家见面啦&#xff01;&#xff01;…

Vue.js 完全指南:从入门到精通

1. Vue.js 简介 1.1 什么是 Vue.js? Vue.js(通常简称为 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架。所谓"渐进式",意味着 Vue 的设计是由浅入深的,你可以根据自己的需求选择使用它的一部分或全部功能。 Vue 最初由尤雨溪(Evan You)在 2014 年创…

qt QOffscreenSurface详解

1、概述 QOffscreenSurface 是 Qt 中用于离屏渲染的一个类。它允许在不直接与屏幕交互的情况下进行 OpenGL 渲染操作&#xff0c;常用于生成纹理、预渲染场景等。通过 QOffscreenSurface&#xff0c;可以在后台创建一个渲染表面&#xff0c;进行绘制操作&#xff0c;并将结果捕…

如何使用VS中的Android Game Development Extension (AGDE) 来查看安卓 Logcat 日志

一、首先按照以下 指引 中的 第1、2步骤&#xff0c;安装一下 AGDE &#xff0c;AGDE 的安装包可以在官网上找到。 UE4 使用AndroidGameDevelopmentExtension&#xff08;AGDE&#xff09;对安卓客户端做“断点调试”与“代码热更”-CSDN博客 在执行第二步骤前&#xff0c;记得…

NodeJs之fs模块

一、定义&#xff1a; fs 模块可以实现与硬盘的交互。例如&#xff1a;文件的创建、删除、重命名、移动&#xff1b;文件内容的写入、读取&#xff1b;文件夹的操作。 二、引入 fs 模块&#xff1a; const fs require(fs)三、文件写入&#xff1a; 1、异步写入&#xff1a;w…

Android14 Settings应用添加有线网开关条目实现

Android14 Settings应用添加有线网开关条目 文章目录 Android14 Settings应用添加有线网开关条目一、前言二、适配修改1、network_provider_settings.xml2、NetworkProviderSettings.java3、TurnOnOffEthernetNetworkController.java4、去除有线网提示条目。5、效果UI&#xff…

微信小程序如何接入直播功能

一、小程序直播开通背景 1.政府资质要求 政府的要求&#xff0c;小程序开通直播需要注册主体具备互联网直播的资质&#xff0c;普通企业需要《信息网络传播视听节目许可证》&#xff0c;表演性质的直播需要《网络文化经营许可证》&#xff0c;政府主体需要《社会信用代码》及…

【Linux】MAC帧

目录 一、MAC帧 &#xff08;一&#xff09;IP地址和MAC地址 &#xff08;二&#xff09;MAC帧格式 &#xff08;三&#xff09;MTU对IP协议的影响、 &#xff08;四&#xff09;MTU对UDP协议的影响 &#xff08;五&#xff09;MTU对TCP协议的影响 二、以太网协议 &…

如何查看window电脑的GPU信息

GPU&#xff08;图形处理器&#xff0c;Graphics Processing Unit&#xff09;和显卡是两个密切相关但不同的概念 概念 1. ‌基本概念‌ ‌GPU‌&#xff1a;是专门用于处理图像和视频信息的微处理器&#xff0c;拥有强大的并行计算能力&#xff0c;主要负责图形渲染、数值分…

大数据判存算法

所谓的大数据判存算法&#xff0c;就是如何在海量数据中快速判断某个数据是否存在。这里用到的知识是布隆过滤器&#xff08;Bloom Filter&#xff09;&#xff0c;下面按照 what - why - how 的顺序来学习它。 1、什么是布隆过滤器 布隆过滤器&#xff08;英语&#xff1a;B…

Solr-搜索引擎-入门到精通

以下是对 Apache Solr 的简介及其常用语法的快速入门指南&#xff1a; 一、Solr 是什么&#xff1f; • 核心定位&#xff1a;Apache Solr 是一个基于 Lucene 的高性能、开源的搜索平台&#xff0c;支持全文检索、分词、高亮、聚合统计等功能。 • 核心功能&#xff1a; • 全…

Ajax与Axios,以及Apifox的入门使用

Ajax与Axios&#xff0c;以及Apifox的入门使用 作者&#xff1a;blue 时间&#xff1a;2025.3.20 文章目录 Ajax与Axios&#xff0c;以及Apifox的入门使用1.Ajax2.Axios3.Apifox的基本使用内容Path 参数定义语法用途 Query 参数定义语法用途 1.Ajax 概念&#xff1a;Asynchr…