【Windows】 C++实现 Socket 通讯

【Windows】 C++实现 Socket 通讯

一:头文件与套接字实例

 (1)Windows 系统下所需头文件 : #include<WinSock2.h>
 (2)我们使用 SOCKET 来作为套接字的实例:通过查看源码得知其是一个无符号 64 位的整形,如下:

typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
typedef UINT_PTR        SOCKET;

二:常用方法

 (1)WSAStartup
  WSAStartup 函数通过进程启动对 Winsock DLL 的使用,即打开网络库/启动网络库,启动了这个库,这个库里的函数/功能才能使用。

int WSAAPI WSAStartup(WORD      wVersionRequested,LPWSADATA lpWSAData
);

  参数:
   WORD wVersionRequested:

typedef unsigned short WORD;//无符号短整型;

    表示调用者可以使用的 Windows 套接字规范的最高版本。 高位字节指定次要版本号; 低位字节指定主要版本号。
     可以使用 MAKEWORD 函数来创建该参数:
      MAKEWORD(lowbyte, highbyte):低位字节和高位字节参数指定主要和次要版本号
   LPWSADATA lpWSAData:
    表示指向 WSADATA 数据结构的指针,该数据结构将接收Windows套接字实现的详细信息。定义时需定义一个 WSADATA 结构体对象,并将其指针传递给该参数
  –返回值:
   成功返回零,否则为错误
  【注意
   ①WSAStartup 函数必须是应用程序或 DLL 调用的第一个 Windows 套接字函数。 它允许应用程序或 DLL 指定所需的 Windows 套接字版本,并检索特定 Windows 套接字实现的详细信息。 应用程序或 DLL 只能在成功调用 WSAStartup 后才能使用更多 Windows 套接字函数。
   ②当应用程序或 DLL 调用 WSAStartup 函数时,Winsock DLL 检查在 wVersionRequested 参数中传递的应用程序请求的 Windows 套接字规范的版本。 如果应用程序请求的版本等于或高于 Winsock DLL 支持的最低版本,则调用成功,并且 Winsock DLL 将在 lpWSAData 参数指向的 WSADATA 结构中返回详细信息。 WSADATA 结构的 wHighVersion 成员指示 Winsock DLL 支持的 Windows 套接字规范的最高版本。 WSADATA 结构的 wVersion 成员指示 Winsock DLL 希望调用方使用的 Windows 套接字规范的版本。
   ③如果调用方无法接受 WSADATA 结构的 wVersion 成员,则应用程序或 DLL 应调用 WSACleanup 来释放 Winsock DLL 资源,并且无法初始化 Winsock 应用程序。 为了支持此应用程序或 DLL,需要搜索要安装在平台上的 Winsock DLL 的更新版本。
    当前是 2024/9/25日,Windows 套接字规范的当前版本为版本 2.2。 当前的 Winsock DLL 【Ws2_32.dll】支持请求以下任一版本的 Windows 套接字规范的应用程序:1.0;1.1;2.0;2.1;2.2,具体对应的版本可以去查询。
   ④这里需要注意的是最开始必须要引入到 Ws2_32.dll 库,所以需要调用 #pragma comment(lib,“ws2_32.lib”) 方式来调用到 Ws2_32.dll 库,在 WSAStartup 初始化的时候分配对应资源,然后在结束的时候使用 WSAClearup 来释放掉;

#pragma comment(lib, "XXX.lib")//是visual studio中使用的,#pragma comment ( lib,"wpcap.lib" )//是导入1个库文件,以使程序可以调用相应的动态链接库。

  示例代码:

#pragma comment(lib, "ws2_32.lib")//添加动态连接库的调用
//--------------------------------
WORD wVersionRequested = MAKEWORD(2, 2);//指定支持的 windows套接字规范的版本
WSADATA wsadata;
int init_res = WSAStartup(wVersionRequested, &wsadata);
if (init_res != 0)
{std::cout << "WSA Start Failed" << std::endl;return -1;
}
else {std::cout << "WSA Start success" << std::endl;
}

 (2)WSACleanup:
  WSACleanup 函数终止使用 Winsock 2 DLL (Ws2_32.dll) ,即释放 WSAStartup 初始化时调用 Ws2_32.dll 库所分配的资源,一般在程序结束时使用。
  示例代码:

WSACleanup();//释放动态链接库 ws2_32.dll 初始化时多分配的空间

 (3)socket:
  socket 函数创建绑定到特定传输服务提供程序的套接字

SOCKET WSAAPI socket([in] int af,[in] int type,[in] int protocol
);

  参数:
   af:  指定地址族:

 AF_UNSPEC		0		地址系列未指定AF_INET			2		IPv4AF_INET6		23		IPv6...

   type: 指定Socket类型:

 SOCK_STREAM     1		一种套接字类型,它通过 OOB 数据传输机制提供排序的、可靠的双向、基于连接的字节流。 此套接字类型对 Internet 地址系列 (AF_INET 或AF_INET6) 使用传输控制协议 (TCP) 。      SOCK_DGRAM      2       支持数据报的套接字类型,这些数据报是固定 (通常较小) 最大长度的无连接、不可靠的缓冲区。 此套接字类型对 Internet 地址系列 (AF_INET 或AF_INET6) 使用用户数据报协议 (UDP) 。        SOCK_RAW        3       一种套接字类型,它提供允许应用程序操作下一层协议标头的原始套接字。 若要操作 IPv4 标头,必须在套接字上设置 IP_HDRINCL 套接字选项。 若要操作 IPv6 标头,必须在套接字上设置 IPV6_HDRINCL 套接字选项。     SOCK_RDM        4       提供可靠消息数据报的套接字类型。 此类型的一个示例是 Windows 中的实用常规多播 (PGM) 多播协议实现,通常称为 可靠多播编程。       SOCK_SEQPACKET  5       提供基于数据报的伪流数据包的套接字类型。     

    注意:在 Windows 套接字 1.1 中,只有 SOCK_DGRAM 和 SOCK_STREAM 这两种套接字类型,而在 套接字 2.0,才添加了其他套接字类型。
   protocol: 是与特定的地址家族相关的协议,如果指定为0,那么系统就会根据地址格式和套接类别,自动选择一个合适的协议

 IPPROTO_ICMP	    1		Internet 控制消息协议 (ICMP) IPPROTO_IGMP	    2		Internet 组管理协议 (IGMP) BTHPROTO_RFCOMM	3		蓝牙射频通信 (蓝牙 RFCOMM) 协议。 当 af 参数AF_BTH且类型参数SOCK_STREAM时,这是一个可能的值。IPPROTO_TCP		6		传输控制协议 (TCP) 。 当 af 参数AF_INET或AF_INET6且类型参数 SOCK_STREAM时,这是一个可能的值。IPPROTO_UDP		17		用户数据报协议 (UDP) 。 当 af 参数AF_INET或AF_INET6且类型参数SOCK_DGRAM时,这是一个可能的值。IPPROTO_ICMPV6	    58		Internet 控制消息协议版本 6 (ICMPv6)IPPROTO_RM		    113		可靠多播的 PGM 协议

  返回值:
   如果等于 INVALID_SOCKET,则表示创建不成功
  示例代码:

SOCKET SocketServer;//声明一个 Socket 套接字对象
SocketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (SocketServer != INVALID_SOCKET)//判断是否创建成功
{
}

 (3)bind:
  bind 函数将本地地址与套接字相关联。

int WSAAPI bind([in] SOCKET         s,[in] const sockaddr *name,[in] int            namelen
);

  参数:
   s:   标识未绑定套接字的描述符,定义的套接字。
   name: 指向要绑定给 s 的协议地址,即 IP 端口号等,创建一个 sockaddr_in 结构体对象,在这里指定地址族、IP 还有端口号等,然后强转成 const sockaddr * 赋值给该参数,其定义如下:

struct sockaddr_in addr;
addr.sin_family = AF_INET;//一般与使用 socket() 函数创建 socket 对象时使用的地址族一样,如下:
addr.sin_port = htons(13111);//设置端口号
inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));

     其中涉及函数解析:

u_short WSAAPI htons([in] u_short hostshort
);
 htons 函数将主机u_short转换为 TCP/IP 网络字节顺序 (大端) 。参数:[in] hostshort	主机字节顺序为 16 位数字。htons 函数以 TCP/IP 网络字节顺序返回值。作用:函数采用主机字节顺序为 16 位数字,并返回 TCP/IP 网络 (AF_INET或AF_INET6地址系列) 中使用的 16 位数字。htons 函数可用于将主机字节顺序中的 IP 端口号转换为网络字节顺序的 IP 端口号。htons 函数不要求 Winsock DLL 之前已通过对 WSAStartup 函数的成功调用加载。inet_pton()、InetPton()之类的新函数,用于IP地址在“点分十进制”和“二进制整数”之间转换,并且能够处理ipv4和ipv6。

   namelen: name 参数指向的值的长度(以字节为单位),即对应的是地址的长度。
  返回值
   如果未发生错误, 绑定 将返回零。 否则,它将返回SOCKET_ERROR
  示例代码:

struct sockaddr_in addr;
addr.sin_family = AF_INET;//一般与使用 socket() 函数创建 socket 对象时使用的地址族一样
addr.sin_port = htons(13111);//设置端口号
inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
int addr_len = sizeof(addr);//地址信息的长度
int res = bind(SocketServer, (sockaddr*)&addr, addr_len);
if (res == 0)//返回 0 则代表绑定成功
{	
}

 (4)listen:
  listen 函数 (winsock2.h) 让套接字进入侦听状态

int WSAAPI listen([in] SOCKET s,[in] int    backlog
);

  参数:
   s      需要进入监听状态的套接字
   backlog  请求队列的最大长度
  侦听状态:是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。
  请求队列:当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)。
  缓冲区的长度:(能存放多少个客户端请求)可通过 backlog 参数指定,如果将 backlog 的值设置为 SOMAXCONN,就由系统来决定请求队列长度,一般来说这个长度会比较大。
  【注意】
   当请求队列满时,就不再接收新的请求,对于 Linux,客户端会收到 ECONNREFUSED 错误,对于 Windows,客户端会收到 WSAECONNREFUSED 错误。
   listen() 只是让套接字处于侦听状态,并没有接收请求。接收请求需要使用 accept() 函数。
  示例代码:

listen(SocketServer, max_listen);

 (5)accept:
  accept 函数 (winsock2.h) 当套接字处于侦听状态,可用 accept 函数接受客户端的请求

SOCKET WSAAPI accept([in]      SOCKET   s,[out]     sockaddr *addr,[in, out] int      *addrlen
);

  参数:
   s 一个描述符,使用已使用 listen 函数置于侦听状态的套接字。 与客户端连接实际上是使用 accept 返回的套接字建立的。
   addr 指向接收连接实体地址的缓冲区的可选指针,该地址称为通信层。 addr 参数的确切格式由创建 sockaddr 结构中的套接字时建立的地址系列确定。
   addrlen 指向包含 addr 参数指向的结构长度的整数的可选指针,即 addr 的长度。
  返回值:
   如果未发生错误, 则 accept 将返回 类型为 SOCKET 的值,该值是新套接字的描述符。 此返回值是建立实际连接的套接字的句柄。否则,将返回 值 INVALID_SOCKET ,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。
  【注意
   accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号(addr 属于 out,只需要声明以下,然后当接收到客户端时,会将客户端的IP地址和端口号存到里面)
   当没有客户端连接过来时,accept 函数会处于堵塞状态,所以可以使用多线程去处理
  示例代码:

sockaddr_in addr_client;
int addr_client_len = sizeof(addr_client);
SOCKET socketClient = accept(SocketServer, (sockaddr*)&addr_client, &addr_client_len);

 (6)send:
  send 函数 (winsock2.h) send 函数在连接的套接字上发送数据。

int WSAAPI send([in] SOCKET     s,[in] const char *buf,[in] int        len,[in] int        flags
);

  参数:
   [in] s    发送端套接字描述符。
   [in] buf   指向包含要传输的数据的缓冲区的指针,即需要发送的数据的缓冲区。
   [in] len    buf参数指向的缓冲区中数据的长度(以字节为单位),需要发送数据的字节长度。
   [in] flags  一组指定调用方式的标志。 此参数是使用以下任一值的按位 OR 运算符构造的,一般为 0。
  返回值:
   如果未发生错误, send 将返回发送的总字节数,该字节数可能小于 在 len 参数中请求发送的字节数。 否则,将返回值 SOCKET_ERROR,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。
  示例代码:

char buf_send[1024];
send(soc, buf_send, sizeof(buf_send), 0);

 (7)recv:
  recv 函数 (winsock2.h)

int WSAAPI recv([in]  SOCKET s,[out] char   *buf,[in]  int    len,[in]  int    flags
);

  参数
   [in] s   标识连接的套接字的描述符。即接收端套接字描述符
   [out] *buf 指向用于接收传入数据的缓冲区的指针,即指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据
   [in] len   buf 参数指向的缓冲区的长度,即buf的长度
   [in] flags  影响此函数行为的一组标志,一般置为0
  返回值:
   如果未发生错误, recv 将返回收到的字节数, buf 参数指向的缓冲区将包含接收的此数据。 如果连接已正常关闭,则返回值为零。
  【注意】默认情况下,如果 recv 没有接收到数据,它会处于堵塞状态,知道接收到下一个数据
  示例代码:

char buf_rec[1024];
int res = recv(soc, buf_rec, sizeof(buf_rec), 0);
if (res > 0)
{
}
else {
}

 (8)connect:
  connect 函数 (winsock2.h) connect 函数与指定的服务端套接字建立连接。

int WSAAPI connect([in] SOCKET         s,[in] const sockaddr *name,[in] int            namelen
);

  参数:
   s      标识未连接的套接字的描述符。需要连接 socket 服务端的客户端
   name   指向应建立连接的 sockaddr 结构的指针,需要被连接的 socket 服务端的信息
   namelen   name 参数指向的 sockaddr 结构的长度(以字节为单位)。需要被连接的 socket 服务端的信息的大小
  返回值:
   如果未发生错误, 返回零。 否则返回SOCKET_ERROR,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。
  示例代码:

sockaddr_in server_addr;
server_addr.sin_port = htons(13111);
server_addr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &(server_addr.sin_addr));
int res = connect(SocketClient, (sockaddr*)&server_addr, sizeof(server_addr));
if (res == 0)//代表连接成功
{
}

 (9)closesocket:
  closesocket 函数 (winsock2.h) closesocket 函数关闭现有套接字。

int WSAAPI closesocket([in] SOCKET s
);

  参数:
   s  标识要关闭的套接字的描述符。
  返回值:
   如果未发生错误, 则 closesocket 返回零。 否则,将返回 值 SOCKET_ERROR ,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。
  示例代码:

closesocket(SocketServer);

三:服务端整体代码展示

