【网络】多路转接——poll | epoll
🐱作者:一只大喵咪1201
🐱专栏:《网络》
🔥格言:你只管努力,剩下的交给时间!
书接上文五种IO模型 | select。
poll | epoll
- 🍧poll
- 🧁认识接口
- 🧁简易poll服务器
- 🧁poll的特点
- 🍧epoll
- 🧁认识接口
- 🧁epoll原理
- 🧁简易epoll服务器
- 🧁epoll的特点
- 🧁epoll的工作方式
- 🍧总结
🍧poll
poll也是一种多路转接的方案,它专门用来解决select的两个问题:
- 等待fd有上限的问题。
- 每次调用都需要重新设置
fd_set的问题。
🧁认识接口

如上图所示便是poll系统调用的声明,它有三个参数。
struct pollfd* fds:用来设置需要等待的fd以及事件

如上图所示,struct pollfd结构体中存在三个成员变量,第一个是fd,表示需要操作系统等待的文件描述符。第二个是short events,表示需要操作系统等待该fd的事件类型。第三个是short revents,操作系统告诉用户层该fd的哪个事件就绪了。
此时的文件描述符fd直接设置到struct pollfd结构中即可,需要设置哪个就设置哪个,不用再去寻找对应的位图。
告诉操作系统需要等待的事件时,只需要直接设置short events即可,不用将不同的事件类型放在不同的位图中。
当指定文件描述符fd的就绪时,操作系统会设置对应short revents,用户层直接读取fds中的这个字段便可知道是哪个事件就绪了。
struct pollfd结构体将用户和操作系统设置的字段分开了,所以就不存在相互干扰的问题。
events和revents的取值:

如上图所示便是用户层以及操作系统可以设置的事件类型,这些同样是一些宏定义,常用的就是POLLIN数据可读,以及POLLOUT数据可写。
假设fds结构体中,events的值是POLLIN,此时操作系统就关注指定文件描述符的读事件是否就绪,如果就绪,就将revents的值也设置成POLLIN,用户层读取到该值后就知道文件可读了。
nfds_t nfds:需要poll等待的文件描述符fd的个数。

如上图所示,在内核中,nfds_t类型本质上是一个unsigned long int类型,也是一个整形。
第二个参数nfds就是用来设定需要poll等待文件描述符的个数的。用户层和操作系统同时维护一个元素为struct pollfd类型的数组,这个数组中有多少个元素,用户层需要让操作系统等待的文件描述符就有多少个,变量nfds就表示数组的大小。
- 这个数组就类似用户层和操作系统之间的“临界资源”,双方都能看到,而且都可以访问,由于访问的位置不同,所以不会出现干扰。
由于nfds的值是由用户层设定的,所以poll可同时等待的文件描述符数量并没有上限,unsigned long int的最大值非常大,远大于一个系统能打开的文件个数,所以可以理解为没有上限。
int timeout:阻塞等待的时间
和select中的struct timeval变量的作用类似,但是这里的timeout是一个int类型的变量,它的单位是1ms。并且它不是一个输入输出型参数,只需要定义一次即可。
timeout>0表示在timout时间以内阻塞等待,超出这个时间就超时返回,如该值是1000就表示阻塞等待1s。
timeout ==0表示非阻塞等待。timeout < 0表示阻塞等待。
- 返回值:就绪事件的个数。
和select的返回值意义一样,本喵就不再解释了。
🧁简易poll服务器
下面本喵用poll实现一下上面select所实现的服务器,大部分代码都一样,本喵仅讲解不一样的部分:

如上图所示,poll服务器中,成员变量只是将原本的int* _fdarry类型数组变成了struct pollfd* _rfds类型的数组,其他成员保持不变。
- initServer():初始化服务器

如上图所示,初始化服务器中,在堆区开辟的数组变成了struct pollfd类型的数组,大小是由户自定义的。
在初始化这个数组的时候,需要初始化struct pollfd中的三个字段,其中fd仍然是-1,revents和events都是0,本喵将初始化字段放在了一个函数中。
同样需要将数组中第一个需要等待的文件设置成监听套接字,事件设置成POLLIN,表示需要操作系统等待监听套接字中的读事件。
- Start():启动服务器

