【第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.pswp.cn/pingmian/73334.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73334.shtml
英文地址,請注明出處:http://en.pswp.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 …