【第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协议示例代码,让读者能够更直观地理解和实践网络编程中的关键操作。
相关文章:
【第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连接流程…...
《深度剖析:BERT与GPT——自然语言处理架构的璀璨双星》
在自然语言处理(NLP)的广袤星空中,BERT(Bidirectional Encoder Representations from Transformers)与GPT(Generative Pretrained Transformer)系列模型宛如两颗最为耀眼的星辰,引领…...
景联文科技:以高质量数据标注推动人工智能领域创新与发展
在当今这个由数据驱动的时代,高质量的数据标注对于推动机器学习、自然语言处理(NLP)、计算机视觉等领域的发展具有不可替代的重要性。数据标注过程涉及对原始数据进行加工,通过标注特定对象的特征来生成能够被机器学习模型识别和使…...
LeetCode 30 —— 30.串联所有单词的子串
题目: 给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。 注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。 示例 1ÿ…...
【redis】主从复制:单点问题、配置详解、特点详解
文章目录 单点问题什么是主从复制主从模式能解决的问题并发量有限可用性问题 配置建立复制通过配置文件来指定端口配置主从查看集群结构 断开复制 特点安全性只读传输延迟 单点问题 分布式系统中,涉及到一个非常关键的问题:单点问题 某个服务器程序&…...
VSCode创建VUE项目(四)增加用户Session管理
将用户信息存储或者更新到Session sessionStorage.setItem("userID",loginform.value.username); sessionStorage.setItem(loginTime, Date.now()); 获取Session信息 const storedUserInfo sessionStorage.getItem(userID); const loginTime sessionStorage.get…...
Spring Boot(十六):拦截器Interceptor
拦截器的简介 拦截器(Interceptor)是Spring框架中的概念,它同样适用于Spring Boot,因为Spring Boot是基于Spring框架的。拦截器是一种AOP(面向切面编程)的轻量级实现方式,它允许我…...
考研复习之队列
循环队列 队列为满的条件 队列为满的条件需要特殊处理,因为当队列满时,队尾指针的下一个位置应该是队头指针。但是,我们不能直接比较 rear 1 和 front 是否相等,因为 rear 1 可能会超出数组索引的范围。因此,我们需…...
sql-labs
p1 sql注入的目的是为了破坏sql语句结构,有三种参数类型,字符型(就是一个字符1或者a之类的),字符串(“hellow之类的”)型,数值型,前两个有闭合,注释符号有# …...
Java 集合框架:从数据结构到性能优化,全面解析集合类
Java 集合框架(Java Collections Framework,JCF)是 Java 语言中用于存储、操作和管理数据的标准库。它提供了一组通用的接口、类和方法,使开发者能够高效地操作不同类型的数据集合。 本文将结合 Java 集合框架类图,介…...
vulkanscenegraph显示倾斜模型(5.4)-相机操纵器
前言 在VSG(Vulkan Scene Graph)中,系统支持用户通过鼠标或触摸输入与三维场景进行交互,从而动态控制相机的位置和姿态,实现与三维场景的交互。VSG提供了多种相机操纵器,其中Trackball是一种常见的相机操作…...
两个还算好用的ppt转word和PDF转word的python脚本
PPT转word: import re from pptx import Presentation from docx import Document from docx.shared import Inches from io import BytesIO from PIL import Imagedef clean_text(text):# 使用正则表达式删除控制字符和NULL字节return re.sub(r[\x00-\x1F\x7F], ,…...
用PostgreSQL玩转俄罗斯方块:当SQL成为游戏引擎
当DBA开始摸鱼2025年某深夜,一位不愿透露姓名的DBA为了在监控大屏上隐藏游戏行为,竟用SQL实现了俄罗斯方块!从此,SELECT成了方向键,UPDATE成了旋转指令,DELETE成了消除大招。本文将揭秘这个疯狂项目的技术内…...
基于WebAssembly的浏览器密码套件
目录 一、前言二、WebAssembly与浏览器密码套件2.1 WebAssembly技术概述2.2 浏览器密码套件的需求三、系统设计思路与架构3.1 核心模块3.2 系统整体架构图四、核心数学公式与算法证明4.1 AES-GCM加解密公式4.2 SHA-256哈希函数五、异步任务调度与GPU加速设计5.1 异步任务调度5.…...
手撕算法之`vector` 扩容、`string` 分割、链表翻转
手写常见操作:vector 扩容、string 分割、链表翻转 (一)vector扩容 在 C++ 中,vector 的扩容机制是动态数组实现的核心特性,直接关系到性能和内存使用效率。以下是深入剖析: 1. 扩容触发条件 vector<int> v; v.push_back(1); // 当 size() == capacity() 时触发…...
tauri2程序单例模式实现,二次点击桌面图标显示之前最小化的程序并聚焦
官方有这个单例的插件可以直接使用:单例 | Tauri,使用单实例插件确保 Tauri 应用程序在同一时间只运行单个实例。插件已经安装并初始化,应该可以立即正常运行。尽管如此,我们也可以使用 init() 方法来增强它的功能。插件的 init()…...
【AI学习笔记】Coze平台实现将Excel文档批量导入数据库全过程
背景前摇&原视频教程: 最近看到很多同学都在用Coze平台操作数据,我也想了解一下工作流的搭建和数据处理过程,但是一下子又看不懂太复杂的逻辑,于是上B站搜索相关的基础教程。 Coze官方教程: 之前有看过Coze平台…...
c++之迭代器
一.迭代器的基本概念 1.什么是迭代器 迭代器是一种对象,它提供了一种访问容器中各个元素的方法,同时隐藏了容器内部的实现细节。简单来说,迭代器就像是一个指针,它可以指向容器中的某个元素,并且能够通过一些操作&am…...
Elasticsearch 索引
一、简介 在 Elasticsearch 中,索引(Index)是存储相关文档的地方,类似于关系数据库中的数据库。索引是 Elasticsearch 中最重要的概念之一,用于组织和存储数据。 二、索引的基本概念 索引(Index…...
Java EE(16)——网络原理——TCP协议解析二
4.滑动窗口(效率机制) 上篇博客讲到的确认应答/超时重传/连接管理都是安全机制,但也会降低传输效率。滑动窗口就是在保证可靠传输的基础上,尽可能地提高传输效率。 根据确认应答机制,客户端每发送一个请求都需要收到服务器的确认应答报文后才…...
解决address already in use报错:如何查看占用某个端口的程序并杀死
文章目录 问题背景解决策略概述端口占用诊断步骤 1:确认占用端口的进程步骤 2:确认进程的详细信息 解决端口占用问题方案 1:安全终止进程方案 2:修改应用配置 最佳实践与预防措施端口使用规范开发环境配置 进阶技巧批量处理端口占…...
linux 设置tomcat开机自启动
tomcat自启动配置 1.添加tomcat.service文件 vim /etc/systemd/system/tomcat.service 2.编辑文件内容,路径修改为自己的 [Unit] DescriptionTomcat8 Aftersyslog.target network.target remote-fs.target nss-lookup.target[Service] Typeoneshot ExecStart/us…...
如何配置本地git
配置本地 Git 主要包含设置用户信息、配置 SSH 密钥、设置 Git 仓库等步骤,以下是详细的配置过程: 1. 安装 Git 在开始配置之前,你需要先安装 Git。不同操作系统的安装方式有所不同: Windows:访问 Git 官方下载页面&a…...
VSCode 生成HTML 基本骨架
在VSCode 新建html文件中敲一个英文感叹号 ! <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…...
蓝桥杯备考:DFS暴搜之健康的荷斯坦奶牛
这道题数据量很小很小,我们可以用dfs暴搜来搜索 这是我们的决策树 #include <iostream> using namespace std; int n, m; const int N 45; int rq[N]; int g[N][N]; int cnt; int path; int ret 45; int st; bool check() {for (int i 1; i < n; i){in…...
android adjust 卸载与重装监测
想要洞察应用内用户的留存率,可以通过Adjust 的卸载与重装进行监测 名词解释: 卸载:集成完成后,卸载应用,安装状态为:卸载 重装:如果应用已经卸载,但一段时间后又进行安装,则会被视为重装。 📢📢📢:adjust 文件中说到24 小时后,可以再 adjust 控制台看安装…...
WPF Reactive 数据绑定
文章目录 Combox 绑定List-通过枚举绑定方法一:方法二:Button 绑定TextBlock绑定NumericUpDown绑定Expander绑定checkbox绑定NumericUpDownCombox 绑定List-通过枚举绑定 方法一: ViewControl using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; usin…...
2.创建Collection、添加索引、加载内存、预览和搜索数据
milvus官方文档 milvus2.3.1的官方文档地址: https://milvus.io/docs/v2.3.x 使用attu创建collection collection必须要有一个主键字段、向量字段 确保字段类型与索引类型兼容 字符串类型(VARCHAR)通常需要使用 Trie 索引,而不是 AutoInd…...
yaffs
YAFFS(Yet Another Flash File System)是专为NAND闪存设计的日志结构文件系统,其核心原理围绕NAND闪存的特性优化数据管理。以下是其关键原理的详细说明: 1. NAND闪存适配 写入限制:NAND闪存需按页写入(通…...
CMake-环境变量介绍
文章目录 作用域获取环境变量初始化查看特殊的环境变量 环境变量类似普通变量,但也有些不同,如下: 作用域 在一个CMake进程中环境变量具有全局作用域 获取环境变量 使用ENV操作符获取环境变量,例如$ENV{<name>}ÿ…...
wordpress表单插件CF7调用方式
Contact Form 7(CF7)是WordPress中非常流行的表单插件,以下是其常见的调用方式: 通过短代码调用 在页面或文章编辑器中添加:完成表单设置后,复制表单对应的短代码,然后在需要显示表单的页面或文章的编辑器中直接粘贴…...
小程序开发中的用户反馈收集与分析
我们在开发小程序的过程中根据开发过程中的代码及业务场景,以下是针对需求管理系统的用户反馈收集与分析方案设计: 需求管理系统用户反馈收集与分析方案 一、反馈数据模型设计 // 新增Feedback模型(app/admin/model/Feedback.php) namespace app\admin\model; use think\…...
【HarmonyOS Next】鸿蒙中App、HAP、HAR、HSP概念详解
【HarmonyOS Next】鸿蒙中App、HAP、HAR、HSP概念详解 (图1-1) 一、鸿蒙中App、HAP、HAR、HSP是什么? (1)App Pack(Application Package) 是应用发布的形态,上架应用市场是以App Pa…...
Linux:一些命令记录
netstat -antp|grep -i 27017 | awk {print $5}| cut -d: -f1 | sort | uniq -c | sort -n 查看磁盘大小 du -sh /usr/local/* 查看剩余内存: free -m linux下获取占用CPU资源最多的10个进程,可以使用如下命令组合: ps aux|head -1;ps aux|gr…...
Microsoft Edge浏览器的取证分析(基于Chromium)
概述 早在2019年,微软就用Chromium替换了EdgeHTML浏览器引擎,这是微软支持谷歌Chrome浏览器的一个开源项目。通过切换到Chromium,Edge与Chrome浏览器共享一个共同的架构,这意味着用于Chrome浏览器调查的取证技术也适用于Edge。 …...
Java面试黄金宝典6
1. 什么是 CAS 原理: CAS (Compare-And-Swap)是一种硬件级别的原子操作指令,在 Java 并发编程中常被用于实现无锁算法。其核心逻辑是:在进行数据更新时,会先将内存位置 V 的值与预期原值 A 进行比较&#x…...
【计算机网络】网络编程
文章目录 1. 客户端/服务器2. TCP/UDP协议3. 网络编程套接字-socket3.1 API的使用3.1 DatagramScoket类3.1 DatagramScoket类 4. 通过UDP实现回显服务器程序4.1 服务器代码4.2 客户端代码4.3 代码执行过程4.4 通过UDP实现翻译客户端 5. 通过TCP实现回显服务器5.1 服务器代码5.2…...
算法刷题整理合集(七)·【算法赛】
本篇博客旨在记录自已的算法刷题练习成长,里面注有详细的代码注释以及和个人的思路想法,希望可以给同道之人些许帮助。本人也是算法小白,水平有限,如果文章中有什么错误或遗漏之处,望各位可以在评论区指正出来…...
【开源宝藏】30天学会CSS - DAY6 第六课 流光文字动画
第 0 步:项目结构 lighting-text/├─ index.html└─ style.cssindex.html:包含列表 <ul>,其中每个 <li> 放一个字母或符号。style.css:设置背景、文字样式,以及关键帧动画(lighting…...
C#与西门子PLC的六大通信库
C#与西门子PLC的六大通信库: 一、S7.NET S7.NET是一款开源的S7协议通信库,支持西门子S7通信。 二、Sharp7 Sharp7与S7.NET一样,是一款.NET版本的S7通信库。 三、Snap7 Snap7是一个开源的C通信库,支持西门子S7通信。 四、Prodave P…...
VScode
由于centos停止了维护 ,后面使用ubuntu 在Ubuntu中用vscode 充当记事本的作用 替代了centos中vim的作用 后面使用vscode编辑 vscode中继续使用makefile , xshell中的cgdb进行debug (半图形写 ,半命令行debug&&运行) 官网下载地址:https://code.visuals…...
Linux文件系统与磁盘管理
文件系统和磁盘管理是Linux系统管理的核心组成部分,直接影响系统的性能、数据安全性和存储效率。本文将从Linux文件系统的基本概念出发,深入探讨其架构、类型、管理工具以及实际操作技巧,帮助读者全面理解并掌握这一关键领域。 第一章&#x…...
【云馨AI-大模型】大模型的开发和应用中,Python、PyTorch和vLLM关系概括
说明 1. Python 定位:基础编程语言。作用:Python 是大模型生态系统的核心语言,几乎所有深度学习框架(如 PyTorch、TensorFlow)和工具链(如 vLLM)都通过 Python 接口提供服务。特点:…...
AWS SAP学习笔记-概念
1、什么是ETL应用程序,举个例子说明? ETL(Extract, Transform, Load)应用程序是一种用于数据处理和迁移的工具或程序,它主要负责从多个数据源提取数据,对数据进行转换和清洗,然后将处理后的数据…...
kotlin知识体系(三) : Android Kotlin 中的函数式编程实践指南
前言 Kotlin以函数式编程革新了Android开发,通过高阶函数、扩展函数等特性,帮助开发者构建高可维护性代码。接下来我们来看一下Kotlin 中的函数式编程的各个特性。 1. 高阶函数与 Lambda 表达式:函数作为参数或返回值 在 Kotlin 中&#x…...
SpringBoot3+Vue3实战(Vue3快速开发登录注册页面并对接后端接口、表单项自定义校验规则、Hutool工具类)(4)
目录 一、SpringBoot3Vue3实现基本增删改查。前后端通信交互、配置后端跨域请求。数据批量删除。(博客链接) 二、SpringBoot3Vue3快速开发登录、注册页面并实现对接。 (1)操作数据表employee(员工信息表)。 <1>修改employee表的字段组成。 <2&g…...
OpenCV图像拼接项目指南
引言 图像拼接是计算机视觉领域中的一个重要应用,它可以将多张有重叠区域的图像拼接成一张全景图。这项技术广泛应用于虚拟现实、医学影像、卫星图像处理等领域。OpenCV作为一个强大的开源计算机视觉库,提供了丰富的工具和函数来实现图像拼接。本文将详…...
机器学习--DBSCAN聚类算法详解
目录 引言 1. 什么是DBSCAN聚类? 2. DBSCAN聚类算法的原理 3. DBSCAN算法的核心概念 3.1 邻域(Neighborhood) 3.2 核心点(Core Point) 3.3 直接密度可达(Directly Density-Reachable) 3…...
使用 Docker 构建 LangChain 开发环镜及 ChatOllama 示例
文章目录 Github官网简介Dockerfilerequirements.txt构建 LangChain 镜像ChatOllama 示例Ollama 示例模拟 tools Github https://github.com/langchain-ai/langchain 官网 https://python.langchain.com/docs/introduction/ 简介 LangChain 是一个用于构建 LLM 驱动的应用…...
持续集成与持续交付:这里有从开发到部署的全流程优化
阅读原文 在上一篇中,我们深入探讨了安全测试的核心内容,强调了它在发现安全漏洞和提升系统安全性中的重要作用。接下来,我们将聚焦于持续集成(CI)与持续交付(CD),这是现代软件开发…...