如上图所示,可以看到此时Start函数比之前简洁了许多,因为不用每次轮询的时候都重新设置一遍fd_set了,直接调用poll即可,然后根据返回值做具体的处理。
同时阻塞事件也不用再重新设置了,因为并不是一个输入输出型参数,操作系统并不会改变timeout只需要设置一次即可。
HandlerReadEvent():处理事件函数。

如上图所示,当事件就绪时,仍然需要调用HandlerReadEvent函数来处理事件,而且仍然需要遍历数组来判断具体是哪个文件描述符的哪个事件就绪了,然后再看是调用Accept还是Recver。
- 此时遍历判断的时候,判断的是
struct pollfd中的fd字段,之前所有判断fd的语句中,都需要变成_rfds[i].fd。- 在判断是否是
POLLIN事件就绪时,需要将revents的值与POLLIN进行按位与,如果结果大于0则说明该事件就绪了。
其他部分代码本喵就不讲解了,因为和select中的代码一样,只是遍历判断时由直接的_fdarry[i]变成了_rfds[i].fd,包括pollServer.cpp中的代码都不用作任何修改。

如上图,服务器运行起来后,由于没有新连接到来,所以监听套接字每隔5s就超时返回一次。
同样为了避免干扰,将poll设置成阻塞等待方式:

如上图所示,将timeout的值设置成-1,表示让poll阻塞等待,可以看到,运行起来后,由于没有新连接到来,程序阻塞不动了。

如上图所示,使用两个telnet客户端连接客户端后,现象和selsect一样,也是一个服务端进程可以同时和两个客户端进行通信。
🧁poll的特点
优点:
struct pollfd结构包含了要监视的event和发生的revent,不再使用select“参数-值”传递的方式,接口使用比select更方便。poll并没有最大等待文件描述符数量限制 (但是数量过大后性能也是会下降)。
缺点:
- 和
select一样,poll返回后,需要轮询struct pollfd数组来获取就绪的描述符。 - 每次调用
poll都需要把大量的struct pollfd结构从用户层拷贝到内核中。 - 同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长, 其效率也会线性下降。
🍧epoll
epoll是基于poll的基础上改进的,它不仅克服了select的缺点,而且解决了poll遍历成本,是效率最高的多路转接模式,但是它也是最复杂的一种模式。
🧁认识接口
epoll_create

如上图所示的epoll_create系统调用是用来创建epoll句柄的。
int size:自Linux2.6.8以后,该参数是被忽略的,不起实际作用,但是必须是大于0的一个值。- 返回值:返回的也是一个文件描述符fd。
epoll句柄在内核中也是一个结构体,类似于struct file,而Linux下一切皆文件,所以返回的也是一个文件描述符,拿着这个文件描述符可以访问到这个epoll句柄。
epoll_ctl

如上图所示的epoll_ctl系统调用是用来修改创建的epoll句柄属性的。
-
int epfd:该值就是epoll_create的返回值,用来指示哪个epoll句柄。 -
int op:是修改句柄属性的选项,有增,删,改三个选项: -
EPOLL_CTL_ADD:向句柄中增加要等待的文件描述符。 -
EPOLL_CTL_MOD:修改句柄中指定的文件描述符。 -
EPOLL_CTL_DEL:从句柄中删除指定的文件描述符。 -
int fd:要进行操作的文件描述符。 -
struct epoll_event* event:用来指定要等待的事件。

