[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操作能有个整体认知!!!

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

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

相关文章

RabbitMQ消息模型之Fanout消息模型

Fanout消息模型 * 广播模型&#xff1a;* 一个交换机绑定多个队列* 每个队列都有一个消费者* 每个消费者消费自己队列中的消息&#xff0c;每个队列的信息是一样的生产者 package com.example.demo02.mq.fanout;import com.example.demo02.mq.util.ConnectionUtils; impor…

神策sdk埋点

一&#xff0c;提出数据需求 首先要把数据需求提出来&#xff0c;先落地成一个标准的文档。数据需求是由业务或者产品去做&#xff0c;然后设计数据采集方案是基于我们的数据需求&#xff0c;首先要满足数据需求&#xff0c;其次在数据在设计完成之后要进行评审。/基于需求设计…

【docker】linux删除大于特定时间的备份文件

目录 shell 删除脚本命令解析 环境&#xff1a;linux容器&#xff1a;docker 前提&#xff1a; 继上次讲述如何备份线上数据库之后&#xff0c;我们来看如何删除多余的dump备份文件 参考&#xff1a;linux写shell脚本备份线上数据库&#xff08;备份为dump文件&#xff09; …

JavaScript之Object.defineProperty详解

Object.defineProperty 是 JavaScript 中用于定义或修改对象属性的方法之一。它允许我们精确地控制对象属性的特性&#xff0c;包括可枚举性、可写性、可配置性等。 1. 基本语法 Object.defineProperty(object, propertyName, descriptor); object&#xff1a;要在其上定义属性…

【PyQt5】环境配置

PyQt5 环境配置 一、前言1.1 PyQt5介绍1.2 PyCharm集成Pyqt5 二、pyqt5安装三、PyQt5-tools工具包安装四、常用工具环境配置4.1、环境变量配置4。2、验证是否安装成功 五、pycharm中设置Qt工具&#xff08;Qt Designer、PyUIC、PyRcc&#xff09;5.1、配置Qt Designer5.2、配置…

gitee上传出现git did not exit cleanly (exit code 1)的错误

在最后push的时候出现下面的结果&#xff1a; 出现这个错误的原因有好多种&#xff0c;目前介绍博主遇到的两种&#xff1a; 在第一次进行push操作的时候&#xff0c;需要输入用户名和密码&#xff0c;如果输入错误&#xff0c;则最后可能会出现上述报错 解决方法&#xff1a;…

centos7安装 on-my-zsh

如下&#x1f447; yum install -y zsh chsh -s /bin/zsh yum install -y git sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 重启即可生效啦~

Python爬虫怎么挣钱?6个Python爬虫赚钱方式,搞搞副业不是问题

1.最典型的就是找爬虫外包活儿 网络爬虫最通常的的挣钱方式通过外包网站&#xff0c;做中小规模的爬虫项目&#xff0c;向甲方提供数据抓取&#xff0c;数据结构化&#xff0c;数据清洗等服务。新入行的程序员大多都会先尝试这个方向&#xff0c;直接靠技术手段挣钱&#xff0…

Oladance、南卡、Cleer开放式耳机好不好用?深度测评剖析实力王!

​近期&#xff0c;不少朋友都在询问如何挑选适合自己的开放式耳机。面对市场上众多品牌的开放式耳机&#xff0c;它们在音质、舒适度和性能上都有所差异&#xff0c;挑选一款合适的耳机对于音乐爱好者来说尤为重要。购买到品质不达标的耳机可能会影响音乐体验&#xff0c;甚至…

Linux nfs 环境搭建

1.开发背景 nfs 即网络文件共享&#xff0c;主要通过 tcp、udp 等网络通讯的方式实现不同机器间的文件共享 2.开发需求 搭建 ubuntu 下的服务端&#xff0c;嵌入式开发板共享 ubuntu 的某个文件夹 3.开发环境 ubuntu20.04 嵌入式开发板 4.实现步骤 4.1 搭建 ubuntu 服务器…

sonar搭建(linux系统)

前景 静态代码扫描是CI/CD中重要的一环&#xff0c;可以在代码提交到代码仓库之后&#xff0c;在CI/CD流程中加入代码扫描步骤&#xff0c;从而及时地对代码进行质量的检查。这可以有效地降低后期维护成本&#xff0c;优化产品质量&#xff0c;提高产品交付速度。同时&#xf…

浅尝一下ECS(Entity Component System)(学习笔记)

参考文章&#xff1a;浅谈Unity ECS&#xff08;一&#xff09;Uniy ECS基础概念介绍&#xff1a;面向未来的ECS - 知乎 (zhihu.com) 视频链接&#xff1a;【青幻译制】GDC讲座系列之三 守望先锋的游戏架构和网络代码_哔哩哔哩_bilibili 云风的 BLOG: 浅谈《守望先锋》中的 E…

XSS攻击与MySQL注入漏洞攻击手法

一、XSS攻击 概念&#xff1a;XSS攻击也被称为 “跨站脚本攻击”&#xff0c;通过将恶意得Script代码注入到Web页面中&#xff0c;当 用户 浏览该页之时&#xff0c;嵌入其中Web里面的Script代码会被执行&#xff0c;从而达到恶意攻击 用户的目的。 攻击对象&#…

保证线程安全的10个小技巧分享

对于从事后端开发的同学来说&#xff0c;线程安全问题是我们每天都需要考虑的问题。 线程安全问题通俗的讲&#xff1a;主要是在多线程的环境下&#xff0c;不同线程同时读和写公共资源&#xff08;临界资源&#xff09;&#xff0c;导致的数据异常问题。 比如&#xff1a;变…

计算两个日期之间相差的天数的四种方法

计算两个日期之间相差的天数的四种方法 第一种&#xff1a;时间戳的方式&#xff0c;计算两个日期的时间戳的差&#xff0c;再除当天的毫秒数即可得到相差的天数。 public static void main(String[] args) {DateFormat dft new SimpleDateFormat("yyyy-MM-dd");t…

家居网购项目(一)

文章目录 1.前置知识1.项目开发阶段2.Java经典三层架构3.项目具体分层&#xff08;包方案&#xff09;4.MVC 2.开发环境搭建1.新建普通javaweb项目&#xff0c;导入jar包2.创建项目结构3.搭建前端页面 3.会员注册前端js校验1.需求分析2.代码login.html 3.结果4.调试阶段1.验证信…

LeetCode 题目:两个总和

LeetCode 题目&#xff1a;两个总和 描述&#xff1a; 编写一个函数&#xff0c;输入为一个整数数组nums和一个目标整数target&#xff0c;要求找到数组中两个数的和等于target&#xff0c;并返回这两个数的索引。 函数定义&#xff1a; def two_sum(nums: List[int], targe…

Java多线程开发——基础篇

目录 1.基本概念 2.创建线程方式 2.1直接建立线程 2.2实现Runnable接口 3.3实现Callable接口 3.4 了解Future接口 Future模式主要角色及其作用 3.5实例化FutureTask类 3.实现线程安全 3.1定义 3.2不安全原因 3.3解决方案 3.4volatile与synchronized区别 4.极端情…

Docker 学习笔记(三):Centos7 中 Docker 使用,镜像、容器,以及操作等常用命令小结

一、前言 记录时间 [2024-4-7] 前置文章&#xff1a; Docker学习笔记&#xff08;一&#xff09;&#xff1a;入门篇&#xff0c;Docker概述、基本组成等&#xff0c;对Docker有一个初步的认识 Docker学习笔记&#xff08;二&#xff09;&#xff1a;在Linux中部署Docker&#…

【数据结构】FHQ-Treap

因为想要学可持久化平衡树&#xff0c;但是之前用平衡树基本都是splay这种需要旋转的&#xff0c;不利于可持久化&#xff0c;所以今天来学一下fhq-treap这种不需要旋转的平衡树 fhq-treap是一种基于分裂&#xff08;split&#xff09;和合并&#xff08;merge&#xff09;的一…