当前位置: 首页 > news >正文

[C++/Linux] 网络I/O处理

引言:网络数据能够正常到达用户并且被接收是进行网络传输的根本目的,网络传输的数据发送和接收有多种方案,本文章就对通过向量接收和发送等数据传输方式,并且对多种I/O模型进详细分析介绍。

目录

一.I/O函数

1.1 recv和send

recv函数

send函数

1.2 readv和writev函数

readv函数

writev函数

1.3 recvmsg和recvmsg函数 

recvmsg函数

sendmsg函数

1.4 客户—服务器实例

服务器端代码:

客户端代码:

二.I/O模型介绍

2.1 阻塞I/O(Blocking I/O)

2.2非阻塞I/O(Non-blocking I/O)

2.3I/O多路复用(I/O Multiplexing)

2.4信号驱动I/O(Signal-driven I/O)

2.5异步I/O(Asynchronous I/O)

三.监视函数 

3.1select和pselect

select函数

pselect函数

3.2poll函数和ppoll函数

poll函数

ppoll函数

四.总结


一.I/O函数

1.1 recv和send

在网络编程中,recv 和 send 函数是用于在套接字上进行数据传输的两个基本函数。它们是在Unix-like系统中的Berkeley套接字 API(也称为BSD套接字API)的一部分,在Windows系统中也有对应的实现。

recv函数

recv 函数用于从连接的套接字接收数据。函数原型如下:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • sockfd:指定接收数据的套接字文件描述符。

  • buf:指向缓冲区的指针,用于存放接收到的数据。

  • len:指定缓冲区的大小,即最多接收的数据量。

  • flags:指定接收数据的操作方式,可以是0或者以下一个或多个值的逻辑或:

    • MSG_OOB:接收带外数据(Out-of-Band Data)。
    • MSG_PEEK:查看数据但不从接收队列中移除数据。
    • MSG_WAITALL:等待所有请求的数据,直到请求的数量被接收为止。
    • 等等。

返回值:

  • 成功时,返回接收到的字节数。
  • 如果连接被对方关闭,返回0。
  • 出错时,返回-1,并设置errno来指示错误。

send函数

send 函数用于向连接的套接字发送数据。函数原型如下:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数说明:

  • sockfd:指定发送数据的套接字文件描述符。

  • buf:指向要发送数据的指针。

  • len:指定要发送数据的长度。

  • flags:指定发送数据的操作方式,可以是0或者以下一个或多个值的逻辑或:

    • MSG_OOB:发送带外数据。
    • MSG_DONTROUTE:发送数据时不通过网关。
    • 等等。

返回值:

  • 成功时,返回发送的字节数。
  • 出错时,返回-1,并设置errno来指示错误。

1.2 readv和writev函数

readv 和 writev 函数是 Unix 和类 Unix 操作系统中的系统调用,它们允许程序在一次操作中从多个缓冲区读取数据或将数据写入多个缓冲区。这些函数对于分散读(scatter read)和集中写(gather write)操作非常有用。

readv函数

readv 函数用于从文件描述符读取数据到多个缓冲区中。函数原型如下:

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

参数说明:

  • fd:指定要读取数据的文件描述符。
  • iov:指向 iovec 结构数组的指针,每个 iovec 结构指定一个缓冲区。
  • iovcnt:指定 iov 数组中 iovec 结构的数量。

iovec 结构定义如下:

struct iovec {void  *iov_base; /* Starting address */size_t iov_len;  /* Number of bytes to transfer */
};
  • iov_base:指向数据缓冲区的指针。
  • iov_len:指定缓冲区的长度。

返回值:

  • 成功时,返回读取的总字节数。
  • 如果遇到文件结束,返回0。
  • 出错时,返回-1,并设置errno来指示错误。

writev函数

writev 函数用于将多个缓冲区的数据写入到文件描述符。函数原型如下:

ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

参数说明:

  • fd:指定要写入数据的文件描述符。
  • iov:指向 iovec 结构数组的指针,每个 iovec 结构指定一个数据源缓冲区。
  • iovcnt:指定 iov 数组中 iovec 结构的数量。

返回值:

  • 成功时,返回写入的总字节数。
  • 出错时,返回-1,并设置errno来指示错误。