如上图所示便是内核中struct epoll_event结构体的定义,它有两个成员变量。
第一个成员是uint32_t events,用来设置需要等待的事件,其值也是有几个宏组成的集合:
| 值 | 意义 |
|---|---|
| EPOLLIN | 表示对应的文件描述符可以读 (包括对端SOCKET正常关闭) |
| EPOLLOUT | 表示对应的文件描述符可以写 |
| EPOLLPRI | 表示对应的文件描述符有紧急的数据可读 (这里应该表示有带外数据到来) |
| EPOLLERR | 表示对应的文件描述符发生错误 |
| EPOLLHUP | 表示对应的文件描述符被挂断 |
| EPOLLET | 将EPOLL设为边缘触发(Edge Triggered)模式, 这是相对于水平触发(Level Triggered)来说的 |
| EPOLLONESHOT | 只监听一次事件, 当监听完这次事件之后, 如果还需要继续监听这个socket的话, 需要再次把这个socket加入到EPOLL队列里 |
第二个参数是一个联合体epoll_data_t data,可以看到有四个成员共用这个联合体,后面本喵会讲解它每个变量的妙用。
- 返回值:调用成功返回0,调用失败返回-1,并且设置相应的错误码。
epoll_wait

如上图所示的epoll_wait系统调用是用来从操作系统中获取被等待文件描述符的状态的。第一个参数不做解释。
-
struct epoll_event* events:是一个数组,操作系统将就绪的文件描述符放入这个数组中供用户层读取。 -
int maxevents:该值就是events数组的大小,是用户层用来告诉内核这个数组有多大的,这个值不能大于epoll_create时的size。 -
int timeout:和poll中是一样的,不再解释。 -
返回值: 也是和
poll的返回值以及select代表的意义一样,大于0表示就绪的文件描述符个数,等于0表示超时返回,小于0表示调用失败。
以上三个系统调用是epoll模型的核心调用,epoll_ctl是用户层用来告诉内核自己的需求的,epoll_wait是内核用来告诉用户层哪些文件描述符的什么事件就绪的。
现在知道了接口的使用,但是仍然并不清除为什么epoll模型能够解决poll和select存在的问题,所以我们需要大概知道epoll模型的底层原理。
🧁epoll原理
网络通信过程中,接收端将数据从网卡(硬件层)开始逐层向上交付,最后给到应用层,那么接收端是如何知道网卡上有数据到来的?也就是操作系统是怎么感知到数据来了呢?

如上图所示,本喵将计算机体系结构,冯诺依曼体系结构,以及中断向量表放在了一起来讲解。
当网卡接收到数据后,输入外设(网卡)会自己产生一个控制信号直接给CPU中的控制器,表示此时网卡中有数据到来,可以读了。
- 冯诺依曼体系中,外设的数据信号不能直接和CPU传递,如上图中红色线条,必须经过存储器。
- 外设的控制信号可以直接传递给CPU的控制器,如上图黑色线条。
- 外设给CPU发送一个信号表示数据到来,这叫做中断事件发生。
CPU根据中断信号的编号,去操作系统维护的中断向量表中找到对应的中断服务函数并且执行。
中断服务函数中会调用网卡接收数据的驱动程序,将数据读取并且向上层交付,如上图绿色线条所示。
- 在这里要重点关注中断服务函数,从网卡中接收数据是从它开始的。
epoll是一个模型,这个模型包含多个数据结构,而前面所讲的句柄,可以理解为是这个模型标志,通过句柄可以找到这个模型,并且使用它。

