【网络】多路转接——五种IO模型 | select
🐱作者:一只大喵咪1201
🐱专栏:《网络》
🔥格言:你只管努力,剩下的交给时间!
五种IO模型 | select
- 🍧五种IO模型
- 🍧select
- 🧁认识接口
- 🧁简易select服务器
- 🧁select的特点
- 🍧总结
🍧五种IO模型
在学习系统部分的时候,本喵就讲解过IO,当时我们学习的IO就是从文件中读数据和写数据,到了后来学习网络的时候,我们知道,从网络中读取和写入数据也是IO,那么IO到底是什么呢?今天我们来更深刻的认识一下IO。
就拿读取数据来说,无论是调用read
还是recv
,在文件描述符所指向的struct file
中的接收缓冲区如果没有数据的时候,都会阻塞等待。
当缓冲区中有数据后,才会进行读取,所谓读取,本质就是在拷贝,就是将内核缓冲区中的数据拷贝到用户缓冲区中供用户去使用。
无论是等待还是拷贝,都是读取的过程,二者缺一不可。
- IO = 等 + 数据拷贝
那么什么是高效的IO呢?我们知道,IO过程中我们在意的是拷贝,而不是等待,由于各种技术的发展,拷贝所花费的时间几乎是固定的,是由电路或者系统等等机制来保证的,所以拷贝的效率已经很难再有提升了。
- 高效的IO = 减少等待的比重
IO的种类有五种,下面本喵给大家介绍一下:
- 阻塞IO:在内核将数据准备好之前,系统调用会一直等待,所有的套接字以及文件,默认都是阻塞方式。
如上图所示便是阻塞IO的示意图,在进程调用recvfrom
从内核缓冲区中读取数据时,如果数据没有准备好,进程就会阻塞在调用处等待,直到数据准备好,才会将内核缓冲区的数据拷贝到用户缓冲区,并且给进程返回值。
- 阻塞IO是最常见的IO模型,也是最简单的IO模型,我们之前写的所有IO都是阻塞式的。
- 非阻塞IO:如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回
EAGAN
或者EWOULDBLOCK
错误码。
如上图所示,进程调用recvfrom
从内核缓冲区中读取数据时,即使数据没有准备好,仍然会给进程一个返回一个EAGAN
或者EWOULDBLOCK
。
通常情况下使用这种IO方式时,如果返回EAGAN
或者EWOULDBLOCK
,说明数据没有准备好,就会再次调用recvfrom
去读取数据,如此反复,直到数据准备好并且完成拷贝,最后返回表示成的返回值。
- 非阻塞IO需要程序员以循环的方式反复尝试读写文件描述符,这个过程称为轮询。
- 这对CPU来说是较大的浪费,一般只有特定场景下才使用。
默认情况下,文件描述符fd指向的struct file
中的缓冲区的阻塞式IO,所以我们前面无论是在进行文件操纵还是使用套接字的时候,都是阻塞IO。
如上图所示系统调用fcntl
,可以使用它将fd所指向的文件改成非阻塞IO模式,它的参数是一个可变参数。
- int fd:要修改文件的文件描述符fd。
- 返回值fl:大于0表示当前文件的状态,小于0表示调用失败。
- int cmd:对fd所指向的文件要进行的操作,可以传递两个参数:
F_GETFL
:用来获取该文件当前的状态。F_SETFL
:用来设置该文件当前的状态。
- 可变参数部分:可以传递参数有O_RDONLY,O_WRONLY等等,最重要的是
O_NONBLOCK
非阻塞IO方式,但是在传参的时候,必须和fl
进行按位或,如fl | O_NONBLOCK
。
如上图所示代码是本喵写的一个工具函数,作用就是将指定fd
所代表的文件设置成非阻塞IO方式。
如上图,从内核中的代码可以看到,O_NONBLOCK
是一个宏,仅仅代表着一个比特位的状态,所以在传参时,需要使用fl | O_NONBLOCK
的方式,在文件原有的状态上增加非阻塞。
再重新来看一下read
系统调用:
如上图所示为man
手册中的描述,调用失败以后,返回-1,并且错误码被设置。
- 设置不同的错误码代表着不同的意义。
如上图所示,可以设置的错误码有这么多,虽然返回值是-1,但是不同的错误码代表着不同的情况,其中EAGAIN
或者EWOULDBLOCK
表示的就是数据没有准备好,需要稍后再读。
- 读取数据时,内核中缓冲区的数据没有准备好并没有错,这是很正常的情况。
- 所以错误码
EAGAIN
并不表示错误了,只是表示数据暂时没有准备好,需要稍后再来读取。
如上图所示,EAGAIN
和EWOULDBLOCK
都是宏,而且它们的值都是11,本质上是一个东西。
如上图代码所示,使用本喵写的工具serNonBlock
将标准输入,也就是文件描述符为0的文件设置成非阻塞IO模式,然后运行。
如上图所示,可以看到,输入提示符>>>
在不停打印,在本喵从键盘上输入的时候,仍然在打印,虽然输入的内容被>>>
分隔开了,但是输入完成以后,回显echo
的内容却是完整的,仍然是helloworld
。
- 输入和输出互不影响,因为有输入缓冲区和输出缓冲区,二者是相互独立的。
如上图所示便是默认情况下的阻塞IO,可以看到,输入和输出轮替进行着,在本喵没有输入的情况下,该进程是阻塞等待的。
如上图所示,可以在else
情况,也就是返回值s小于0的时候再进行具体的判断,如果错误码errno
的值是EAGAIN
说明只是数据没有准备好,并不是出错了,在等待数据就绪的过程中可以执行其他任务。
如上图所示,此时进程并没有阻塞,而是在轮询,数据没有准备好就执行其他任务来打发时间。
- 信号驱动IO:内核将数据准备好的时候,使用
SIGIO
信号通知应用程序进行IO操作。
如上图所示信号驱动IO模型,该模式调用recvfrom
并不是在主进程中调用,而且使用signal
注册信号处理函数,在信号处理函数中调用recvfrom
。
进程一直在正常运行,执行自己的逻辑,当内核接收缓冲区有数据到来时,进程会收到系统给发的信号SIGIO
,然后就会调用该信号注册的处理方式,在信号处理方式中调用recvfrom
读取缓冲区中的数据。
- 一旦进入信号处理函数中,说明内核缓冲区中的数据已经准备好了,在这里只需要读取,而不再需要等待。
- IO多路转接:虽然从流程图上看起来和阻塞IO类似,实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态。
如上图IO多路转接模式,该模式中,将IO的等待和拷贝两个步骤分开了。
进程调用select
系统调用来等待内核缓冲区中数据就绪,当数据就绪以后通知进程调用recvfrom
来将数据拷贝到用户缓冲区中。
这样看起来和信号驱动的IO模式没有什么区别,但是多路转接的优势在于可以同时等待多个文件描述符所指向的文件。
- IO多路转接模式下,可以同时等待多个文件,当一个或者多个文件的缓冲区中数据就绪时,就会通知上层用户来读取。
此时虽然也有等,但是等的比重就降低了,因为能够一次等待多个文件,进而让上层用户一次来读取多个缓冲区中的数据,拷贝的比重就增加了,从而提高了IO的效率。
- 异步IO:由内核在数据拷贝完成时,通知应用程序直接去用户缓冲区中使用数据。
- 同步和异步关注的是消息通信机制:
- 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回,但是一旦调用返回,就得到返回值了。
- 也就是由调用者主动等待这个调用的结果。
异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。
- 当一个异步过程调用发出后,调用者不会立刻得到结果,而是在调用发出后,被调用者通过状态、信号等来通知调用者,或通过回调函数处理这个调用。
如上图所示异步IO模式示意图,进程调用aio_read
,将等待数据就绪和将数据拷贝到用户缓冲区两个步骤的工作全部交给操作系统来完成。
当操作系统完成两个步骤以后,通知上层用户直接去用户缓冲区中使用数据即可。
信号驱动是告诉进程可以从内核缓冲区中拷贝数据到用户缓冲区了,而异步IO连拷贝这一步也不用做了,直接使用数据。
- 另外,我们回忆在学习多进程多线程的时候,也提到同步和互斥,这里的同步通信和进程之间的同步是完全不同的概念。
- 进程/线程同步也是进程/线程之间直接的制约关系,是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。尤其是在访问临界资源的时候。
比较这五种IO模式,阻塞IO模式肯定是效率最低的,非阻塞以及信号驱动的IO模式,同样需要参加IO的等待和拷贝两个过程,对于IO的效率是相同的。
异步IO模式也是同样的道理,虽然等待和拷贝不是由线程去做的,而是由操作系统在做,但是线程也得在等待和拷贝完成后才能使用数据,所以IO的效率还是没有提高的。
而多路转接不一样,虽然等待和拷贝两个过程都参与,但是等待时可以一次等待多个文件描述符,拷贝时也是可以拷贝多个文件描述符中的数据。
- 由于等待的文件描述符数量多,所以有数据就绪的概率就高,进行拷贝也更加频繁。
- 站在上帝视角来看,多路转接模式下,进程在单位时间内进行拷贝的次数要比其他几种模式多。
所以多路转接更加高效,而我们研究的重点也在多路转接模式上,主要有select
,poll
,epoll
三种方式。
🍧select
🧁认识接口
如上图所示,该系统调用有5个参数。
- int nfds:要等待的所有文件描述符中的最大
fd+1
。
假设现在要等待的文件有3个,文件描述符分别是3,4,7,则传参时就需要传7+1=8
给nfds。
- fd_set* reads:等待读取就绪文件描述符的位图。
select
等待的文件有不同的事件会就绪,比如读取就绪,写就绪,错误就绪等等。
如上图所示是fd_set
类型在内核中的定义,可以看到它就是一个位图。它有多个比特位,每一个比特位代表一个文件描述符,比特位的状态表示该比特位是否被监听。
如上图所示便是fd_set
位图示意图,其中比特位的顺序由低到高从左向右,比特位的下标就是代表文件描述符fd,比特位的内容表示状态,这张图中,文件描述符为1,3,1023的三个文件描述符需要被select
监视。
如上图所示,使用sizeof
得到fd_set
类型的大小是8字节,所以有1024个比特位,这意味着使用select
最多监视1024个文件描述符。
将接收缓冲区所在文件的文件描述符设置到readfds
中,当该缓冲区有数据到来时(读就绪),select
就会通知上层进程去读取该缓冲区中的数据。
- fd_set* writefds:等待写入就绪文件描述符所在位图。
该位图和readfds
一样,只是操作系统等待的事件由读就绪变成了写就绪,当发送缓冲区空了以后(写就绪),操作系统就会通知上层进程向该缓冲区中写入数据。
- fd_set* exceptfds:等待异常就绪文件描述符所在位图。
打开的文件,如TCP中的套接字,当对端关闭套接字以后,自己这边的套接字就会出现异常,此时操作系统就会通知上层进程处理该异常。
需要操作系统监视哪里事件就将对应的位图传给select
,待事件就绪后就会通知上层进程去处理,如果不需要监视直接设置成nullptr
就行。
- struct timeval* timeout:设置等待方式。
如上图所示struct timeval
类型的定义,有两个成员,第一个是秒,第二个是微秒。
该参数传入nullptr
的时候,select
是阻塞等待,只有当一个或者多个文件描述符就绪时才会通知上层进程去读取数据。
该参数如果是struct timeval timeout = {0, 0}
,时间设置成0,select
是非阻塞等待,就需要使用轮询的方式。
该参数如果设置了具体值,如struct timeval timeout = {5, 0}
,时间设置成5,select
在5秒内阻塞等待,如果在5秒内有文件描述符就绪,则通知上层,如果没有文件描述符就绪则超时返回。
- 返回值:
ret > 0
表示有ret个fd就绪了,ret == 0
表示超时返回,ret < 0
表示select
调用失败了。
为了使用方便,内核还提供了一组宏供用户去设置fd_set
位图,将对应的文件描述符设置进去,避免了我们自己使用按位或等运算给位图赋值的麻烦:
如上图所示,FD_CLR
是将位图中指定文件描述符fd所对应位的状态清零,表示不用操作系统再等待该文件。
FD_ISSET
是用来判断特定文件描述符fd是否被设置进了位图,如果设置返回1,没有则返回0。
FD_SET
是用来将指定文件描述符fd对应的位图设置为1,表示需要操作系统等待该文件。
FD_ZERO
是用来将整个位图清空的,此时操作系统不等待任何一个文件。
select
的五个参数中,后面四个都是输入输出型参数,都是传的指针,意味着操作系统和用户共用一个参数。
调用select
传参时,表示用户告诉内核,需要操作系统等待哪个文件,以及哪种事件。操作系统第一时间会将传入的位图参数清空,每就绪一个文件,传入位图相应比特位置一,然后传返回值给用户。
此时就是内核在告诉用户,你关心的多个fd,有哪些已经就绪了。
用户根据返回值来处理调用结果,如果是ret>0
,则判断自己当初设置进位图中的文件描述符是否被置一了,如果置一了,说明该文件所对应的事件就绪了,用户就可以进行进一步处理。
- 由于传入的参数是输入输出型的,操作系统等待后的结果也是在这个参数中,所以用户必须自己再维护一个数据结构来记录自己最初设置要等待的文件描述符。
- 根据
select
返回状态以及操作系统修改后的位图,与自己维护的记录结构作对比,得出自己当初要等待的文件是否就绪的结论。
无论是读取数据,还是写入数据,再或者是异常事件,都是采用这样的方式和机制。
struct timeval* timeout
表示的阻塞等待时间,操作系统内部也会进行修改,假设初值是{5, 0}
表示阻塞等待5s中,如果5s内没有事件就绪则select
返回0,表示超时返回,此时原本传入的time
的值也变成了{0, 0}
。
如果5s内有事件就绪,假设操作系统等待了3秒,selsect
返回值大于0,表示不是超时返回,有事件就绪,此时原本传入的time
的值也变成了{2, 0}
,表示设置的阻塞事件还剩两秒。
- 所以在轮询过程中,每循环一次就需要重新设置一下时间,否则第一次超时返回后,
time
的值就成了0,之后select
就成了非阻塞IO,与初衷不符。
- 输入输出型参数的作用就是让用户和内核之间相互沟通,互相知晓对方要关心的。
🧁简易select服务器
本喵将创建套接字的代码封装成了一个Sock
类,使用的时候直接调用即可,同样可以将其作为一个小组件。
如上图上,Sock
类中包含套接字的创建,绑定,监听,以及获取新连接等四个方法,这些函数中还包含日志信息,具体的日志代码本喵就不贴了,有兴趣的小伙伴自行去查阅日志功能。
如上图所示便是select
简易服务器的基本配置,成员变量包含监听套接字,端口号,用户维护的用于设置等待位图的数组_fdarry
,以及回调函数。
定义了几个全局默认变量,包括默认端口号,select
所能等待文件的上限1024
,以及用户维护数组的初始默认值。
- initServer():初始化服务器
如上图所示,第一步便是初始化服务器,在系统中创建套接字,进行绑定和监听,构建完整的会话层。之后将用户维护的用来设置等待位图的数组进行初始化,大小是1024个元素,初始值都是-1。
- 0,1,2文件描述符不用
select
等待,从listen
套接字开始进行等待。
- Start():启动服务器
如上图所示,服务器在启动后,是一个while(1)
的死循环,在这个循环中进行一遍又一遍的轮询。
由于select
的后面四个参数都是输入输出型参数,所以每一次轮询之前都需要重新设置一遍,否则就会因为操作系统的修改而出现问题。需要重新设置位图状态,重新设置等待时间等等。
- 每次轮询的时候,都需要将用户维护的数组中需要等待的文件描述符重新设置到
fd_set
位图中。
在将参数设置好以后便调用select
系统调用,用switch
将返回的情况分别处理,当n>0
时,说明有文件就绪,可以去读取,所以调用HandlerReadEvent
函数去处理该事件。
事件处理函数:
如上图所示,当执行到该函数的时候,必有套接字就绪,此时用户要做的就是判断到底是哪个套接字上事件就绪。
第一次执行该函数的时候,必然是监听套接字就绪,因为当前select
等待的只有这一个套接字,所以判断符合条件以后,去执行Accepter
函数来获取新连接。
- 监听套接字就绪也是属于读事件就绪,因为此时有新连接到来,需要用户去将新连接读取走。
如上图所示,当监听套接字就绪后,用户第一时间肯定就是读取监听到的新连接,并且获取客户端的IP地址以及端口号。
新连接获取成功后,由于此时没有客户端的数据到来,所以新连接套接字没有就绪。
- 此时必须将新连接套接字交给
select
去等待就绪,所以需要向用户层维护的数组中添加该套接字的文件描述符。- 如果此时直接读取新连接套接字的话,由于没有就绪,就会阻塞在这里,服务器无法继续执行下去,与我们的初衷就不符合了。
为了方便,本喵还增加了一个Print
函数,打印新增加的需要select
等待的文件描述符。
第一次之后调用Accepter
函数时,已经就绪的文件就不一定是监听套接字了,有可能是前面获取的新连接套接字,所以就需要进行具体判断到底是哪个套接字就绪了。当新连接套接字就绪后,就调用Recver
从套接字中读取客户端的请求。
如上图所示,当新连接套接字就绪以后,首先要进行的就是读取套接字中客户端发来的数据,使用recv
获取数据。
- 当前这种读取数据的方式是存在一定问题的,因为不能保证套接字中的数据是否被读完了。
但是此时并不会产生影响,所以暂且这样,后面本喵会制定具体的协议去处理它。
如果读取数据出现了问题,或者套接字出现异常,那么就需要关闭套接字,并且将文件描述符从用户维护的数组中清除。
读取成功后将请求处理,并且构建响应发送给客户端,当前的select
服务端的重点在于读取,所以写入本喵这里就不详细写了。
- selectServer.cpp
如上图所示就是selectServer.cpp
中的代码,服务器的回调函数中仅是将客户端发送来的数据原封不动的响应给客户端,没有做其他处理,其他部分代码本喵不再解释。
如上图所示,当服务器开始运行后,由于此时没有新连接到来,所以监听套接字一直处于未就绪的状态,前面在设置等待事件的时候设置了5s,所以每隔5s就超时返回一次。
- 可以看到当前最大文件描述符是3,也就是监听套接字的文件描述符。
如上图所示,为了避免超时返回的干扰,本喵将select
的最后一个参数也设置成nullptr
此时select
就是阻塞等待,根据运行结果也可以看成是阻塞不动的。
如上图所示,此时使用两个telnet
连接客户端,可以看到服务端提示有新事件就绪,此时是监听套接字监听到了两个客户端连接,所以显示两次,服务端每处理一次,用户层维护的数组中文件名描述符就会增加一个,所以最终是3,4,5
三个文件需要select
进行等待。
两个客户端向服务端发送消息的时候,服务端显示事件就绪,这是进行数据通信的套接字上有数据到来,提醒用户层去读取数据。
- 服务器上只有一个进程(线程),客户端有多个,此时服务端可以同时接收多个客户端的连接请求和数据。
- 多路转接实现了我们之前只能通过多进程或者多线程才能实现的功能,而且效率非常高。
🧁select的特点
select
能同时等待的文件fd是有上限的,除非重新修改内核,否则无法解决。
fd_set
位图大小只有1024个比特位,意味着select
同时最多只能等待1024个文件描述符。
可以看到,本喵的Linux机器上,最多可以打开100001个文件,远大于1024,所以说select
能够同时等待的文件数量在高访问量的服务器中是远远不够的。
- 必须借助第三方数组等结构来维护需要
select
等待的文件描述符fd。
由于select
的后四个参数都是输入输出型参数,操作系统也会修改这几个参数,这就导致用户层必须自己维护一个数组来记录自己曾经想要让select
等待的文件描述符。
并且每轮询一次就需要重新设置一次fd_set
位图,不仅繁琐,而且对于效率相对较低。
select
存在遍历成本。
在上面本喵实现的服务器代码中,对于用户层来说,存在多处遍历所维护的数组。
在将文件描述符设置到fd_set
位图中的时候,需要遍历数组中的所有元素来找到合法的fd设置到位图中。
在select
完成等待后,同样需要遍历一次数组,来确定是哪个fd的事件就绪了,然后再去处理。
- 内核也要进行遍历
select
的第一个参数传参时传入的是最大文件描述符fd + 1
,这个参数就是为了让内核确定遍历的范围,这个值之前的所有文件描述符,操作系统都会进行查看,看看是否有事件就绪。
- 内核和用户层来回拷贝的成本问题
select
采用的是位图来标记哪个文件的事件就绪,无论是用户层告诉内核,还是内核告诉用户层,都需要拷贝,这样来回进行数据拷贝也是有很大的成本。
🍧总结
多路转接是一个非常重要的IO模式,而select
方式只最基础的一种,它主要的作用是带领我们理解多路转接,后面本喵还会讲解poll
和epoll
方式。
相关文章:

【网络】多路转接——五种IO模型 | select
🐱作者:一只大喵咪1201 🐱专栏:《网络》 🔥格言:你只管努力,剩下的交给时间! 五种IO模型 | select 🍧五种IO模型🍧select🧁认识接口🧁…...

sql顺序倒序查询
要根据 orderNum 字段的顺序查询,你可以使用 SQL 的 ORDER BY 子句。默认情况下,ORDER BY 是按升序排序的,但你可以使用 DESC 关键字来指定降序排序。 以下是一个示例查询,按照 orderNum 字段的顺序将结果返回: SELEC…...

java和sql生成时间维度数据
JAVA: POM依赖: <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.4.2</version></dependency> 代码: package com.kone.kcdp.common;import cn.hutool.co…...
HUT23级训练赛
目录 A - tmn学长的字符串1 B - 帮帮神君先生 C - z学长的猫 D - 这题用来防ak E - 这题考察FFT卷积 F - 这题考察二进制 G - 这题考察高精度 H - 这题考察签到 I - 爱派克斯,启动! J - tmn学长的字符串2 K - 秋奕来买瓜 A - tmn学长的字符串1 思路&#x…...

sm4 加解密算法工具类( Java 版 )
sm4 加解密算法工具类(java) 说明:密钥是 hexString import java.security.Key; import java.security.Security; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec;import cn.hutool.core.codec.Base64Decoder; import…...