1.3 recvmsg和recvmsg函数 

recvmsg 和 sendmsg 函数是 Unix 和类 Unix 操作系统中的系统调用,它们提供了比 recv 和 send 更高级的接口,用于在套接字上发送和接收数据。这些函数支持更多的功能,如scatter/gather I/O、控制消息的发送和接收,以及带外数据。

recvmsg函数

recvmsg 函数用于从套接字接收数据,并可以接收辅助数据(ancillary data)。函数原型如下:

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

参数说明:

  • sockfd:指定接收数据的套接字文件描述符。

  • msg:指向 msghdr 结构的指针,该结构包含了接收操作的所有参数。

  • flags:指定接收数据的操作方式,可以是0或者以下一个或多个值的逻辑或:

    • MSG_OOB:接收带外数据。
    • MSG_PEEK:查看数据但不从接收队列中移除数据。
    • MSG_WAITALL:等待所有请求的数据,直到请求的数量被接收为止。
    • 等等。

msghdr 结构定义如下:

struct msghdr {void         *msg_name;       /* Optional address */socklen_t     msg_namelen;    /* Size of address */struct iovec *msg_iov;        /* Scatter/gather array */int           msg_iovlen;     /* # elements in msg_iov */void         *msg_control;    /* Ancillary data, see below */socklen_t     msg_controllen; /* Ancillary data buffer len */int           msg_flags;      /* Flags on received message */
};
  • msg_name 和 msg_namelen:用于接收对方地址信息(仅用于面向连接的套接字)。
  • msg_iov 和 msg_iovlen:指定分散读的缓冲区数组 iovec 和其长度。
  • msg_control 和 msg_controllen:用于接收辅助数据和控制信息。
  • msg_flags:接收消息的标志,由系统填充。

返回值:

  • 成功时,返回接收到的字节数。
  • 如果连接被对方关闭,返回0。
  • 出错时,返回-1,并设置errno来指示错误。

sendmsg函数

sendmsg 函数用于向套接字发送数据,并可以发送辅助数据。函数原型如下:

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

参数说明:

  • sockfd:指定发送数据的套接字文件描述符。

  • msg:指向 msghdr 结构的指针,该结构包含了发送操作的所有参数。

  • flags:指定发送数据的操作方式,可以是0或者以下一个或多个值的逻辑或:

    • MSG_OOB:发送带外数据。
    • MSG_DONTROUTE:发送数据时不通过网关。
    • 等等。

返回值:

  • 成功时,返回发送的字节数。
  • 出错时,返回-1,并设置errno来指示错误。

1.4 客户—服务器实例

这个服务器的功能主要是返回客户端输入的数据长度,其中使用了I/O函数中的readv和writev。

服务器端代码:

(我这个代码涉及到许多前面的基础知识,需要了解的就可以看我上一篇博客:

写文章-CSDN创作中心socket套接字函数-CSDN博客写文章-CSDN创作中心)

