【第21節】windows sdk編程:網絡編程基礎

目錄

引言:網絡編程基礎

一、socket介紹(套接字)

1.1 Berkeley Socket套接字

1.2 WinSocket套接字

1.3 WSAtartup函數

1.4 socket函數

1.5 字節序轉換

1.6 綁定套接字

1.7 監聽

1.8 連接

1.9 接收數據

1.10 發送數據

1.11 關閉套接字

二、UDP連接流程

2.1 接收數據

2.2 發送數據

三、阻塞與非阻塞模式

四、示例代碼

4.1 TCP協議代碼

4.2 UDP協議代碼


引言:網絡編程基礎

????????在網絡編程領域,實現高效、可靠的網絡通信至關重要。套接字(Socket)作為網絡通信的關鍵接口,在其中扮演著核心角色。從最初加利福尼亞大學Berkeley分校為UNIX系統開發的Berkeley Socket,到后來多家公司共同制定的Windows Sockets規范,套接字不斷發展完善。了解套接字的原理、相關函數的使用以及不同網絡協議(如TCP、UDP)的連接流程,對于開發穩定的網絡應用程序意義重大。

一、socket介紹(套接字)

1.1 Berkeley Socket套接字

????????套接字(Socket)最初是由加利福尼亞大學Berkeley分校專門為UNIX操作系統搞出來的網絡通信接口。時間回到20世紀80年代初,這所學校把美國國防部高研署提供的TCP/IP整合進了Unix系統里,緊接著,很快就開發出了TCP/IP應用程序接口(API),這個接口其實就是Socket(套接字)接口。后來,UNIX操作系統用的人越來越多,套接字也跟著火了起來,成了現在最常用的網絡通信應用程序接口之一。?

1.2 WinSocket套接字

????????在90年代初期,Sun Microsystems、JSB Corporation、FTP software、Microdyne還有Microsoft這幾家公司一起搞出了一套標準,叫做Windows Sockets規范。這個規范是對Berkeley Sockets的一個重要升級。具體來說,它新添了一些異步函數,還弄出了符合Windows消息驅動特點的網絡事件異步選擇機制。?

????????Windows Sockets規范是一套開放的網絡編程接口,能支持多種協議,專門用于Windows系統。在實際使用中,Windows Sockets規范主要有1.1版和2.0版這兩個版本。1.1版只能支持TCP/IP協議,而2.0版就厲害了,它能支持好幾種協議,并且對于之前的版本,也能很好地兼容,老程序也能正常使用。?

TCP連接流程


1. 包含必要的頭文件及庫:

#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")

2. 指定需要使用的Winsock規范的最高版本,并初始化Winsock,裝入Winsock.dll:

WSAStartup(MAKEWORD(2,2),&wsaDATA);

3. 創建套接字:

socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

4. 綁定IP和端口:

bind(sock,(sockaddr*)&addr,sizeof(sockaddr_in));

5. 監聽:

listen(sock,SOMAXCONN);

6. 連接客戶端:

accept(sock,(sockaddr*)&addrClient,&nAddrSize);

7. 接收數據:

recv(sockClient,buf,1024,0);

8. 發送數據:

send(Sock,message,nSize,O);

9. 在調用“closesocket”函數之后,但是在程序結束之前需要清理Winsock:

closesocket(sock);  //關閉套接字
WSACleanup();

1.3 WSAtartup函數

????????不管是開發客戶端還是服務端的Socket應用程序,都必須先加載Windows Sockets動態庫,通常使用WSAtartup函數來實現這個功能。

int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData
);

- `wVersionRequested`:通過`MAKEWORD(X,Y)`宏定義來表示,`X`(高位)表示次版本號,`Y`(低位)表示主版本號,即期望調用者使用的WinSocket版本。
- `lpWSAData`:指向`WSADATA`結構體,用于返回被加載動態庫的有關信息。