#include<WinSock2.h>
#include<iostream>
#include <WS2tcpip.h>
#include<thread>
#include<mutex>
#pragma comment(lib,"ws2_32.lib")     //链接库文件std::mutex mux;
std::unique_lock<std::mutex> lock(mux);
bool is_used = true;/// <summary>
///	开启线程进行通讯
/// </summary>
/// <param name="soc"></param>
void ServerCom(SOCKET& soc) {char buf_rec[1024];char buf_send[1024];std::string msg;while (is_used) {int res = recv(soc, buf_rec, sizeof(buf_rec), 0);if (res > 0){std::cout << buf_rec << std::endl;memset(buf_send, 0, sizeof(buf_send));//赋值std::cin >> msg;memcpy(buf_send, msg.c_str(), sizeof(buf_send));send(soc, buf_send, sizeof(buf_send), 0);}else {std::cout << "receive fail" << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(10));}}
}
//创建 socket 对象int main(int argc, char* argv[]) {//① 初始化动态链接库 ws2_32.dll,分配资源WORD wVersionRequested = MAKEWORD(2, 2);//指定支持的 windows套接字规范的版本WSADATA wsadata;int init_res = WSAStartup(wVersionRequested, &wsadata);if (init_res != 0){std::cout << "WSA Start Failed" << std::endl;return -1;}else {std::cout << "WSA Start success" << std::endl;}//②创建 socket 服务端SOCKET SocketServer;//声明一个 Socket 套接字对象constexpr int max_listen = 20;//设置最大连接数SOCKET socket_client[max_listen];//创建一个客户端组int index = 0;//当前为第几个连接的客户端std::thread server_th[max_listen];SocketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (SocketServer != INVALID_SOCKET)//判断是否创建成功{//③绑定对应地址信息//设置地址端口号机器地址族struct sockaddr_in addr;addr.sin_family = AF_INET;//一般与使用 socket() 函数创建 socket 对象时使用的地址族一样addr.sin_port = htons(13111);//设置端口号inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));int addr_len = sizeof(addr);//地址信息的长度int res = bind(SocketServer, (sockaddr*)&addr, addr_len);if (res == 0)//返回 0 则代表绑定成功{std::cout << "bind success" << std::endl;//④将套接字设置成被动监听状态,并且为其指定可访问的服务端个数listen(SocketServer, max_listen);while (true) {//创建一个线程sockaddr_in addr_client;int addr_client_len = sizeof(addr_client);//⑤ 使用 accept 等待连接客户端, addr_client 获取客户端 ip,端口//这里可以优化下,使用多线程,在实际情况下避免堵塞SOCKET socketClient = accept(SocketServer, (sockaddr*)&addr_client, &addr_client_len);if (index < max_listen && socketClient != INVALID_SOCKET)//判断接收是否正确,而且没超出最大个数{//发送消息回去,通知一下已经连接成功socket_client[index] = std::move(socketClient);//没有问题,且能连接成功将其传给数组char buf[] = "service: connect success";int send_len = sizeof(buf);send(socket_client[index], buf, send_len, 0);std::cout << "accept" << std::endl;//开启一个线程server_th[index] = std::thread(ServerCom, std::ref(socket_client[index]));index++;}}}else {std::cout << "bind Failed" << std::endl;}}else {std::cout << "socket create Failed" << std::endl;}lock.lock();is_used = false;//关闭线程使用lock.unlock();for (size_t i = 0; i < index; i++){if (server_th[i].joinable()){server_th[i].join();}}//当不在使用时,关闭 socket 服务端和释放动态链接库 ws2-32.dll 分配的资源closesocket(SocketServer);WSACleanup();//释放动态链接库 ws2_32.dll 初始化时多分配的空间return 0;
}

四:客户端整体代码展示

#include<WinSock2.h>
#include<WS2tcpip.h>
#include<iostream>
#include<thread>#pragma comment(lib, "ws2_32.lib")//添加动态连接库的调用
int main(int argc, char* argv[]) {//使用 WSAStartuo 来调用 ws2_32.dll 初始化环境,设定所需要的版本号WSADATA wsa_data;WSAStartup(MAKEWORD(2, 2), &wsa_data);//创建客户端的 SocketSOCKET SocketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//连接服务端,设置服务端信息if (SocketClient != INVALID_SOCKET){//连接服务端sockaddr_in server_addr;server_addr.sin_port = htons(13111);server_addr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &(server_addr.sin_addr));int res = connect(SocketClient, (sockaddr*)&server_addr, sizeof(server_addr));if (res == 0)//代表连接成功{char buf[1024];char buf_send[1024];std::string msg;//开线程接收数据while (true) {int n = recv(SocketClient, buf, sizeof(buf), 0);if (n > 0){std::cout << buf << std::endl;memset(buf_send, 0, sizeof(buf_send));std::cin >> msg;memcpy(buf_send, msg.c_str(), sizeof(buf_send));send(SocketClient, buf_send, sizeof(buf_send), 0);}else {std::cout << "info len:" << n << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(10));//判断下是否是服务端死掉//如果errno == EINTR 则说明 recv 函数是由于程序接收到信号后返回的,socket 连接还是正常的,不应 close 掉 socket 连接。if (errno != EINTR){closesocket(SocketClient);//释放调用 ws2_32 所占用的内存空间WSACleanup();return -1;}}}}}closesocket(SocketClient);//释放调用 ws2_32 所占用的内存空间WSACleanup();return 0;
}

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

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

相关文章

计算机网络803-(4)网络层

目录 1.虚电路服务 虚电路是逻辑连接 2.数据报服务 3.虚电路服务与数据报服务的对比 二.虚拟互连网络-IP网 1.网络通信问题 2.中间设备 3.网络互连使用路由器 三.分类的 IP 地址 1. IP 地址及其表示方法 2.IP 地址的编址方法 3.分类 IP 地址 &#xff08;1&#x…

LabVIEW中的非阻塞定时器

在LabVIEW编程中&#xff0c;通常需要在某些任务执行过程中进行非阻塞的延时操作。例如&#xff0c;显示某条信息一段时间&#xff0c;同时继续执行其他任务&#xff0c;并在延时时间结束后停止显示该信息。这类需求通常用于处理优先级不同的信息显示&#xff0c;如错误信息需要…

【Arduino IDE安装】Arduino IDE的简介和安装详情

目录 &#x1f31e;1. Arduino IDE概述 &#x1f31e;2. Arduino IDE安装详情 &#x1f30d;2.1 获取安装包 &#x1f30d;2.2 安装详情 &#x1f30d;2.3 配置中文 &#x1f30d;2.4 其他配置 &#x1f31e;1. Arduino IDE概述 Arduino IDE&#xff08;Integrated Deve…

Jupyter的使用分享

文章目录 碎碎念安装方法1.安装Anaconda方法2.通过库的安装方式 启动使用教程1.指定目录打开2.启动后的简单使用 小结 碎碎念 前情提示 之前与许多小伙伴交流的时候&#xff0c;发现大家对于pycharm更容易上手&#xff08;可能是比较好设置中文的原因&#xff09;&#xff0c;在…

算法: 前缀和题目练习

文章目录 前缀和题目练习前缀和二维前缀和寻找数组的中心下标除自身以外数组的乘积和为 K 的子数组和可被 K 整除的子数组连续数组矩阵区域和 前缀和题目练习 前缀和 自己写出来了~ 坑: 数据太大,要用long. import java.util.Scanner;public class Main {public static voi…

vue中用echarts做一个躺着的柱状图

在 Vue 中集成 ECharts 并绘制一个躺着的柱状图&#xff08;即横向的柱状图&#xff09;&#xff0c;你可以通过设置 ECharts 的 bar 类型&#xff0c;并配置 xAxis 和 yAxis 来实现。下面是一个完整的 Vue 示例代码。 示例代码&#xff1a; <template><div id"…

《C++编程新探索:实现高效视频拼接算法》

在当今数字化时代&#xff0c;视频内容的创作和处理变得越来越重要。视频拼接作为一种常见的视频处理技术&#xff0c;能够将多个视频片段组合成一个连续的视频&#xff0c;为视频创作者和用户带来了更多的可能性。本文将探讨如何在 C中实现高效的视频拼接算法&#xff0c;为开…

数据结构与算法JavaScript描述练习------第3章列表

1. 增加一个向列表中插入元素的方法&#xff0c;该方法只在待插元素大于列表中的所有元素时才执 行插入操作。这里的大于有多重含义&#xff0c;对于数字&#xff0c;它是指数值上的大小&#xff1b;对于字母&#xff0c;它 是指在字母表中出现的先后顺序。 function isGreate…

【element-tiptap】如何引进系统中的字体?

源码地址&#xff1a; https://github.com/Leecason/element-tiptap 源码中给出的字体如下 可以看到&#xff0c;咱们日常需要的黑体、微软雅黑等都没有&#xff0c;所以这篇文章来探索一下怎么加字体。 另外呢&#xff0c;肯定有小伙伴发现&#xff0c;这个按钮点击的时候&am…

IDEA 配置 Git 详解

本文将介绍在IntelliJ IDEA 中如何配置Git 没有安装配置 Git 的可以参考我的这篇文章&#xff1a;安装配置 Git 一、操作环境及准备 1.win 10 2.已安装且配置了Git 3.有Gitee账户 4.安装了IntelliJ IDEA 2023.2.1 5.全程联网 二、配置步骤 2.1 配置git 1.采用全局设置&…

OpenCV视频I/O(18)视频写入类VideoWriter之初始化 VideoWriter 对象的函数open()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 初始化或重新初始化视频编写器。 该方法打开视频编写器。参数与构造函数 VideoWriter::VideoWriter 中的相同。 cv::VideoWriter::open() 函数用…

C++继承与菱形继承(一文了解全部继承相关基础知识和面试点!)

目的减少重复代码冗余 Class 子类(派生类) &#xff1a; 继承方式 父类&#xff08;基类&#xff09; 继承方式共有三种&#xff1a;公共、保护、私有 父类的私有成员private无论哪种继承方式都不可以被子类使用 保护protected权限的内容在类内是可以访问&#xff0c;但是在…

息肉检测数据集 yolov5 yolov8适用于目标检测训练已经调整为yolo格式可直接训练yolo网络

息肉检测数据集 yolov5 yolov8格式 息肉检测数据集介绍 数据集概述 名称&#xff1a;息肉检测数据集&#xff08;基于某公开的分割数据集调整&#xff09;用途&#xff1a;适用于目标检测任务&#xff0c;特别是内窥镜图像中的息肉检测格式&#xff1a;YOLO格式&#xff08;边…

设计一个OAuth2认证系统:支持第三方登录的实用指南

设计一个OAuth2认证系统:支持第三方登录的实用指南 引言 在现代Web应用中,用户认证是一个至关重要的环节。OAuth2作为一种开放标准,允许用户通过第三方服务进行身份验证,简化了用户登录流程,同时提高了安全性。本文将详细介绍如何设计一个支持第三方登录的OAuth2认证系统…

wenserver中 一些常见的 错误码

EINTR 是 Linux 系统中定义的一个错误码&#xff0c;代表“被信号中断”。当一个系统调用在执行过程中被一个信号处理函数中断时&#xff0c;这个系统调用会立即返回错误&#xff0c;并且 errno 被设置为 EINTR。 举个例子 read函数是阻塞的 现在没有数据要读 我们read一直阻…

【3dgs】总结3DGS与NeRF如何重塑SLAM24年4月最新进展

【3dgs】总结3DGS与NeRF如何重塑SLAM&#xff01; 1. 摘要2. 简洁3. 背景3.1 Existing SLAM Surveys3.2 progress in Radiance Field Theory3.3.1 NeRF3.3.2 3dgs3.4 数据集 4 数据集4.1 SLAM3.1 RGB-D SLAM方法3.1.1 基于NeRF风格的RGB-D SLAM3.1.2 基于3DGS风格的 RGB-D SLAM…

React(一) 认识React、熟悉类组件、JSX书写规范、嵌入变量表达式、绑定属性

文章目录 一、初始React1. React的基本认识2. Hello案例2.1 三个依赖2.2 渲染页面2.3 hello案例完整代码 二、类组件1. 封装类组件2. 组件里的数据3. 组件里的函数 (重点)4. 案例练习(1) 展示电影列表 三、JSX语法1. 认识JSX2. JSX书写规范及注释3. JSX嵌入变量作为子元素4. JS…

小猿口算脚本

实现原理&#xff1a;安卓adb截图传到电脑&#xff0c;然后用python裁剪获得两张数字图片&#xff0c;使用ddddocr识别数字&#xff0c;比较大小&#xff0c;再用adb命令模拟安卓手势实现>< import os import ddddocr from time import sleep from PIL import Imagedef …

遍历有向图链路(DFS算法)- 优化版

在上一节基础上&#xff0c;去除了节点的pre集合&#xff0c;只保留节点next的结合&#xff0c;对数据模型进行了优化&#xff0c;实现思想做了优化。 有向图示例&#xff1a; 基本思路 构建有向图数据模型校验有向图不能出现回路&#xff0c;即当前节点不能出现在历史链路中首…

连续点击三次用户

有用户点击日志记录表 t2_click_log&#xff0c;包含user_id(用户ID),click_time(点击时间)&#xff0c;请查询出连续点击三次的用户数&#xff0c; 连续点击三次&#xff1a;指点击记录中同一用户连续点击&#xff0c;中间无其他用户点击&#xff1b; CREATE TABLE t2_click…