#include<t_stdio.h>
#include<t_file.h>
#include<stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include<unistd.h>
static struct iovec*vs=NULL;
void sig_process(int signo){//信号处理函数printf("catch a exit signal..\n");free(vs);_exit(0);
}#define port 8888//端口号
#define backlog 2//最大监听数量//业务处理逻辑
void process_conn_server(int s){char buffer [30];ssize_t size=0; //向量的缓冲区//申请3个向量空间struct iovec * v = (struct iovec *)malloc(3*sizeof(struct iovec));if(!v) E_MSG("malloc",-1);vs = v;//挂接全局变量,易于释放管理v[0].iov_base= buffer;//每个向量十个地址空间v[1].iov_base= buffer+10;v[2].iov_base= buffer+20;v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;//初始化长度为10for(;;){size = readv(s, v , 3);// 从套接字中读取数据放入数据缓冲区if(size == 0){return ;}//构建响应字符sprintf(v[0].iov_base,"%d",size);sprintf(v[1].iov_base,"bytes alter");sprintf(v[2].iov_base,"ogether\n");//设置写入数据的长度v[0].iov_len = strlen(v[0].iov_base);v[1].iov_len = strlen(v[1].iov_base);v[2].iov_len = strlen(v[2].iov_base);writev(s , v , 3);//发送给客户端}}
int main(int argc ,char * argv[])
{int ss ,sc;//服务器和客户端的套接字文件描述符struct  sockaddr_in server_addr;//服务器地址结构struct  sockaddr_in client_addr;//客户端地址结构pid_t pid;//创建子进程进行分叉进行signal(SIGINT,sig_process);//添加sigint信号到信号掩码signal(SIGPIPE,sig_process);ss = socket(AF_INET,SOCK_STREAM,0);// 创建服务器套接字if(ss < 0) E_MSG("socket",-1);bzero(&server_addr,sizeof(server_addr));//将地址清零server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htons(INADDR_ANY);//本地地址server_addr.sin_port=htons(port);//设置端口//绑定本地到套接字符int err = bind(ss,(struct  sockaddr * )&server_addr,sizeof(server_addr));if(err < 0)E_MSG("bind",-1);err = listen(ss,backlog);//创建监听队列for(;;){int addrlen = sizeof(struct sockaddr);sc = accept(ss,(struct sockaddr * )&client_addr,&addrlen);//获取用户套接字if(sc < 0)continue;//客户端出错,结束这次循环,继续监听客户;pid = fork();//创建新的子进程来处理当前连接客户端;if(pid==0){close(ss);// 子进程中关闭服务端process_conn_server(sc);}else {close(sc);//父进程关闭客户端连接,继续监听;}}return 0;
}

客户端代码:

#include<t_stdio.h>
#include<t_file.h>
#include<stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include<unistd.h>
#define port 8888
static int s;
static struct iovec*vc=NULL;
void  sig_proccess(int signo){printf("catch a exit signal..\n");free(vc);_exit(0);
}void sig_pipe(int signo){printf("catch a sigpipe signal..\n");free(vc);_exit(0);}//业务处理函数
void process_conn_client(int s){char buffer [30];ssize_t size=0; //向量的缓冲区//申请3个向量空间struct iovec * v = (struct iovec *)malloc(3*sizeof(struct iovec));if(!v) E_MSG("malloc",-1);vc = v;//挂接全局变量,易于释放管理v[0].iov_base= buffer;//每个向量十个地址空间v[1].iov_base= buffer+10;v[2].iov_base= buffer+20;v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;//初始化长度为10int i=0;for(;;){//从标准输入中读取数据放入缓冲区buffer中size = read(0,v[0].iov_base,10);//只有十个字节if(size>0){v[0].iov_len = size;writev(s,v,1);//发送给服务器v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;size=readv(s,v,3);//从服务器读取数据for(i = 0;i<3;i++){if(v[i].iov_len > 0){write(1,v[i].iov_base,v[i].iov_len);//将服务区处理后数据输出}}                  }}}
int main(int argc ,char * argv[])
{struct sockaddr_in server_addr;//服务器结构地址int err;if(argc == 1){printf("pls input server addr\n");return 0;}//没有输入指令参数signal(SIGINT,sig_proccess);signal(SIGPIPE,sig_pipe);//设置信号掩码s = socket(AF_INET,SOCK_STREAM,0);//创建客户端套接字if(s < 0)E_MSG("socket",-1);//设置服务器地址bzero(&server_addr,sizeof(server_addr));//将地址清0server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htons(INADDR_ANY);//本地地址server_addr.sin_port=htons(port);//设置端口inet_pton(AF_INET,argv[1],&server_addr.sin_addr);//将用户输入的字符串ip转化为二进制ipconnect(s,(struct sockaddr *)&server_addr,sizeof(server_addr));//连接服务器process_conn_client(s);return 0;
}

 对于这两个代码,大家可以仔细阅读,我写了非常详细的注释,包括信号的处理,业务逻辑的处理等等,可以将我们整个基础网络知识串联起来!

二.I/O模型介绍

网络编程中的I/O模型主要影响程序如何处理网络套接字的读写操作。以下是几种常见的I/O模型:

2.1 阻塞I/O(Blocking I/O)

 阻塞I/O模型是最简单的I/O模型。当发起一个I/O操作(如读或写)时,如果数据未准备好,程序会阻塞直到操作完成。在这段时间内,程序无法执行其他任务。

// 阻塞读
char buffer[1024];
read(socket, buffer, sizeof(buffer));

2.2非阻塞I/O(Non-blocking I/O)

在非阻塞I/O模型中,I/O操作不会阻塞程序。如果数据未准备好,I/O操作会立即返回一个错误码(通常是EAGAINEWOULDBLOCK)。程序需要定期轮询检查数据是否准备好.

// 设置套接字为非阻塞模式
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);// 非阻塞读
char buffer[1024];
while (read(socket, buffer, sizeof(buffer)) == -1 && errno == EAGAIN) {// 数据未准备好,继续做其他事情
}

2.3I/O多路复用(I/O Multiplexing)

 I/O多路复用允许程序同时监视多个文件描述符,等待一个或多个变得“就绪”。这可以通过selectpollepoll(Linux特有)或kqueue(BSD特有)系统调用来实现。

// 使用select进行I/O多路复用
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(socket, &readfds);select(socket + 1, &readfds, NULL, NULL, NULL);if (FD_ISSET(socket, &readfds)) {char buffer[1024];read(socket, buffer, sizeof(buffer));
}

2.4信号驱动I/O(Signal-driven I/O)

信号驱动I/O模型中,程序通过sigaction系统调用请求内核在文件描述符就绪时发送一个信号。当数据准备好时,内核会发送一个SIGIO信号,程序可以在信号处理函数中处理数据。 

// 设置信号处理函数
struct sigaction sa;
sa.sa_handler = io_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGIO, &sa, NULL);// 设置套接字的所有者
fcntl(socket, F_SETOWN, getpid());// 开启信号驱动I/O
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_ASYNC);