如上图所示是整个epoll模型理论图,包含计算机体系结构中的驱动层,操作系统,系统调用三层。
在调用epoll_create创建模型后,会返回一个文件描述符fd,这个fd同样放在服务器进程PCB所维护的进程描述符表中,通过fd这个句柄就可以找到对应的epoll模型。
- epoll模型同样是一个大的结构体,只是这个结构体更加复杂,在Linux眼中,都是
struct file,所以创建模型后返回的也是一个文件描述符。
上图中操作系统中黑色框内的部分就是epoll模型,包含一个红黑树和一个就绪队列。
以增加需要操作系统等待的文件描述符为例,调用epoll_ctl,将fd以及需要等待的事件构建成struct epoll_event变量插入到红黑树中,操作系统会遍历红黑树中所有节点。
- 红黑树节点中包含很多成员变量,如上图左下角所示,这其中必然有文件描述符fd,需要等待的事件
event,左右字节的指针。- 还包括
next和prev指针。
如果是删除或者修改等操作,同样是在修改这颗红黑树,而红黑树查找效率非常高,所以对应的操作也会很高效。
当操作系统发现红黑树中有节点的事件就绪后,就会将该节点放入到就绪队列中,就绪队列是一个双向循环链表。
将节点从红黑树放入到就绪队列中并没有发生拷贝,秘密就在next和prev指针上。当网卡中有数据到来时,通过中断函数最终调用了网卡驱动程序,在驱动程序中有一个回调函数void* private_data,这是由操作系统提供的。
private_data回调函数会将红黑树节点中的next和prev指针的指向关系做对应的修改,让该节点链入到就绪队列中去。
- 红黑树的一个节点,它不只属于红黑树,还可能属于就绪队列。
- 如上图所示,红黑树中的节点和就绪队列中的节点地址都是
0x11223344。
本喵画的是逻辑图,所以将就绪队列和红黑树分开了。
就绪队列中必然也包括就绪文件的文件描述符,以及就绪的事件,如上图所示的struct epoll_event结构。
- 所以,凡是处于就绪队列中的节点必然已经就绪。
用户层在调用epoll_wait后,获取的就是内核中就绪队列中的内容,所以获取到的全部都是就绪的事件,所以用户层的struct epoll_event类型数组中,全部都是就绪的事件。
epoll_wait将所有就绪的事件,按照顺序放入到用户层传入的数组中。
此时从内核到用户层虽然也需要遍历,但是此时是遍历拷贝,而不需要遍历检测,所以时间复杂度相当于从之前的O(N)变成了O(1),效率提升的不是一点半点。
🧁简易epoll服务器
和poll服务器一样,本喵仅讲解不一样的地方,其他和select服务器中有详细讲解。

如上图所示是epoll服务器类的基本组成,相比poll服务器有三个变化的成员变量,_epfd是epoll模型句柄文件描述符,_revs是用户层获取就绪文件描述符的数组,_num是该数组的大小。
还包含几个全局的默认值,size是调用epoll_create是的参数,要大于0并且大于用户层数组的大小。defaultnum是用户层数组的默认大小。
- initServer: 初始化服务器

如上图所示初始化服务器代码,创建epoll模型,并且将监听套接字的读事件让操作系统去等待,加入到红黑树中,最后就是在堆区开辟获取就绪事件所用的数组。
在设置struct epoll_event结构体变量的时候,暂时先给data联合体的fd赋值,虽然epoll_ctl的第三个参数才是真正指定要等待的文件描述符。
由于用户层在设置的时候设置的是联合体的fd字段,所以当该文件的事件就绪时,就绪队列中该文件的联合体中仍然是fd字段。
- Start: 启动服务器

如上图代码所示,Start成员函数的实现更加简单,仅仅是调用了epoll_wait函数,当有事件就绪时,调用HandlerReadEvent时需要传入就绪事件的个数,方便遍历拷贝。
HandlerReadEvent

如上图所示,可以看到,此时遍历拷贝的元素全部是已经就绪的事件,而不需要再挨个检测,所以效率非常高。
Accept

如上图代码所示,监听到的新连接同样需要交给操作系统去等待,所以需要将新连接的文件描述符添加到红黑树中。
Recver

如上图所示,接收数据的代码和之前是一模一样的,不同的是,当新连接出现异常关闭后,需要将新连接的文件描述符从红黑树中删除,让操作系统不用再等待该文件描述符了。
epollServer.cpp源文件同样不需要做修改,直接用之前的就可以。