typedef struct WSAData {WORD wVersion;  //期望調用者使用的WinSocket版本WORD wHighVersion;//DLL支持的最高版本char szDescription[WSADESCRIPTION_LEN + 1];//DLL的描述信息char szSystemStatus[WSASYS_STATUS_LEN + 1];//DLL的狀態信息unsigned short iMaxSockets;//一個進程可以打開套接字最多數量unsigned short iMaxUdpDg;char FAR* lpVendorInfo;//一個進程發送或接收的最大數據的長度
}WSADATA,*LPWSADATA;

1.4 socket函數

????????初始化WinSocket DLL后,通過`scoket`函數和`WSASocket`函數來創建套接字。該函數調用成功后,會返回一個新建的套接字句柄。

SOCKET WSAAPI socket(_In_  int af,     //通信協議族_In_ int type,      //套接字類型_In_ int protocol);//傳輸協議

af:
??? - `AF_INET`:internet協議(IP V4)
??? - `AF_IRDA`:紅外協議
??? - `AF_BTH`:藍牙協議
type:
??? - `SOCK_STREAM`:流式socket(需建立連接,通信過程可靠TCP)
??? - `SOCK_DGRAM`:數據報socket(無需建立連接,通訊過程不可靠UDP)
??? - `SOCK_RAW`:原始套接字
Protocol:
??? - 對于`SOCK_STREAM`套接字類型,該字段為`IPPROTO_TCP`或者`0`。
??? - 對于`SOCK_DGRAM`套接字類型,該字段為`IPPROTO_UDP`或者`0`。

(1)流套接字????????

????????流套接字能夠提供雙向的數據流服務,數據傳輸是有序的,不會重復,也不存在記錄邊界,特別適合處理大量數據的情況。在網絡傳輸層,它可以根據需要把數據分散成合適大小的數據包,或者把數據包集中起來。

????????使用流套接字通信時,雙方得先建立一條通路。這么做一方面能確定雙方之間的傳輸路線,另一方面能確保雙方都處于活動狀態,隨時可以互相響應。不過,建立這樣一個通信信道可不容易,要耗費不少資源。另外,大多數面向連接的協議,為了保證數據發送準確無誤,往往得做一些額外的計算來驗證數據的正確性,這又進一步增加了開銷 。

(2)數據報套接字????????

????????數據報套接字能實現雙向的數據流動。但它有個問題,沒辦法確保數據在傳輸過程中是可靠的,也不能保證數據按順序到達,還可能出現重復數據。打個比方,一個進程通過數據報套接字接收信息時,可能會發現收到的信息跟發送時的順序不一樣,甚至還會收到重復的內容。

????????數據報套接字在工作時不需要建立連接,發送端發送信息后,它不管接收端是不是在監聽,也不關心接收端有沒有按時收到信息。正因為這樣,數據報的可靠性比較差。所以在使用數據報套接字編程時,程序員得自己想辦法去管理數據報的順序,還要確保數據的可靠性 。

1.5 字節序轉換

????????不同的計算機有時使用不同的字節順序存儲數據。任何從Winsock函數對IP地址和端口號的引用,以及傳送給Winsock函數的IP地址和端口都是按照網絡順序組織的。
- 將32位數從網絡字節轉換成主機字節(大端到小端):

u_long ntohl(u_long hostlong);

- 將16位數從網絡字節轉換成主機字節(大端到小端):

u_short ntohs(u_short short);

- 將32位數從主機字節轉換成網絡字節(小端到大端):

u_long htonl(u_long hostlong);

- 將16位數從主機字節轉換成網絡字節(小端到大端):

U_short htons(u short short);

1.6 綁定套接字

`bind()`函數將套接字綁定到一個已知的地址上。

int bind(SOCKET s,                 //套接字struct sockaddr FAR*name,//地址結構體變量(IP,端口,協議簇int namelen               //Sockaddr結構長度
);

示例:

sockaddr_in addr;
addr.sin_family = AF_INET; //地址家族
addr.sin_port = htons(1234); //端口號
addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.100"); //IP地址 本地:127.0.0.1
//3.綁定套接字
nErrCode = bind(sock,(sockaddr*)&addr, //套接字sizeof(sockaddr_in));//IP定址結構體大小

1.7 監聽

`listen()`函數將套接字設置為監聽模式。

1.8 連接

`accept`函數實現接收一個連接請求。

SOCKET accept(SOCKET s,                     //監聽套接字struct sockaddr FAR*addr,int FAR*addrlen
);

該函數返回請求連接的套接字句柄。
示例:

SOCKET ClientSocket = accept(sock,(sockaddr*)&addrClient, //返回請求連接主機的地址&nAddrSize);            //sockaddr_in的大小

示例2:域名解析

hostent *phostent;
in_addr in; //指向hostent結構的指針 //IPV4地址結構
if((phostent = gethostbyname("www.15pb.com"))==NULL){printf("gethostbyname()錯誤:%d",WSAGetLastError());
}
else{//拷貝4字節的IP地址到IPV4地址結構memcpy(&in,phostent->h_addr,4);printf("主機%s的IP地址是:",phostent->h_name);printf("%s",inet_ntoa(in));

1.9 接收數據

`Recv()`函數用于接收數據。

int recv(SOCKET s,  char *buf, //接收數據緩沖區int len,int flags
);

????????該函數返回接收到的數據實際長度,最后一個參數可以是`0`、`MSG_PEEK`和`MSG_OOB`。
- `0`表示無特殊行為。
- `MSG_PEEK`表示會使有用的數據被復制到接收緩沖區,但沒有從系統中將其刪除。
- `MSG_OOB`表示處理帶外數據。
示例:

char     buf[1024]={0};
nRecvSize = recv(sockClient, buf, 1024, 0);

1.10 發送數據

`send()`函數用于發送數據。

int send(SOCKET s,  char *buf, //發送數據緩沖區int  len,int flags
);

示例:

send(Sock, message, nSize, 0);

1.11 關閉套接字

????????`Closecocket()`函數關閉套接字,釋放所占資源。

closecocket(SOCKET s  //要關閉的套接字);

????????當調用該函數釋放套接字后,如果再使用該套接字執行函數調用,則會失敗并返回`WSAENOTSOCK`錯誤。

二、UDP連接流程

1. 包含必要的頭文件及庫:

#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")


2. 指定需要使用的Winsock規范的最高版本,并初始化Winsock,裝入Winsock.dll:

WSAStartup(MAKEWORD(2,2),&wsaDATA);

3. 創建套接字:

socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

4. 綁定IP和端口:

bind(sock,(sockaddr*)&addr,sizeof(sockaddr_in));

5. 接收數據:

recvfrom(sockClient,buf,1024,0,(sockaddr*)&fromAddr,&fromLen);

6. 發送數據:

sendto(Sock,message,nSize,0,(sockaddr*)&toAddr,toLen);

7. 在調用“closesocket”函數之后,但是在程序結束之前需要清理Winsock:

closesocket(sock);  //關閉套接字
WSACleanup();

2.1 接收數據

`recvfrom()`函數用于接收數據,并且返回發送數據主機的地址。

recvfrom(SOCKET s,     //用來接收數據的套接字char FAR*buf, //接收數據的緩沖區int  len,      //接收緩沖區的長度int flags,    //一般為0struct sockaddr*to, //接收的地址結構int FAR*fromlen     //sockaddr結構大小
);


注:函數的返回值,是接收到的大小。

2.2 發送數據

`sendto()`函數用于發送數據。

sendto(SOCKET s,const char FAR*buf,int  len,int  flags,const struct sockaddr*to,int tolen
);

- `s`:用來發送數據的套接字。
- `buf`:發送數據的緩沖區。
- `len`:要發送數據的長度。
- `flags`:一般為`0`。
- `to`:目標地址和端口。
- `tolen`:`sockaddr`結構大小。

如果該函數成功則返回發送數據的字節數。

需要注意以下兩點:
????????1. 在UDP編程里,從程序編寫的角度來看,很難明確區分出服務端和客戶端。簡單來說,誰提供服務,就把誰當作服務端。
????????2. 還有一點要留意,當剛創建好socket時,直接調用sendto函數是可行的,此時不需要手動綁定,系統會自動進行綁定操作。

三、阻塞與非阻塞模式

阻塞模式
????????在阻塞模式中,一旦執行操作函數,它就會一直處于等待狀態,不會馬上給出返回結果。執行這個函數的線程也會卡在這兒,只有當特定條件滿足了,函數才會返回。打個比方,就像快遞員通知你今天會有個快遞送達,卻沒告訴你具體時間。在阻塞模式下,你就只能一直在校門口干等著快遞,這一整天別的事都做不了。

非阻塞模式
????????要是處于非阻塞模式,操作函數執行后會立刻返回,執行這個函數的線程能接著往下運行。同樣拿快遞的例子來說,快遞員告知你今天有快遞,沒說具體時間,你不用一直守在校門口,而是每隔30分鐘去校門口瞅瞅快遞到了沒,要是沒到就回來繼續做自己原來的事。

????????為了避免線程長時間被阻塞,提升線程的使用效率,就出現了非阻塞模型。我們可以通過調用`ioctlsocket()`函數,來讓socket明確處于非阻塞模式 。

int ioctlsocket(_In_    SOCKET s,_In_    long cmd,_Inout_ u_long *argp
);

示例:

//2.設置套接字非阻塞模式
unsigned long ul = 1; //設置套接字選項
int nRet = ioctlsocket(sSocket,FIONBIO,&ul);

四、示例代碼

4.1 TCP協議代碼

服務端:

#include <winsock2.h> // Winsock庫頭文件
#include <ws2tcpip.h> // 提供IP地址轉換函數
#include <iostream>   // 標準輸入輸出流
#pragma comment(lib, "Ws2_32.lib") // 鏈接Winsock庫// 初始化Winsock庫
bool InitWinSock() {WSADATA wsaData; // 用于存儲Winsock庫的初始化信息// 調用WSAStartup初始化Winsock庫,版本2.2if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {std::cerr << "WSAStartup failed!" << std::endl;return false; // 初始化失敗返回false}return true; // 初始化成功返回true
}// 創建并綁定套接字
SOCKET CreateAndBindSocket(const char* ip, int port) {// 創建TCP套接字SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sock == INVALID_SOCKET) {std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl;return INVALID_SOCKET; // 創建套接字失敗返回INVALID_SOCKET}sockaddr_in addr{}; // 定義一個IPv4地址結構體addr.sin_family = AF_INET; // 地址族為IPv4addr.sin_port = htons(port); // 設置端口號,轉換為網絡字節序// 將IP地址從字符串轉換為二進制格式inet_pton(AF_INET, ip, &addr.sin_addr);// 綁定套接字到指定IP和端口if (bind(sock, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {std::cerr << "Bind failed: " << WSAGetLastError() << std::endl;closesocket(sock); // 綁定失敗關閉套接字return INVALID_SOCKET; // 返回INVALID_SOCKET}return sock; // 返回綁定成功的套接字
}// 主函數
int main() {// 初始化Winsock庫if (!InitWinSock()) return 1;const char* serverIp = "192.168.1.100"; // 服務器IP地址int serverPort = 1234; // 服務器端口號// 創建并綁定套接字SOCKET serverSocket = CreateAndBindSocket(serverIp, serverPort);if (serverSocket == INVALID_SOCKET) {WSACleanup(); // 釋放Winsock資源return 1;}// 開始監聽連接請求,SOMAXCONN為最大連接數if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {std::cerr << "Listen failed: " << WSAGetLastError() << std::endl;closesocket(serverSocket); // 監聽失敗關閉套接字WSACleanup(); // 釋放Winsock資源return 1;}std::cout << "Server listening on " << serverIp << ":" << serverPort << std::endl;// 主循環,處理客戶端連接while (true) {sockaddr_in clientAddr{}; // 存儲客戶端地址信息int clientAddrSize = sizeof(clientAddr);// 接受客戶端連接,返回客戶端套接字SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrSize);if (clientSocket == INVALID_SOCKET) {std::cerr << "Accept failed: " << WSAGetLastError() << std::endl;continue; // 接受失敗繼續循環}char clientIp[INET_ADDRSTRLEN]; // 存儲客戶端IP地址字符串// 將客戶端IP地址從二進制轉換為字符串格式inet_ntop(AF_INET, &clientAddr.sin_addr, clientIp, INET_ADDRSTRLEN);std::cout << "Client connected: " << clientIp << std::endl;char buffer[1024]; // 接收數據的緩沖區while (true) {// 接收客戶端發送的數據int bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);if (bytesReceived <= 0) {std::cerr << "Client disconnected or recv failed: " << WSAGetLastError() << std::endl;break; // 接收失敗或客戶端斷開連接,退出循環}buffer[bytesReceived] = '\0'; // 確保字符串以NULL結尾std::cout << "Received: " << buffer << std::endl; // 打印接收到的數據}closesocket(clientSocket); // 關閉客戶端套接字}closesocket(serverSocket); // 關閉服務器套接字WSACleanup(); // 釋放Winsock資源return 0;
}


客戶端:

#include <winsock2.h> // Winsock庫頭文件
#include <ws2tcpip.h> // 提供IP地址轉換函數
#include <iostream>   // 標準輸入輸出流
#pragma comment(lib, "Ws2_32.lib") // 鏈接Winsock庫// 初始化Winsock庫
bool InitWinSock() {WSADATA wsaData; // 用于存儲Winsock庫的初始化信息// 調用WSAStartup初始化Winsock庫,版本2.2if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {std::cerr << "WSAStartup failed!" << std::endl;return false; // 初始化失敗返回false}return true; // 初始化成功返回true
}// 主函數
int main() {// 初始化Winsock庫if (!InitWinSock()) return 1;const char* serverIp = "127.0.0.1"; // 服務器IP地址int serverPort = 1234; // 服務器端口號// 創建TCP套接字SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (clientSocket == INVALID_SOCKET) {std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl;WSACleanup(); // 釋放Winsock資源return 1;}sockaddr_in serverAddr{}; // 定義一個IPv4地址結構體serverAddr.sin_family = AF_INET; // 地址族為IPv4serverAddr.sin_port = htons(serverPort); // 設置端口號,轉換為網絡字節序// 將IP地址從字符串轉換為二進制格式inet_pton(AF_INET, serverIp, &serverAddr.sin_addr);// 連接到服務器if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {std::cerr << "Connection failed: " << WSAGetLastError() << std::endl;closesocket(clientSocket); // 連接失敗關閉套接字WSACleanup(); // 釋放Winsock資源return 1;}std::cout << "Connected to server at " << serverIp << ":" << serverPort << std::endl;char message[1024]; // 存儲發送的消息std::cout << "Enter message: ";std::cin.getline(message, sizeof(message)); // 從標準輸入讀取消息// 發送消息到服務器if (send(clientSocket, message, static_cast<int>(strlen(message)), 0) == SOCKET_ERROR) {std::cerr << "Send failed: " << WSAGetLastError() << std::endl;}closesocket(clientSocket); // 關閉客戶端套接字WSACleanup(); // 釋放Winsock資源return 0;
}


4.2 UDP協議代碼

#include <winsock2.h> // Winsock庫頭文件
#include <ws2tcpip.h> // 提供IP地址轉換函數
#include <iostream>   // 標準輸入輸出流
#pragma comment(lib, "Ws2_32.lib") // 鏈接Winsock庫// 函數名稱:InitWinSock
// 功能:初始化Winsock庫
// 返回值:成功返回TRUE,失敗返回FALSE
BOOL InitWinSock() {WSADATA wsaData; // 用于存儲Winsock庫的初始化信息// 調用WSAStartup初始化Winsock庫,版本2.2int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);if (nResult != 0) { // 初始化失敗std::cerr << "WSAStartup failed with error: " << nResult << std::endl;return FALSE;}return TRUE; // 初始化成功
}// 函數名稱:InitServer
// 功能:初始化UDP服務器
// 返回值:成功返回true,失敗返回false
bool InitServer() {// 1. 初始化Winsock庫if (!InitWinSock()) {return false;}// 2. 創建UDP套接字SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (serverSocket == INVALID_SOCKET) {std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl;WSACleanup(); // 釋放Winsock資源return false;}// 3. 定義服務器地址sockaddr_in serverAddr{};serverAddr.sin_family = AF_INET; // 地址族為IPv4serverAddr.sin_port = htons(1234); // 設置端口號,轉換為網絡字節序// 將IP地址從字符串轉換為二進制格式inet_pton(AF_INET, "192.168.199.207", &serverAddr.sin_addr);// 4. 綁定套接字到指定IP和端口if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {std::cerr << "Bind failed: " << WSAGetLastError() << std::endl;closesocket(serverSocket); // 綁定失敗關閉套接字WSACleanup(); // 釋放Winsock資源return false;}std::cout << "Server started and listening on 192.168.199.207:1234" << std::endl;// 5. 主循環,接收客戶端數據while (true) {char buffer[1024] = {0}; // 接收數據的緩沖區sockaddr_in clientAddr{}; // 存儲客戶端地址信息int clientAddrSize = sizeof(clientAddr);// 接收客戶端發送的數據int bytesReceived = recvfrom(serverSocket, buffer, sizeof(buffer), 0,(sockaddr*)&clientAddr, &clientAddrSize);if (bytesReceived == SOCKET_ERROR) {std::cerr << "recvfrom failed: " << WSAGetLastError() << std::endl;break; // 接收失敗退出循環}// 打印接收到的數據buffer[bytesReceived] = '\0'; // 確保字符串以NULL結尾char clientIp[INET_ADDRSTRLEN];inet_ntop(AF_INET, &clientAddr.sin_addr, clientIp, INET_ADDRSTRLEN); // 將客戶端IP轉換為字符串std::cout << "Received from " << clientIp << ": " << buffer << std::endl;}// 6. 關閉套接字并釋放資源closesocket(serverSocket);WSACleanup();return true;
}// 主函數
int main() {if (!InitServer()) {std::cerr << "Server initialization failed!" << std::endl;return 1;}return 0;
}

??????? 這篇網絡編程基礎的內容全面介紹了套接字相關知識,涵蓋了Berkeley Socket和WinSocket的起源與發展,詳細闡述了TCP和UDP的連接流程,包括各個步驟中涉及的函數使用方法、參數含義,還介紹了阻塞與非阻塞模式的概念及設置方式,并通過完整的TCP和UDP協議示例代碼,讓讀者能夠更直觀地理解和實踐網絡編程中的關鍵操作。

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/898848.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/898848.shtml
英文地址,請注明出處:http://en.pswp.cn/news/898848.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

QT 圖表(拆線圖,欄狀圖,餅狀圖 ,動態圖表)

效果 折線圖 // 創建折線數據系列// 創建折線系列QLineSeries *series new QLineSeries;// series->append(0, 6);// series->append(2, 4);// series->append(3, 8);// 創建圖表并添加系列QChart *chart new QChart;chart->addSeries(series);chart->setTit…

vector和list的區別是什么

vector 和 list 都是C 標準模板庫&#xff08;STL&#xff09;中的容器&#xff0c;它們的區別如下&#xff1a; 內存結構 - vector &#xff1a;是連續的內存空間&#xff0c;就像數組一樣&#xff0c;元素在內存中依次排列。 - list &#xff1a;是由節點組成的雙向鏈表…

【AI】【AIGC】降低AIGC檢測率:技術、挑戰與應對策略

引言 隨著生成式人工智能&#xff08;AIGC&#xff09;技術的迅速發展&#xff0c;越來越多的內容開始由人工智能生成。AIGC技術的應用非常廣泛&#xff0c;包括文本生成、圖像生成、音頻生成等。然而&#xff0c;隨著這些技術的普及&#xff0c;如何有效識別并檢測AIGC生成的…

vue3 ts 請求封裝后端接口

一 首頁-廣告區域-小程序 首頁-廣告區域-小程序 GET/home/banner1.1 請求封裝 首頁-廣告區域 home.ts export const getHomeBannerApi (distributionSite 1) > {return http<BannerItem[]>({method: GET,url: /home/banner,data: {distributionSite,},}) }函數定…

響應式CMS架構優化SEO與用戶體驗

內容概要 在數字化內容生態中&#xff0c;響應式CMS架構已成為平衡搜索引擎可見性與終端用戶體驗的核心載體。該系統通過多終端適配技術&#xff0c;確保PC、移動端及平板等設備的內容渲染一致性&#xff0c;直接降低頁面跳出率并延長用戶停留時長。與此同時&#xff0c;智能S…

算法基礎篇(1)(藍橋杯常考點)

算法基礎篇 前言 算法內容還有搜索&#xff0c;數據結構&#xff08;進階&#xff09;&#xff0c;動態規劃和圖論 數學那個的話大家也知道比較難&#xff0c;放在最后講 這期包含的內容可以看目錄 模擬那個算法的話就是題說什么寫什么&#xff0c;就不再分入目錄中了 注意事…

MyBatis一級緩存和二級緩存

介紹 在開發基于 MyBatis 的應用時&#xff0c;緩存是提升性能的關鍵因素之一。MyBatis 提供了一級緩存和二級緩存&#xff0c;合理使用它們可以顯著減少數據庫的訪問次數&#xff0c;提高系統的響應速度和吞吐量。本文將深入探討 MyBatis 一級緩存和二級緩存的工作原理、使用…

C++核心語法快速整理

前言 歡迎來到我的博客 個人主頁:北嶺敲鍵盤的荒漠貓-CSDN博客 本文主要為學過多門語言玩家快速入門C 沒有基礎的就放棄吧。 全部都是精華&#xff0c;看完能直接上手改別人的項目。 輸出內容 std::代表了這里的cout使用的標準庫&#xff0c;避免不同庫中的相同命名導致混亂 …

如何讓自動駕駛汽車“看清”世界?坐標映射與數據融合概述

在自動駕駛領域,多傳感器融合技術是實現車輛環境感知和決策控制的關鍵。其中,坐標系映射和對應是多傳感器融合的重要環節,它涉及到不同傳感器數據在統一坐標系下的轉換和匹配,以實現對車輛周圍環境的準確感知。本文將介紹多傳感器融合中坐標系映射和對應的數學基礎和實際應…

Unity Shader 的編程流程和結構

Unity Shader 的編程流程和結構 Unity Shader 的編程主要由以下三個核心部分組成&#xff1a;Properties&#xff08;屬性&#xff09;、SubShader&#xff08;子著色器&#xff09; 和 Fallback&#xff08;回退&#xff09;。下面是它們的具體作用和結構&#xff1a; 1. Pr…

第十四天- 排序

一、排序的基本概念 排序是計算機科學中一項重要的操作&#xff0c;它將一組數據元素按照特定的順序&#xff08;如升序或降序&#xff09;重新排列。排序算法的性能通常通過時間復雜度和空間復雜度來衡量。在 Python 中&#xff0c;有內置的排序函數&#xff0c;同時也可以手…

移除idea External Liraries 中maven依賴包

問題背景 擴展包里面不停的出現已經在POM文件注釋的包&#xff0c;其實是沒有查詢到根源位置。 在IDEA插件中搜索Maven Helper 點擊pom.xml文件 會出現擴展插件 定位之后在pom中添加exclusions&#xff0c;如下代碼 <dependency><groupId>com.disney.eva.framewo…

AI革命!藍耘攜手海螺AI視頻,打造智能化視頻新紀元

AI革命&#xff01;藍耘攜手海螺AI視頻&#xff0c;打造智能化視頻新紀元 前言 在這個信息爆炸的時代&#xff0c;視頻已經成為我們獲取信息、學習新知識的重要方式。而隨著人工智能&#xff08;AI&#xff09;技術的快速發展&#xff0c;AI與視頻內容的結合為我們帶來了全新的…

dify1.1.1安裝

1、 按照GitHub上操作 下載源碼&#xff0c;沒有安裝git的&#xff0c;可以下載成zip包&#xff0c; unzip 解壓 git clone https://github.com/langgenius/dify.git cd dify cd docker cp .env.example .env2、啟動前 &#xff0c;先改下 docker-compose.yaml&#xff0c;…

ElasticSearch 可觀測性最佳實踐

ElasticSearch 概述 ElasticSearch 是一個開源的高擴展的分布式全文檢索引擎&#xff0c;它可以近乎實時的存儲、檢索數據&#xff1b;本身擴展性很好&#xff0c;可以擴展到上百臺服務器&#xff0c;處理 PB 級別&#xff08;大數據時代&#xff09;的數據。ES 也使用 Java 開…

Excel處理控件Spire.XLS系列教程:C# 在 Excel 中添加或刪除單元格邊框

單元格邊框是指在單元格或單元格區域周圍添加的線條。它們可用于不同的目的&#xff0c;如分隔工作表中的部分、吸引讀者注意重要的單元格或使工作表看起來更美觀。本文將介紹如何使用 Spire.XLS for .NET 在 C# 中添加或刪除 Excel 單元格邊框。 安裝 Spire.XLS for .NET E-…

前端Wind CSS面試題及參考答案

目錄 標準盒模型與 IE 盒模型的區別是什么?如何通過 box-sizing 屬性切換這兩種盒模型? 如何計算一個元素在標準盒模型下的總寬度(包含 margin、padding、border)? 父元素高度塌陷的原因是什么?請列舉至少 3 種清除浮動的方法。 方法一:使用 clear 屬性 方法二:使用…

基于 ECharts 實現動態圖表渲染支持10萬+數據點實時更新方案

引言 實現支持10萬數據點實時更新的動態圖表渲染確實具有挑戰性&#xff0c;尤其是在性能和用戶體驗方面。以下是一些關鍵點和應用場景&#xff1a; 關鍵挑戰 性能優化&#xff1a; 渲染性能&#xff1a;大量數據點會導致瀏覽器渲染壓力大&#xff0c;可能引發卡頓。數據處理…

裝飾器模式 (Decorator Pattern)

裝飾器模式 (Decorator Pattern) 是一種結構型設計模式,它動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾器模式相比生成子類更為靈活。 一、基礎 1 意圖 動態地給一個對象添加一些額外的職責。 就增加功能來說,裝飾器模式相比生成子類更為靈活。 2 適用場景 當…

【Java】TCP網絡編程:從可靠傳輸到Socket實戰

活動發起人小虛竹 想對你說&#xff1a; 這是一個以寫作博客為目的的創作活動&#xff0c;旨在鼓勵大學生博主們挖掘自己的創作潛能&#xff0c;展現自己的寫作才華。如果你是一位熱愛寫作的、想要展現自己創作才華的小伙伴&#xff0c;那么&#xff0c;快來參加吧&#xff01…