2.5异步I/O(Asynchronous I/O)

 异步I/O模型中,程序发起I/O操作后,立即返回,无需等待数据准备好。当操作完成时,程序会收到通知。这种模型通常通过aio_readaio_write等系统调用来实现。

// 异步读
aiocb aio;
memset(&aio, 0, sizeof(aio));
aio.aio_fildes = socket;
aio.aio_buf = buffer;
aio.aio_nbytes = sizeof(buffer);
aio.aio_offset = 0;aio_read(&aio);

每种I/O模型都有其适用的场景。例如,阻塞I/O适用于简单应用,非阻塞I/O和I/O多路复用适用于需要处理多个套接字的应用,而异步I/O适用于需要高性能和低延迟的应用。在选择合适的I/O模型时,需要考虑应用的特定需求和性能。

三.监视函数 

3.1select和pselect

在网络编程中,select 和 pselect 函数是用于多路复用(I/O multiplexing)的系统调用,它们允许程序同时监视多个文件描述符,以判断它们是否准备好进行读、写或异常(错误)操作。

select函数

select 函数的原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

参数说明:

  • nfds:需要检查的最高文件描述符加1。通常设置为最大的文件描述符加上1。
  • readfds:指向一组文件描述符的指针,这些文件描述符需要检查是否准备好读取数据。
  • writefds:指向一组文件描述符的指针,这些文件描述符需要检查是否准备好写入数据。
  • exceptfds:指向一组文件描述符的指针,这些文件描述符需要检查是否发生异常。
  • timeout:指向 timeval 结构的指针,用于指定 select 应该阻塞等待的最大时间。如果设置为 NULLselect 将无限期地等待;如果设置为0,select 将立即返回。

timeval 结构定义如下:

struct timeval {long    tv_sec;         /* seconds */long    tv_usec;        /* microseconds */
};

返回值:

  • 成功时,返回准备好的文件描述符的数量。
  • 如果超时,返回0。
  • 出错时,返回-1,并设置errno来指示错误。

使用 select 时,需要使用 FD_ZEROFD_SET 和 FD_CLR 宏来操作 fd_set 集合。

pselect函数

pselect 函数与 select 类似,但它提供了一些额外的功能,特别是在处理信号方面。pselect 的原型如下:

int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);

参数说明:

  • 前四个参数与 select 函数相同。
  • timeout:指向 timespec 结构的指针,用于指定 pselect 应该阻塞等待的最大时间。timespec 结构与 timeval 类似,但它使用秒和纳秒来表示时间。
  • sigmask:指向 sigset_t 类型的一个信号集的指针,用于在 pselect 调用期间临时替换进程的信号掩码。这意味着 pselect 在等待文件描述符就绪时会阻塞指定的信号。