Redis项目实战——商户查询缓存
目录 为什么要用Redis实现商户查询缓存?用Redis实现商户查询缓存的基本思路?使用Redis缓存的问题及解决方法?一、如何保持数据库数据和Redis缓存数据的一致性?1 内存淘汰机制2 超时剔除机制3 主动更新机制(胜ÿ…...

重磅OpenAI发布ChatGPT企业版本
8月29日凌晨,Open AI官网发布ChatGPT企业版本! 企业版简介: ChatGPT企业版提供企业级安全和隐私、无限的高速 GPT-4 访问、用于处理更长输入的更长上下文窗口、高级数据分析功能、自定义选项等等。人工智能可以协助和提升我们工作生活的各个…...

# Go学习-Day7
文章目录 断言文件打开/关闭文件读取文件写入文件 命令行参数解析Argsflag包 JSON 个人博客:CSDN博客 断言 type Node struct {x inty int }func main() {var a interface{}var n Node Node{1, 2}a nvar b Nodeb a.(Node)fmt.Println(b) }此处我们有一个结构体…...

uniapp-form表单
<template><view class"ptb-20 plr-30 bg min100"><view class"bg-white radius-20 pd-30"><view class"bold mt-30 mb-50 size-32">选择方式:</view><u--form labelPosition"left" :mod…...

漏洞挖掘-利用
一、文章简介 整合一些web漏洞,以及对漏洞的理解。 二、Web漏洞 1.SQL注入 (1)定义 开发者程序编写过程中,对传入用户数据过滤不严格,将可能存在的攻击载荷拼接到SQL查询语句当中,再将这些查询语句传递到…...

React钩子函数之useDeferredValue的基本使用
在React中,使用钩子函数可以方便地管理组件的状态和副作用。useDeferredValue是React 18中新引入的钩子函数之一,它可以帮助我们优化渲染性能,让组件更加流畅。 useDeferredValue的作用是将一个值延迟更新。这个值可以是状态、属性或其他变量…...

lodash常用方法
cloneDeep 克隆 import { cloneDeep,reduce } from lodash; const b {c:1} const a cloneDeep(b)debounce 防抖 import { debounce } from lodash; debounce(() > {}, 300, { trailing: true })()omit方法删除指定属性,返回一个新的对象 import …...

QByteArray与结构体之间相互转换
Qt项目会碰到自定义结构体和字符数组之间的转换问题,不妨假设结构体名字为custom_struct, 字符数组名字为array_data QByteArray转换为自定义结构体 custom_struct *struct_data reinterpret_cast<custom_struct *>(array_data.data());自定义结构体转换为…...

npm如何安装淘宝镜像
通过命令配置 这种方法是通过修改npm的全局配置文件,将默认的镜像源改为淘宝镜像。具体步骤如下: 打开终端,输入以下命令,设置淘宝镜像源:(windowr) npm config set registry https://registr…...

从项目中突显技能:在面试中讲述你的编程故事
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...

python的观察者模式案例
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言二、具体代码写在结尾 前言 最近写安卓的代码比较多,了解了java代码的注册回调机制,也就是观察者模式,搜索了一下python也有…...

C语言——类型转换
数据有不同的类型,不同类型数据之间进行混合运算时涉及到类型的转换问题。 转换的方法有两种: 自动转换(隐式转换):遵循一定的规则,由编译系统自动完成强制类型转换:把表达式的运算结果强制转换成所需的数据类型 语法格…...

jmeter性能测试入门完整版
1. Jmeter简介 Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件。相比Loadrunner而言,JMeter小巧轻便且免费,逐渐成为了主流的性能测试工具,是每个测试人员都必须要掌握的工具之一。 本文为JMeter性能测试完整入门篇&…...

报错sql_mode=only_full_group_by
首发博客地址 https://blog.zysicyj.top/ 报错内容 ### The error may exist in file[D:\code\cppCode20221025\leader-system\target\classes\mapper\system\TJsonDataMapper.xml] ### The error may involve defaultParameterMap ### The error occurred while…...

伪造 IP 地址的原理和防范措施
在数字化时代,网络安全是至关重要的话题。其中,伪造 IP 地址是一种可能导致网络攻击和欺诈的技术手段。这里将深入探讨伪造 IP 地址的原理以及如何采取措施来防范这种风险。 一.伪造 IP 地址的原理 伪造 IP 地址是一种操纵网络通信的方式,它…...

Linux通过libudev获取挂载路径、监控U盘热拔插事件、U盘文件系统类型
文章目录 获取挂载路径监控U盘热拔插事件libusb 文件系统类型通过挂载点获取挂载路径添libudev加库 获取挂载路径 #include <stdio.h> #include <libudev.h> #include <string.h>int main() {struct udev *udev;struct udev_enumerate *enumerate;struct ud…...

【会议征稿】2023智能通信与网络国际学术会议(ICN 2023)
2023智能通信与网络国际学术会议(ICN 2023) 2023 International Conference on Intelligent Communication and Networking (ICN2023) 2023智能通信与网络国际学术会议(ICN 2023)将于2023年11月10-12日在中国常州召开。ICN 2023…...

Android投屏总结
#android手机投屏 ####导语 至于手机投屏的实现方法可谓五花八门,今天小袁就说下以开发人员的角度来说下当今手机的主流投屏方法。目前这种将终端信号经由WiFi传输到电视、电视盒的技术有三种:DLNA、AirPlay、Miracast、Google Cast。 ##手机投屏智能电…...

vue2 组件组成部分,组件通信,进阶语法
一、学习目标 1.组件的三大组成部分(结构/样式/逻辑) scoped解决样式冲突/data是一个函数 2.组件通信 组件通信语法父传子子传父非父子通信(扩展) 4.进阶语法 v-model原理v-model应用于组件sync修饰符ref和$refs$nextTic…...

信看课堂笔记—LDO和DC-DC电路打PK
LDO(low dropout voltage regulator,低压差线性稳压器)和DC-DC(Direct current-Direct current converter,直流电压转直流电压转换器)电源是非常常见的电源电路,LDO 出来的比较早,像老戏骨一样,…...

C++ Day6
目录 一、菱形继承 1.1 概念 1.2 格式 二、虚继承 2.1 作用 2.2 格式 2.3注意 三、多态 3.1函数重写 3.2 虚函数 3.3 赋值兼容规则 3.4 多态中,函数重写的原理 3.5 虚析构函数 3.5.1 格式 3.6 纯虚函数 3.6.1格式 四、抽象类 五、模板 5.1模板的特…...

分布式系统与微服务的区别是什么?
分布式系统和微服务是两个相关但不同的概念,它们都是在构建复杂的软件应用时使用的架构思想。 分布式系统: 分布式系统是指由多个独立的计算机或服务器通过网络连接共同工作,协同完成一个任务或提供一个服务。在分布式系统中,各个…...

python:用python构建一个物联网平台
要使用Python构建物联网平台,您需要考虑以下步骤: 确定平台的基本要求和功能 首先,您需要明确您将要构建的平台的功能和特点。例如,您可能需要支持多种设备,并使用各种传感器来收集数据。您可能需要实现实时数据可视化…...

基于Qt5开发图形界面——WiringPi调用Linux单板电脑IO
Qt5——WiringPi Qt5WiringPi示例教程 Qt5 Qt是一种跨平台的应用程序开发框架。它被广泛应用于图形用户界面(GUI)开发,可以用于构建桌面应用程序、移动应用程序和嵌入式应用程序。Qt提供了丰富的功能和工具,使开发人员可以快速、高…...

【MySQL】组合查询
目录 一、组合查询 1.创建组合查询 2.union规则 3.包含或取消重复的行 4.对组合查询结果排序 一、组合查询 多数SQL查询都只包含从一个或多个表中返回数据的单条SELECT语句。MySQL也允许执行多个查询(多条SELECT语句),并将结果作为单个查…...