如上图运行结果所示,epoll模型创建好以后,句柄的值是4,监听套接字的文件描述符是3,两个telnet客户端的连接文件描述符是5和6,足以说明该句柄就是一个文件描述符,其他效果和之前的一样。
🧁epoll的特点
- 接口使用方便:虽然拆分成了三个函数,但是反而使用起来更方便高效,不需要每次循环都设置关注的文件描述符,也做到了输入输出参数分离开。
- 数据拷贝轻量:只在合适的时候调用
epoll_ctl将文件描述符结构拷贝到内核中,这个操作并不频繁(而select/poll是每次循环都要进行拷贝)。 - 事件回调机制:避免使用遍历检测,而是使用回调函数的方式,将就绪的文件描述符结构加入到就绪队列中。
epoll_wait返回直接访问就绪队列就知道哪些文件描述符就绪,这个操作时间复杂度是O(1),即使文件描述符数目很多, 效率也不会受到影响。 - 没有数量限制:文件描述符数目无上限。
虽然epoll的机制更复杂,但是它用起来更方便也更高效。
🧁epoll的工作方式
epoll主要解决的是多路转接中,进行IO的时候等的这一环节,当操作系统所监管的事件就绪了,就会通知用户层来处理事件,这个通知有两种方式:
- 水平触发(Level Triggered)工作模式:简称LT。
- 边缘触发(Edge Triggered)工作模式:简称ET。
来举一个生活中的例子,假设你正在打王者荣耀,正要推对方水晶的时候,你妈喊你吃饭,此时就存在两种方式:
- 如果喊你一次你没动,那么就会继续喊第二次,第三次…,直到你去吃饭,这种方式就是水平触发。
- 如果喊你一次你没动,之后就不再喊你了,这种方式就是边沿触发。
放在多路转接中就是,事件就绪时,操作系统通知用户层后,用户层没有读取数据或者没有读取完毕,如果操作系统继续通知就是LT模式,如果没有继续通知就是ET模式。
epoll默认状态下就是LT工作模式。- LT模式下,事件未被用户层处理完毕,每调用一次
epoll_wait就会返回一个大于0的值。- ET模式下,事件未被用户处理完毕,只有第一次调用
epoll_wait才会返回大于0的值,之后不再返回,并且将事件设置为未就绪状态,除非该套接字中数据增加,才会再返回一次大于0的值。
在调用epoll_ctl的时候,将struct epoll_event中的uint32_t events字段设置成EPOLLET,此时该文件描述符就变成了ET模式,并没有设置LT模式的方法,因为默认就是LT模式。
使用ET模式能够减少epoll触发的次数,但是代价就是强逼着程序猿一次响应就绪过程中就把所有的数据都处理完,如果不处理完,剩下的数据就有可能被覆盖,后果由程序猿自己承担。
相当于一个文件描述符就绪之后,不会反复被提示就绪,所以就比 LT 更高效一些。
ET模式的高效是建立在程序员的痛苦之上的,由于它只通知用户层一次,如果不一次处理完数据就没机会再处理了,但是,用户层是怎么知道数据有没有读取完毕呢?
- 答案是:循环读取,直到读不到数据了,就证明读完了。