timespec 结构定义如下:

struct timespec {time_t  tv_sec;         /* seconds */long    tv_nsec;        /* nanoseconds */
};

返回值:

  • 成功时,返回准备好的文件描述符的数量。
  • 如果超时,返回0。
  • 出错时,返回-1,并设置errno来指示错误。

例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>int main() {fd_set readfds;struct timeval timeout;int result;FD_ZERO(&readfds);FD_SET(0, &readfds); // 监视标准输入(文件描述符0)timeout.tv_sec = 5; // 设置超时时间为5秒timeout.tv_usec = 0;result = select(1, &readfds, NULL, NULL, &timeout);if (result == -1) {perror("select");exit(EXIT_FAILURE);} else if (result > 0) {printf("Data is available to read.\n");} else {printf("Timeout occurred.\n");}return 0;
}

在这个例子中,我们使用 select 来监视标准输入(文件描述符0),设置超时时间为5秒。如果在这段时间内有数据可读,select 将返回大于0的值;如果超时,将返回0;如果发生错误,将返回-1。

3.2poll函数和ppoll函数

 poll 和 ppoll 函数是 Unix 和类 Unix 操作系统中的系统调用,用于多路复用(I/O multiplexing),允许程序同时监视多个文件描述符的读写状态。与 select 和 pselect 函数相比,poll 和 ppoll 提供了更丰富的接口和更好的性能。

poll函数

poll 函数的原型如下:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数说明:

  • fds:指向一个 pollfd 结构数组的指针,每个结构描述了一个要监视的文件描述符。
  • nfds:指定 fds 数组中 pollfd 结构的数量。
  • timeout:指定 poll 应该阻塞等待的最大时间(毫秒)。如果设置为-1,poll 将无限期地等待;如果设置为0,poll 将立即返回。

pollfd 结构定义如下:

struct pollfd {int   fd;         /* file descriptor */short events;     /* requested events */short revents;    /* returned events */
};
  • fd:要监视的文件描述符。
  • events:请求监视的 events(通过位掩码指定)。
  • revents:返回时,表示实际发生的事件(通过位掩码指定)。

events 和 revents 可以是以下标志的组合(定义在 <poll.h> 头文件中):

  • POLLIN:有数据可读。
  • POLLOUT:可以写数据。
  • POLLERR:发生错误。
  • POLLHUP:挂起。
  • POLLNVAL:无效的请求。

返回值:

  • 成功时,返回准备好的文件描述符的数量。
  • 如果超时,返回0。
  • 出错时,返回-1,并设置errno来指示错误。

ppoll函数

ppoll 函数与 poll 类似,但它提供了一些额外的功能,特别是在处理信号方面。ppoll 的原型如下:

int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask);

参数说明:

  • 前三个参数与 poll 函数相同。
  • timeout:指向 timespec 结构的指针,用于指定 ppoll 应该阻塞等待的最大时间。timespec 结构与 timeval 类似,但它使用秒和纳秒来表示时间。
  • sigmask:指向 sigset_t 类型的一个信号集的指针,用于在 ppoll 调用期间临时替换进程的信号掩码。这意味着 ppoll 在等待文件描述符就绪时会阻塞指定的信号。

返回值:

  • 成功时,返回准备好的文件描述符的数量。
  • 如果超时,返回0。
  • 出错时,返回-1,并设置errno来指示错误。

例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>int main() {struct pollfd fds[1];int result;fds[0].fd = 0; // 监视标准输入(文件描述符0)fds[0].events = POLLIN; // 监视可读事件result = poll(fds, 1, 5000); // 设置超时时间为5000毫秒if (result == -1) {perror("poll");exit(EXIT_FAILURE);} else if (result > 0) {if (fds[0].revents & POLLIN) {printf("Data is available to read.\n");}} else {printf("Timeout occurred.\n");}return 0;
}

 在这个例子中,我们使用 poll 来监视标准输入(文件描述符0),设置超时时间为5000毫秒。如果在这段时间内有数据可读,poll 将返回大于0的值;如果超时,将返回0;如果发生错误,将返回-1。

四.总结

这篇文章对数据I/O进行了整体的介绍,包括recv ,send, readv , writev,recmsg , sendmsg等I/O函数,并且给了很多例子,来介绍I/O的各种模型及其使用,相信经过这篇文章的学习,大家对网络编程中的I/O操作能有个整体认知!!!

相关文章:

[C++/Linux] 网络I/O处理

引言&#xff1a;网络数据能够正常到达用户并且被接收是进行网络传输的根本目的&#xff0c;网络传输的数据发送和接收有多种方案&#xff0c;本文章就对通过向量接收和发送等数据传输方式&#xff0c;并且对多种I/O模型进详细分析介绍。 目录 一.I/O函数 1.1 recv和send rec…...

HarmonyOS4 页面路由

Index.ets: import router from ohos.routerclass RouterInfo {// 页面路径url: string// 页面标题title: stringconstructor(url: string, title: string) {this.url urlthis.title title} }Entry // 入口組件 Component struct Index {State message: string 页面列表// …...

ShardingSphere再回首

概念&#xff1a; 连接&#xff1a;通过协议 方言及库存储的适配&#xff0c;连接数据和应用&#xff0c;关注多模数据苦之间的合作 增量&#xff1a;抓取库入口流量题提供重定向&#xff0c; 流量变形(加密脱敏)/鉴权/治理(熔断限流)/分析等 可插拔&#xff1a;微内核 DDL:cr…...

第七篇:3.6 其他评估考虑/4.审计指南/5. 通用报告规范/6.披露指南、参考标准及其他 - IAB/MRC及《增强现实广告效果测量指南1.0》

翻译计划 第一篇概述—IAB与MRC及《增强现实广告效果测量指南》之目录、适用范围及术语第二篇广告效果测量定义和其他矩阵之- 3.1 广告印象&#xff08;AD Impression&#xff09;第三篇广告效果测量定义和其他矩阵之- 3.2 可见性 &#xff08;Viewability&#xff09;第四篇 …...

函数、指针和数组的相互运用(C语言)

1、函数指针数组 含义&#xff1a;数组的每个元素都是函数指针类型.eg&#xff1a; &#xff08;此代码链接&#xff1a;http://t.csdnimg.cn/ClJmb.也可以在我发布博客中找到&#xff09; 2、指向函数指针数组的指针 1、引入 3、回调函数 1、含义&#xff1a;就是一个通过…...

.Net Core/.Net 6/.Net 8,一个简易的消息队列

.Net Core/.Net 6/.Net 8,一个简易的消息队列 身份验证接口身份验证接口实现program.cs通过api调用 做着玩的&#xff0c; 只实现了消息入队出队功能&#xff0c;没有持久化&#xff0c;也没有其它任何高级功能 直接上代码 public class AMQBase//:ISingleton {/// <summary…...

OpenHarmony4.0分布式任务调度浅析

1 概述 OpenHarmony 分布式任务调度是一种基于分布式软总线、分布式数据管理、分布式 Profile 等技术特性的任务调度方式。它通过构建一种统一的分布式服务管理机制&#xff0c;包括服务发现、同步、注册和调用等环节&#xff0c;实现了对跨设备的应用进行远程启动、远程调用、…...

element-ui backtop 组件源码分享

今日简单分享 backtop 组件的源码实现&#xff0c;从以下三个方面&#xff1a; 1、backtop 组件页面结构 2、backtop 组件属性 3、backtop 组件事件 一、backtop 组件页面结构 二、backtop 组件属性 2.1 target 属性&#xff0c;触发滚动的对象&#xff0c;类型 string&am…...

MongoDB快照(LVM)业务场景应用实战

MongoDB和LVM快照概述 MongoDB的重要性&#xff1a;MongoDB支持的灵活的文档模型&#xff0c;使其成为处理大量分散数据的理想选择&#xff0c;特别是在需要快速迭代和频繁更改数据结构的应用中。 LVM&#xff08;逻辑卷管理&#xff09;快照技术基本概念&#xff1a;LVM允许…...

3D开发工具HOOPS:推动汽车行业CAD可视化发展

在最近的行业对话中&#xff0c;Tech Soft 3D&#xff08;HOOPS厂商&#xff09;的Jonathan Girroir和Actify的Peter West探讨了CAD可视化在当代企业中的重要性和挑战。作为CAD可视化领域的佼佼者&#xff0c;Actify通过其广受欢迎的Spinfire应用&#xff0c;赋能了全球40多个国…...

Centos安装MySQL提示公钥尚未安装

一、问题 在Centos7.9使用yum安装MySQL时出现错误&#xff0c;提示&#xff1a;mysql-community-server-5.7.44-1.el7.x86_64.rpm 的公钥尚未安装&#xff0c;如下图所示&#xff1a; 执行命令&#xff1a;systemctl start mysqld也提示错误&#xff1a;Failed to start mysq…...

FebHost:英国.UK域名简介

.UK域名是互联网上最广为人知且广泛使用的国家代码顶级域名之一。作为英国官方的国家代码&#xff0c;自诞生之日起&#xff0c;.UK域名对英国本土个人、企业及在线品牌建设扮演了关键角色。 .UK域名于1985年首次推出&#xff0c;这是早期为创建有序域名系统而努力的一部分。当…...

SQL Serve---查询

概要 1、order by子句 —默认asc&#xff08;升序&#xff09;、desc&#xff08;降序&#xff09; 2、distinct关键字 3、group by子句 4、聚合函数 —max()、min()、sum()、avg()、count() 5、having子句 6、compute子句 英文关键字 order by 排序 asc…...

RabbitMQ3.13.x之十一_RabbitMQ中修改用户密码及角色tags

RabbitMQ3.13.x之十一_RabbitMQ中修改用户密码及角色tgs 文章目录 RabbitMQ3.13.x之十一_RabbitMQ中修改用户密码及角色tgs1. 修改用户的密码1. 修改密码语法2. 修改案例 2.修改角色tags1. 修改标签(tags)语法2. 修改案例 可以使用 RabbitMQ 的命令行工具 rabbitmqctl 来修改用…...

Taro打包生成不同目录

使用taro init创建taro项目时&#xff0c;taro默认打包目录是&#xff1a; /config/index.js outputRoot:dist默认的目录&#xff0c;编译不同平台代码时就会覆盖掉&#xff0c;为了达到多端同步调试的目的&#xff0c;这时需要修改默认生成目录了&#xff0c;通过查看官方文…...

2024-04-08 NO.5 Quest3 手势追踪进行 UI 交互

文章目录 1 玩家配置2 物体配置3 添加视觉效果4 添加文字5 其他操作5.1 双面渲染5.2 替换图片 ​ 在开始操作前&#xff0c;我们导入先前配置好的预制体 MyOVRCameraRig&#xff0c;相关介绍在 《2024-04-03 NO.4 Quest3 手势追踪抓取物体-CSDN博客》 文章中。 1 玩家配置 &a…...

PaddleDetection 项目使用说明

PaddleDetection 项目使用说明 PaddleDetection 项目使用说明数据集处理相关模块环境搭建 PaddleDetection 项目使用说明 https://github.com/PaddlePaddle/PaddleDetection/blob/release/2.7/configs/ppyoloe/README_cn.md 自己项目&#xff1a; https://download.csdn.net/d…...

leetcode解题思路分析(一百五十五)1352 - 1358 题

最后 K 个数的乘积 请你实现一个「数字乘积类」ProductOfNumbers&#xff0c;要求支持下述两种方法&#xff1a; add(int num) 将数字 num 添加到当前数字列表的最后面。 getProduct(int k) 返回当前数字列表中&#xff0c;最后 k 个数字的乘积。 你可以假设当前列表中始终 至少…...

如何将普通maven项目转为maven-web项目

文件-项目结构&#xff08;File-->Project Structure &#xff09; 模块-->learn&#xff08;moudle-->learn&#xff09; 选中需要添加web的moudle&#xff0c;点击加号&#xff0c;我得是learn&#xff0c;单击选中后进行下如图操作&#xff1a; 编辑路径 结果如下…...

LeetCode 226. 翻转二叉树

给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,3,1] 示例…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...