如上图所示,此时就存在一个问题,客户端发送了10K的数据给服务端,服务端收到了epoll的通知后,用户层调用recv进行读取,但是一次没有读取完毕,只读取了1K的数据。
由于此时epoll是ET模式,所以操作系统认为事件已经被处理了,就又将读事件设置成了未就绪的状态,再次读取时recv就会阻塞不动,整个进程就阻塞了,如下面伪代码:
while(1)
{int ret = recv(sock,buffer,sizeof(buffer)-1,0);//第二次读取就会阻塞
}
由于epoll_wait不会再次返回,剩下的9K数据会一直在缓冲区中,直到下一次客户端再给服务器写数据,操作系统再次将读事件设置成就绪状态,才能再次recv。
- 服务端只有将10k数据完全读取完,才会给客户端一个确认应答。
- 客户端收到服务端的确认应答后才会发送下一个请求。
- 客户端发送下一个请求,
epoll_wait才会返回,才会将读事件设置未继续,服务端才能再次去缓冲区中读取。
服务端无法读取剩余的数据,也就不会发出响应,客户端无法收到响应,也就不会再次发送请求,服务端无法收到再次的请求,就无法再次读取缓冲区中剩余的数据。
时间一长,就会触发TCP的超时重传机制,导致数据被覆盖甚至丢失等问题。
- 为了解决这个问题,文件描述符对应的缓冲区必须设置成非阻塞 IO方式,本喵在上篇文章中讲解过,使用
fcntl设置。- 只有非阻塞方式,才能用轮询的方式不断读取缓冲区中的数据,直到读取完毕。
如果是LT模式就不用设置成非阻塞模式,因为数据没有读取完毕,epoll_wait会持续返回,而事件也被保持就绪状态,recv就可以持续读取数据,直到将数据读取完毕。
select和poll是采用LT模式的,和epoll的默认方式一样,那么如果将文件描述符设置成非阻塞方式,仍然使用LT模式不是更方便吗?既能循环读取,又能让epoll持续返回,也能提高效率啊,为什么仍然要多此一举设计一个ET模式呢?
- ET模式的高效不仅仅体现在通知机制上,减少通知次数,降低系统调用的开销。
- ET模式的高效还体现在增加底层网络的吞吐量上。
ET模式表面上看是在倒逼程序员将本轮就绪的数据全部读走,深入网络底层TCP协议去看,服务端由于一次将数据全部读走了,从而能给客户端应答一个更大的窗口值。
客户端就能更新出一个更大的滑动窗口,增加一次发送的数据量,从而提高底层数据发送的效率,更好的利用诸如TCP延迟应答等策略,提高整个网络通信的吞吐量。
- 所以说,ET模式在压榨程序员的基础上,提高了整个网络通信的效率。
🍧总结
虽然介绍了poll和epoll两种方式,但是epoll不仅解决了poll方式的问题,而且还带来了其他优势,比如使用简单,遍历成本低等优势,以及ET模式对于通信效率的提升,虽然epoll的机制更复杂,但是它带来了更好的效果,利远大于弊。
epoll的高性能是有一定的特定场景的,如果场景选择的不适宜,epoll的性能可能适得其反。
- 对于多连接,且多连接中只有一部分连接比较活跃时,比较适合使用epoll。
例如,一个需要处理上万个客户端的服务器,例如各种互联网APP的入口服务器,这样的服务器就很适合epoll。
如果只是系统内部,服务器和服务器之间进行通信,只有少数的几个连接,这种情况下用epoll就并不合适,具体要根据需求和场景特点来决定使用哪种模型。
相关文章:
【网络】多路转接——poll | epoll
🐱作者:一只大喵咪1201 🐱专栏:《网络》 🔥格言:你只管努力,剩下的交给时间! 书接上文五种IO模型 | select。 poll | epoll 🍧poll🧁认识接口🧁简…...
音视频 ffmpeg命令视频录制(Windows)
先安装dshow软件 Screen Capturer Recorder, 项目地址:https://sourceforge.net/projects/screencapturer/files/ 然后查看可用设备名字:ffmpeg -list_devices true -f dshow -i dummy [dshow 0509d6c0] DirectShow video devices (some ma…...
【拾枝杂谈】从游戏开发的角度来谈谈原神4.0更新
君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello,米娜桑们,这里是君兮_,结合最近的学习内容和以后自己的目标,今天又开了杂谈这个新坑,分享一下我在学习游戏开发的成长和自己的游戏理解,当然现在还是一枚…...
QT设置mainwindow的窗口title
QT设置mainwindow的窗口title 在QT程序中,通常会有**aaaa-[bbbbbbb]**这种形式的title,对于刚上手qt的程序员同学,可能会简单的以为修改这种title,就是使用setWindowTitle这个接口,其实只对了一半,这种形式…...
SaaS多租户系统架构设计
前言:多租户是SaaS(Software-as-a-Service)下的一个概念,意思为软件即服务,即通过网络提供软件服务。SaaS平台供应商将应用软件统一部署在自己的服务器上,客户可以根据工作的实际需求,通过互联网…...
Java自定义捕获异常
需求分析 ElectricalCustomerVO electricalCustomerVO new ElectricalCustomerVO(); electricalCustomerVO.setElcNumber(chatRecordsLog.getDeviceNumber()); List<ElectricalCustomerVO> electricalCustomerlist electricalCustomerMapper.selectElectricalCustomer…...
力扣--数组类题目27. 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 示例 1: 输入:nums [3,2,2,3], val 3 输出:2, nums [2,2] 解释:函数应该返回新的长度 2, 并且 n…...
实际并行workers数量不等于postgresql.conf中设置的max_parallel_workers_per_gather数量
1 前言 本文件的源码来自PostgreSQL 14.5,其它版本略有不同 PostgreSQL的并行workers是由compute_parallel_worker函数决定的,compute_parallel_worker是估算扫描所需的并行工作线程数,并不是您在postgresql.conf中设置的max_parallel_work…...
java定位问题工具
一、使用 JDK 自带工具查看 JVM 情况 在我的机器上运行 ls 命令,可以看到 JDK 8 提供了非常多的工具或程序: 接下来,我会与你介绍些常用的监控工具。你也可以先通过下面这张图了解下各种工具的基本作用: 为了测试这些工具&#x…...
【Java】基础入门 (十六)--- 异常
1.异常 1.1 异常概述 异常是指程序在运行过程中出现的非正常的情况,如用户输入错误、除数为零、文件不存在、数组下标越界等。由于异常情况再程序运行过程中是难以避免的,一个良好的应用程序除了满足基本功能要求外,还应具备预见并处理可能发…...
[javaWeb]Socket网络编程
网络编程:写一个应用程序,让这个程序可以使用网络通信。这里就需要调用传输层提供的 api。 Socket套接字 传输层提供协议,主要是两个: UDP和TCP 提供了两套不同的 api,这api也叫做socket api。 UDP和 TCP 特点对比: UDP: 无连…...
<MySon car=“宝马“ :money=“money“></MySon>有没有冒号
为什么car"宝马"没有: 但是 :money"money"就有: <script setup> import {ref} from vue import MySon from /components/MySon.vueconst money ref(100) </script><template><h3>father</h3><My…...
netty(三):NIO——多线程优化
NIO多线程优化 使用Boss线程来处理accepct事件使用Worker线程来处理读写事件,可以创建多个worker线程 package com.review;import lombok.extern.slf4j.Slf4j;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.*; impor…...
Linux操作系统--linux概述
1.Linux概述 Linux,全称GNU/Linux,是一种免费使用和自由传播的类UNIX操作系统(OS)。简单的说就是一种操作系统。在日常中常见的操作系统有一下三种: 2.linux起源和背景 (1).linux的诞生 linux操作系统是由李纳斯托瓦兹…...
数组中出现次数超过一半的数字
⭐️ 题目描述 🌟 OJ链接:数组中出现次数超过一半的数字 思路: 采用投票计数的方式,我们可以把每个数字都看成一次投票并且计数,那么最后剩下来的就是数组中数字出现次数最多的那一个。比如 { 1,2,3,2,2,2,5,4,2 } &a…...
网络优化工程师,你真的了解吗?
一、5G网络优化工程师到底是什么? 5G,就是我们通常所说的第五代移动通信标准,属于目前最热门的新技术趋势。随着2019年5G技术进入正式的商用阶段,拥有广阔的发展前景,备受瞩目。“5G工程师”这个词是一个概念词&#x…...
git 的常用命令
git是一个版本管理器,是程序员必备工具之一,其主分为三个区: 工作区: 暂存区: 仓库: 通过保持软件版本,分支,合并,等多种版本操作,使软件能在自己想要的版本…...
linux如何拷贝文件,删除多余的一级目录,用*号代替所有文件
加上*,代表目录下的所有文件 mv /home/user/dir1/dir1/* /home/user/dir1/可以使用mv命令的通配符来去掉一层目录。 例如,假设有一个名为/home/user/dir1/dir2/file.txt的文件,要将它移动到/home/user/dir2/目录下并去掉dir1目录࿰…...
springboot使用properties
一、方式1: 1.1.配置类: package cn.zyq.stater.config;import cn.zyq.stater.bean.User4; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework…...
Android中获取手机SIM卡的各种信息
通过以下工具类方法可以获取到手机SIM的各种信息数据!!! package com.utils; import android.telephony.TelephonyManager; import com.baidu.platform.comapi.map.E; import org.json.JSONArray; import org.json.JSONObject; import java.…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

