I/O多路复用:poll与epoll
一、select/poll与epoll对比
核心区别
特性 | select/poll | epoll |
---|---|---|
内核数据结构 | 数组(线性结构) | 红黑树(存储监听的fd) |
内存拷贝 | 每次调用需将fd列表从用户态拷贝到内核态 | 仅在注册fd时拷贝一次(epoll_ctl) |
内核事件检测 | 轮询所有fd(O(n)) | 回调机制(事件触发时主动通知,O(1)) |
用户态处理 | 遍历所有fd查找就绪事件(O(n)) | 直接获取就绪事件列表(O(1)) |
最大连接数 | select受FD_SETSIZE限制(默认1024) | 无限制(受系统资源限制) |
触发模式 | 仅水平触发(LT) | 支持水平触发(LT)和边缘触发(ET) |
触发模式详解
-
水平触发(LT,默认)
- 只要fd就绪(如可读),就会一直触发事件(类似“持续提醒”)。
- 适用于数据分批到达的场景,可多次读取数据。
-
边缘触发(ET)
- 仅在fd状态变化时触发一次事件(如从不可读变为可读)。
- 必须一次性读完所有数据,否则可能丢失后续事件。
- 需配合非阻塞I/O使用(如设置fd为O_NONBLOCK)。
二、poll服务器代码示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>#define MAX_FDS 10 // 最大监听fd数
#define SERVER_PORT 6000// 初始化pollfd数组
void init_pollfd(struct pollfd fds[]) {for (int i = 0; i < MAX_FDS; i++) {fds[i].fd = -1; // 标记未使用的fdfds[i].events = 0; // 初始无监听事件fds[i].revents = 0; // 初始无就绪事件}
}// 向pollfd数组添加新fd
void add_fd(struct pollfd fds[], int fd) {for (int i = 0; i < MAX_FDS; i++) {if (fds[i].fd == -1) {fds[i].fd = fd;fds[i].events = POLLIN; // 监听读事件break;}}
}// 从pollfd数组删除fd
void del_fd(struct pollfd fds[], int fd) {for (int i = 0; i < MAX_FDS; i++) {if (fds[i].fd == fd) {fds[i].fd = -1;fds[i].events = 0;fds[i].revents = 0;break;}}
}// 处理客户端连接
void handle_accept(int listen_fd, struct pollfd fds[]) {int client_fd = accept(listen_fd, NULL, NULL);if (client_fd == -1) {perror("accept failed");return;}printf("Client %d connected\n", client_fd);add_fd(fds, client_fd); // 将客户端fd加入poll监听
}// 处理客户端数据接收
void handle_recv(int client_fd, struct pollfd fds[]) {char buf[128] = {0};int n = recv(client_fd, buf, sizeof(buf), 0);if (n < 0) {perror("recv failed");close(client_fd);del_fd(fds, client_fd);return;}if (n == 0) { // 客户端关闭连接printf("Client %d disconnected\n", client_fd);close(client_fd);del_fd(fds, client_fd);return;}printf("Received from %d: %s\n", client_fd, buf);send(client_fd, "OK", 2, 0); // 简单响应
}// 初始化服务器套接字
int init_server() {int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) {perror("socket failed");return -1;}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");close(listen_fd);return -1;}if (listen(listen_fd, 5) == -1) {perror("listen failed");close(listen_fd);return -1;}return listen_fd;
}int main() {int listen_fd = init_server();if (listen_fd == -1) exit(1);struct pollfd fds[MAX_FDS];init_pollfd(fds);add_fd(fds, listen_fd); // 监听服务器套接字while (1) {int n = poll(fds, MAX_FDS, 5000); // 超时5秒if (n < 0) {perror("poll failed");continue;} else if (n == 0) {printf("Poll timeout\n");continue;}// 遍历所有fd,处理就绪事件for (int i = 0; i < MAX_FDS; i++) {if (fds[i].fd == -1) continue; // 跳过未使用的fdif (fds[i].revents & POLLIN) { // 读事件就绪if (fds[i].fd == listen_fd) {handle_accept(listen_fd, fds); // 处理新连接} else {handle_recv(fds[i].fd, fds); // 处理客户端数据}}}}close(listen_fd);return 0;
}
三、epoll服务器代码示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>#define MAX_EVENTS 10 // 最大就绪事件数
#define SERVER_PORT 6000// 向epoll实例添加fd
void add_epoll_event(int epfd, int fd, int events) {struct epoll_event event;event.data.fd = fd;event.events = events;if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event) == -1) {perror("epoll_ctl add failed");}
}// 从epoll实例删除fd
void del_epoll_event(int epfd, int fd) {if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) == -1) {perror("epoll_ctl del failed");}
}// 处理客户端连接
void handle_accept(int listen_fd, int epfd) {int client_fd = accept(listen_fd, NULL, NULL);if (client_fd == -1) {perror("accept failed");return;}printf("Client %d connected\n", client_fd);// 设置为非阻塞模式(配合ET模式使用)// int flags = fcntl(client_fd, F_GETFL);// fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);add_epoll_event(epfd, client_fd, EPOLLIN); // 监听读事件
}// 处理客户端数据接收
void handle_recv(int client_fd, int epfd) {char buf[128] = {0};int n = recv(client_fd, buf, sizeof(buf), 0);if (n < 0) {perror("recv failed");close(client_fd);del_epoll_event(epfd, client_fd);return;}if (n == 0) { // 客户端关闭连接printf("Client %d disconnected\n", client_fd);close(client_fd);del_epoll_event(epfd, client_fd);return;}printf("Received from %d: %s\n", client_fd, buf);send(client_fd, "OK", 2, 0); // 简单响应
}// 初始化服务器套接字
int init_server() {int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) {perror("socket failed");return -1;}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");close(listen_fd);return -1;}if (listen(listen_fd, 5) == -1) {perror("listen failed");close(listen_fd);return -1;}return listen_fd;
}int main() {int listen_fd = init_server();if (listen_fd == -1) exit(1);int epfd = epoll_create(1); // 参数无实际意义,仅需>0if (epfd == -1) {perror("epoll_create failed");exit(1);}add_epoll_event(epfd, listen_fd, EPOLLIN); // 监听服务器套接字struct epoll_event events[MAX_EVENTS];while (1) {int n = epoll_wait(epfd, events, MAX_EVENTS, 5000); // 超时5秒if (n < 0) {perror("epoll_wait failed");continue;} else if (n == 0) {printf("Epoll timeout\n");continue;}// 处理就绪事件for (int i = 0; i < n; i++) {if (events[i].data.fd == listen_fd) {handle_accept(listen_fd, epfd); // 处理新连接} else {handle_recv(events[i].data.fd, epfd); // 处理客户端数据}}}close(listen_fd);close(epfd);return 0;
}
四、epoll核心系统调用详解
1. epoll_create
int epoll_create(int size); // Linux 2.6.8后size被忽略,传入>0即可
- 作用:创建epoll实例,返回文件描述符
epfd
。 - 注意:需调用
close(epfd)
释放资源。
2. epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
op
取值:EPOLL_CTL_ADD
:注册fdEPOLL_CTL_MOD
:修改事件EPOLL_CTL_DEL
:删除fd(event可为NULL)
event.events
常用值:EPOLLIN
:可读事件EPOLLOUT
:可写事件EPOLLET
:边缘触发模式(默认水平触发)
3. epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
- 返回值:
>0
:就绪事件数0
:超时-1
:错误(如被信号中断)
timeout
:-1
:永久阻塞0
:立即返回>0
:超时时间(毫秒)
五、内核实现原理
-
红黑树存储监听fd:
- 内核使用红黑树管理所有注册的fd,插入/删除/查询时间复杂度均为O(log n)。
-
事件驱动机制:
- 每个fd关联一个回调函数,当fd状态变化(如可读)时,内核主动将事件加入就绪队列。
epoll_wait
直接从就绪队列获取事件,无需轮询所有fd。
-
内存优化:
- 仅在
epoll_ctl
时拷贝fd和事件到内核,后续epoll_wait
无需重复拷贝。
- 仅在
六、适用场景
- 高并发场景:处理上万级连接时性能优势显著。
- 长连接管理:适合连接活跃但数据交互较少的场景(如IM、聊天服务器)。
- 边缘触发模式:需配合非阻塞I/O,适用于数据一次性读取的场景(如日志服务器)。
以下是关于设置非阻塞模式并配合**边缘触发(ET)**模式的详细补充笔记,包含原理、代码示例和注意事项:
七、ET模式与非阻塞I/O的绑定关系
为什么ET模式必须搭配非阻塞I/O?
-
ET模式特性:
- 仅在文件描述符状态变化时触发一次事件(如从不可读→可读)。
- 若未一次性读完数据,后续数据到达时不会再次触发事件(除非再次发生状态变化)。
-
阻塞I/O的风险:
- 若使用阻塞I/O,
read()
/write()
可能因数据未完全到达而阻塞,导致:- 错过后续事件(ET模式仅触发一次)。
- 阻塞整个事件循环,影响其他连接。
- 若使用阻塞I/O,
-
非阻塞I/O的作用:
- 通过
O_NONBLOCK
标志使read()
/write()
立即返回,允许循环读取/写入直至数据处理完毕(通过EAGAIN
错误判断)。 - 确保在ET模式下一次性处理完所有就绪数据,避免事件丢失。
- 通过
八、设置非阻塞模式的方法
1. 使用fcntl函数
#include <fcntl.h>// 将fd设置为非阻塞模式
int set_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL); // 获取当前文件状态标志if (flags == -1) {perror("fcntl F_GETFL failed");return -1;}flags |= O_NONBLOCK; // 添加非阻塞标志if (fcntl(fd, F_SETFL, flags) == -1) {perror("fcntl F_SETFL failed");return -1;}return 0;
}
2. 调用时机
- 在通过
epoll_ctl
注册fd时设置(如客户端连接建立后)。 - 示例代码(在
handle_accept
中设置):void handle_accept(int listen_fd, int epfd) {int client_fd = accept(listen_fd, NULL, NULL);if (client_fd == -1) {perror("accept failed");return;}printf("Client %d connected\n", client_fd);set_nonblocking(client_fd); // 设置非阻塞模式add_epoll_event(epfd, client_fd, EPOLLIN | EPOLLET); // 注册ET模式 }
九、ET模式下的数据读写处理
1. 读取数据(以EPOLLIN为例)
void handle_recv(int client_fd, int epfd) {char buf[128];ssize_t n;// 循环读取直至EAGAIN(非阻塞I/O返回-1且errno=EAGAIN)while ((n = recv(client_fd, buf, sizeof(buf), 0)) > 0) {printf("Received from %d: %s\n", client_fd, buf);memset(buf, 0, sizeof(buf)); // 清空缓冲区}if (n == -1) {if (errno == EAGAIN) { // 数据读完,正常退出send(client_fd, "OK", 2, 0); // 响应客户端return;}perror("recv failed");close(client_fd);del_epoll_event(epfd, client_fd);return;}if (n == 0) { // 客户端关闭连接printf("Client %d disconnected\n", client_fd);close(client_fd);del_epoll_event(epfd, client_fd);}
}
2. 写入数据(以EPOLLOUT为例)
void handle_send(int client_fd, int epfd, const char* data, size_t len) {ssize_t sent = 0;while (sent < len) {ssize_t n = send(client_fd, data + sent, len - sent, 0);if (n == -1) {if (errno == EAGAIN) { // 缓冲区满,等待下次可写事件// 重新注册EPOLLOUT事件(需先删除再添加)struct epoll_event event;event.data.fd = client_fd;event.events = EPOLLOUT | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_MOD, client_fd, &event);return;}perror("send failed");close(client_fd);del_epoll_event(epfd, client_fd);return;}sent += n;}// 数据发送完毕,取消可写事件监听(按需)struct epoll_event event;event.data.fd = client_fd;event.events = EPOLLIN | EPOLLET; // 恢复监听读事件epoll_ctl(epfd, EPOLL_CTL_MOD, client_fd, &event);
}
十、完整ET模式代码示例(基于epoll)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>#define MAX_EVENTS 10
#define SERVER_PORT 6000
#define BUF_SIZE 1024// 设置非阻塞模式
int set_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL);if (flags == -1) return -1;flags |= O_NONBLOCK;return fcntl(fd, F_SETFL, flags);
}// 向epoll添加fd(ET模式)
void add_epoll_et(int epfd, int fd) {struct epoll_event event;event.data.fd = fd;event.events = EPOLLIN | EPOLLET; // 注册读事件+ET模式if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event) == -1) {perror("epoll_ctl add ET failed");}
}// 处理客户端连接
void handle_accept(int listen_fd, int epfd) {int client_fd = accept(listen_fd, NULL, NULL);if (client_fd == -1) {perror("accept failed");return;}printf("Client %d connected (ET mode)\n", client_fd);if (set_nonblocking(client_fd) == -1) { // 设置非阻塞close(client_fd);return;}add_epoll_et(epfd, client_fd); // 注册ET模式
}// 处理客户端数据(ET模式下的非阻塞读取)
void handle_recv_et(int client_fd, int epfd) {char buf[BUF_SIZE] = {0}; // 初始化缓冲区ssize_t n;while (1) { // 大循环,持续读取直到遇到 EAGAIN/EWOULDBLOCK 或错误n = recv(client_fd, buf, sizeof(buf), 0); // 非阻塞读取if (n == -1) { // 读取错误或无数据可读if (errno == EAGAIN || errno == EWOULDBLOCK) {// 缓冲区无数据,正常情况,发送响应并退出循环send(client_fd, "ok", 2, 0);break;} else {// 其他错误(如连接重置)perror("recv error");close(client_fd);epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, NULL);break;}} else if (n == 0) { // 对端关闭连接printf("Client %d disconnected\n", client_fd);close(client_fd);epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, NULL);break;} else { // n > 0,读取到有效数据printf("ET Recv from %d: %s\n", client_fd, buf);// 处理数据(这里简单回发 "ok")send(client_fd, "ok", 2, 0);memset(buf, 0, sizeof(buf)); // 清空缓冲区// 注意:这里不 break,继续循环读取剩余数据(ET 模式需要一次性读完)}}
}int main() {int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) { perror("socket failed"); exit(1); }struct sockaddr_in addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(SERVER_PORT);addr.sin_addr.s_addr = INADDR_ANY;if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {perror("bind failed"); exit(1);}if (listen(listen_fd, 5) == -1) { perror("listen failed"); exit(1); }int epfd = epoll_create(1);if (epfd == -1) { perror("epoll_create failed"); exit(1); }// 注册服务器套接字(LT模式,因为accept是阻塞操作)struct epoll_event event;event.data.fd = listen_fd;event.events = EPOLLIN; // 水平触发模式epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &event);struct epoll_event events[MAX_EVENTS];while (1) {int n = epoll_wait(epfd, events, MAX_EVENTS, -1);if (n < 0) { perror("epoll_wait failed"); continue; }for (int i = 0; i < n; i++) {if (events[i].data.fd == listen_fd) {handle_accept(listen_fd, epfd); // 处理连接(LT模式)} else {if (events[i].events & EPOLLIN) {handle_recv_et(events[i].data.fd, epfd); // 处理ET读事件}}}}close(listen_fd);close(epfd);return 0;
}
十一、LT模式与ET模式对比
特性 | 水平触发(LT,默认) | 边缘触发(ET) |
---|---|---|
触发条件 | fd就绪时持续触发 | fd状态变化时触发一次 |
I/O模式 | 阻塞/非阻塞均可 | 必须使用非阻塞I/O |
数据处理 | 可分批读取(每次触发时处理部分数据) | 必须一次性读完(循环读取至EAGAIN) |
适用场景 | 通用场景(如数据分批到达) | 高性能场景(如日志、消息队列) |
代码复杂度 | 较低(无需循环读取) | 较高(需处理EAGAIN错误) |
十二、注意事项
-
避免阻塞操作:
- ET模式下,任何阻塞操作(如不带
O_NONBLOCK
的read
)都会导致事件循环阻塞,引发严重性能问题。
- ET模式下,任何阻塞操作(如不带
-
数据完整性:
- 单次
recv
可能无法读取完整数据,必须通过循环读取确保数据全部处理(依赖EAGAIN
判断)。
- 单次
-
事件注册:
- 若修改事件(如从读改为写),需通过
epoll_ctl(EPOLL_CTL_MOD)
重新注册,否则可能丢失事件。
- 若修改事件(如从读改为写),需通过
-
缓冲区管理:
- 写操作时需处理
EAGAIN
(缓冲区满),通常需要维护待发送数据缓冲区,并在下次EPOLLOUT
事件时继续发送。
- 写操作时需处理
相关文章:
I/O多路复用:poll与epoll
一、select/poll与epoll对比 核心区别 特性select/pollepoll内核数据结构数组(线性结构)红黑树(存储监听的fd)内存拷贝每次调用需将fd列表从用户态拷贝到内核态仅在注册fd时拷贝一次(epoll_ctl)内核事件检…...
【调制识别】PGD攻击中参数的含义
在PGD(Projected Gradient Descent)对抗攻击中,代码如下: # 定义PGD对抗样本生成类 class AttackPGD(nn.Module): def __init__(self, model, config):super(AttackPGD, self).__init__()self.model model …...
设备数据看板助力自动化工厂实现生产智能精细化管理
工厂数字化转型需要实现自动化设备生产现场可视化、设备系统间的互联互通,以及数据的智能决策。然而,当前许多制造企业仍面临着传统单机设备同质化严重、数字化服务能力不足、售后成本高企、系统集成效率低下等挑战。企业如何通过自动化装备看板和实时数…...
单点登录是是什么?具体流程是什么?
SSO⼀般都需要⼀个独⽴的认证中⼼(passport),⼦系统的登录均得通过passport,⼦系统本⾝将不参与登录操作,当⼀个系统成功登录以后,passport将会颁发⼀个令牌给各个⼦系统,⼦系统可以拿着令牌会获…...
什么业务需要用到waf
Web应用防火墙(Web Application Firewall,简称WAF)主要用于保护Web应用程序免受各种网络攻击,以下是需要用到WAF的业务类型及具体场景: 一、电子商务业务 业务特点:涉及用户注册、登录、支付等敏感操作&a…...
java中的Servlet4.x详解
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站 Servlet 4.x 是 Java EE 8(现 Jakarta EE)规范的一部分,于 2017 年正式发布。作为 Servlet 技术的重大更…...
## Docker 中 Elasticsearch 启动失败:日志文件权限问题排查与解决
好的,这是一份关于你遇到的 Docker Elasticsearch 启动报错问题的笔记,包含问题描述、我的分析判断以及最终的解决方案,适合用于整理成文章。 Docker 中 Elasticsearch 启动失败:日志文件权限问题排查与解决 在使用 Docker部署 E…...
JavaScript:PC端特效--缓动动画
一、缓动效果原理 缓动动画就是让元素运动速度有所变化,最常见的就是让元素慢慢停下来 思路: 让盒子每次移动的距离慢慢变小,速度就会慢慢降下来核心算法:(目标值-现在位置)/10作为每次移动距离的步长停…...
【深度学习新浪潮】什么是多模态大模型?
多模态大模型是人工智能领域的前沿技术方向,它融合了多种数据模态(如文本、图像、语音、视频、传感器数据等),并通过大规模参数模型实现跨模态的联合理解与生成。简单来说,这类模型就像人类一样,能同时“看”“听”“读”“说”,并将不同信息关联起来,完成复杂任务。 …...
C++23 std::mdspan:多维数组处理新利器
文章目录 引言C23简介std::mdspan的定义与特点定义特点 std::mdspan的优势零成本抽象的多维数据访问减少内存开销提高代码灵活性 std::mdspan的应用场景科学计算图形学 相关提案示例代码使用动态扩展使用静态和动态扩展 总结 引言 在C的发展历程中,每一个新版本都带…...
09、底层注解-@Import导入组件
09、底层注解-Import导入组件 Import是Spring框架中的一个注解,用于将组件导入到Spring的应用上下文中。以下是Import注解的详细介绍: #### 基本用法 - **导入配置类** java Configuration public class MainConfig { // 配置内容 } Configuration Impo…...
码蹄集——N是什么、棋盘
MT1223 N是什么 给定一系列数字3、10、21、36…,输入正整数N,输出上述序列的第N个值。从N1开始计数。 格式 输入格式:输入正整数N 输出格式:输出整型 样例 1 输入:5 输出:55 备注:N小于…...
C++中聚合类(Aggregate Class)知识详解和注意事项
一、聚合类(Aggregate Class)概念 聚合(Aggregate) 是 C 中一类特殊的类类型,无用户自定义构造函数、无私有或受保护非静态数据成员、无虚函数、无基类(C11 起基类必须也是聚合且无私有/受保护成员&#x…...
python打卡day30
模块和库的导入 知识点回顾: 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑:找到根目录(python解释器的目录和终端的目录不一致) 作业:自己新建几个不同路径文件尝试下如何导入 python的学习就像…...
PostGIS实现栅格数据导出图片标准格式【ST_AsGDALRaster】
ST_AsGDALRaster 函数应用详解:将栅格数据导出为标准图片格式 [文章目录] 一、函数概述 二、函数参数详解 三、关键功能示例 四、GDAL 选项配置指南 五、性能优化建议 六、注意事项与限制 七、应用场景 八、总结 一、函数概述 ST_AsGDALRaster是PostGIS中…...
4.6 sys模块
sys --- 仅作了解 面试之前冲击一下 python的垃圾回收机制 import sys # 1. api_version : 获取python的内部版本号 print(sys.api_version) #1013 # 2. copyright: 获取cpython的版本 print(sys.copyright) #3.getfilesystemencoding() getdefaultencoding():获…...
「HHT(希尔伯特黄变换)——ECG信号处理-第十三课」2025年5月19日
一、引言 心电信号(ECG)是反映心脏电活动的重要生理信号,其特征提取对于心脏疾病的诊断和监测具有关键意义。Hilbert - Huang Transform(HHT)作为一种强大的信号处理工具,在心电信号特征提取领域得到了广泛…...
枪机定焦系统的自动控制装置
枪机定焦系统,作为一种监控设备,通常被广泛应用于各种需要高清、远距离监控的场合。该系统的主要特点是其镜头焦距固定,不能手动或自动调节,从而确保了监控画面的稳定性和清晰度。当提到枪机定焦系统采用自动功能时,可…...
【Unity】Unity中将字典序列化
Unity中将字典序列化,在预制体上能够看到字典的内容,也可以直接在预制体上拖拽给字典赋值 直接上代码 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine;public class SerializableD…...
VTK|箱体切割器
文章目录 效果实现类头文件实现类源文件如何调用项目git链接 效果 实现类头文件 /*** file BoxClipperController.h* brief 该头文件定义了 BoxClipperController 类,用于管理基于盒子的网格数据裁剪操作。* details 该类提供了使用 vtkBoxWidget 对网格数据进行裁…...
CS50x 01 c
1 getchar() 在 C 语言里,getchar()是一个十分常用的函数,其功能是从标准输入(一般指键盘)读取单个字符。下面为你详细介绍它的用法。 基本语法 getchar()函数的原型定义在<stdio.h>头文件中,语法形式如下&am…...
确保高质量的音视频通话,如何最大化利用视频带宽
在当今数字时代,音视频内容随处可见,对于开发者来说,理解互联网带宽变得至关重要。我们的在线体验质量,无论是观看高清电影还是演唱会直播,都严重依赖于互联网带宽的概念。在本文中,我们将揭示视频带宽的复…...
@RequestParam 和 @RequestBody、HttpServletrequest 与HttpServletResponse
在Java Web开发中,RequestParam、RequestBody、HttpServletRequest 和 HttpServletResponse 是常用的组件,它们用于处理HTTP请求和响应。下面分别介绍它们的使用场景和使用方法: 1. RequestParam RequestParam 是Spring MVC框架中的注解&am…...
HashMap 的特点及应用场景
一、HashMap 核心特点 1. 基本特性 键值对存储:基于 Map 接口实现,存储 Key-Value 对 允许 null 键/值:可以有一个 null 键和多个 null 值 非线程安全:多线程环境下需要额外的同步措施 无序存储:不保证元素的插入顺…...
day30 python 模块、包与库的高效使用指南
目录 一、Python库的分类与适用场景 表格 1.1 基础工具库 1.2 科学计算库 1.3 数据分析库 1.4 Web开发库 1.5 机器学习库 1.6 自动化脚本库 1.7 网络爬虫库 二、模块与包的导入方式 2.1 标准导入 2.2 从模块中导入特定项 2.3 非标准导入(不推荐&#x…...
JVM核心配置参数详解与调优指南
精心整理了最新的面试资料和简历模板,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 引言 Java虚拟机(JVM)的配置参数直接影响应用程序的性能、稳定性和资源利用率。合理配置参数能够显著提升吞吐量、降低延迟并避免内存…...
python打卡第30天
知识点回顾: 一,导入官方库的三种手段。 使用 import 直接导入整个模块 import module_name 使用 from ... import ... 导入特定功能 from module_name import function_name 使用 as 关键字重命名模块或功能 import module_name as alias # 或 from mod…...
数据要素及征信公司数据要素实践
数据要素及征信公司数据要素实践 1.数据要素的定义与核心特征2.征信公司应用数据要素的实践路径3.总结1.数据要素的定义与核心特征 数据要素是数字经济时代的新型生产要素,指以电子形式存在、通过计算方式参与生产经营活动并创造价值的数据资源。 其核心特征包括: 新型生产…...
耗时十分钟,做了一个 uniapp 灵感泡泡机
最近,我用 UniApp 搭配 CodeBuddy 实现了一个充满童话感的小应用,名叫 IdeaBubbles(灵感泡泡机)。它是一个单页 WebApp,用来记录那些转瞬即逝的灵感时刻。整个界面以梦幻气泡和彩虹玻璃拟态为主题,视觉效果…...
《Head First 设计模式》第二章 - 笔记
本书是本人写的设计模式的笔记,写下核心要点,如果你掌握过设计模式,想快速阅读本书内容,这个笔记适合你阅读。如果你是新手,有 java 基础和 oo 设计原则基础,你适合跟我一样从零阅读本书。 第一章 观察者模…...
matlab绘制光学传递函数mtf曲线
在 MATLAB 中绘制光学系统的光学传递函数(Modulation Transfer Function,MTF)曲线可以通过以下步骤实现。MTF 是描述光学系统对物体细节的传递能力的函数,通常用于分析成像系统的性能。 假设我们有一个理想的光学系统,…...
贵州某建筑物挡墙自动化监测
1. 项目简介 某建筑物位于贵州省某县城区内,靠近县城主干道,周边配套学校、医院、商贸城。建筑物临近凤凰湖、芙蓉江等水系,主打“湖景生态宜居”。改建筑物总占地面积:约5.3万平方米;总建筑面积:约15万平…...
自定义协议与序列化
前言 书接上回,我们上一篇提到了协议并且我们草率的写了一个协议,然后又对TCP的R和W留了一个伏笔,我们今天彻底做个了断。 UDP是面向数据报的,它要么不读,要么就是一次读完的,所以不存在数据断断续续的问…...
MySQL中的Change Buffer是什么,它有什么作用?
MySQL 中的 Change Buffer(更改缓冲区)是 InnoDB 存储引擎使用的一种特殊数据结构,主要用于优化对二级索引(secondary indexes)的写操作性能。 它的核心作用是: 当对表进行 INSERT、UPDATE 或 DELETE 操作…...
Ubuntu 20.04之Docker安装ES7.17.14和Kibana7.17.14
你需要已经安装如下运行环境: Ubuntu 20.04 docker 28 docker-compose 1.25 一、手动拉取镜像 docker pull docker.elastic.co/kibana/kibana:7.17.14docker pull docker.elastic.co/elasticsearch/elasticsearch:7.17.14 或者手动导入镜像 docker load -i es7.17.14.ta…...
ThreadLocal作一个缓存工具类
1、工具类 import java.util.HashMap; import java.util.Map;public class ThreadLocalUtil {// 使用Map存储多类型数据private static final ThreadLocal<Map<String, Object>> CONTEXT_HOLDER new ThreadLocal<>();// 存储数据public static void set(Str…...
DeepSeek在旅游行业的智能化革命
2025年,从贵州全域智慧旅游平台的行程规划革命,到黄山景区"AI旅行助手"的实时服务升级,再到宁夏"游宁AI"的全域智能导览,DeepSeek通过技术创新与行业场景的深度融合,正在重新定义"智慧旅游"的内涵。这场变革不仅体现在效率提升层面,更通过…...
说一下响应状态码有哪些?
HTTP响应状态码分类(RFC 7231标准) 1. 1xx(信息类) 临时响应,表示请求已被接收,需要继续处理 100 Continue:客户端应继续发送请求体 101 Switching Protocols:服务器同意升级协议(如WebSocket) 102 Processing(WebDAV):服务器正在处理但未完成 2. 2xx(成功类)…...
ABAP实战案例--获取当前数据由哪个用户锁住
1、业务场景: A用户正在打开订单,订单已上锁;B用户打开时只允许查看并提醒由哪个用户正在操作该笔订单。 2、函数使用:ENQUEUE_READ 代码示例: DATA:LV_MESSAGE TYPE SY-MSGV1,LV_UNAME TYPE UNAME.DATA:LV_GARG …...
CSS 选择器入门
一、CSS 选择器基础:快速掌握核心概念 什么是选择器? CSS 选择器就像 “网页元素的遥控器”,用于定位 HTML 中的特定元素并应用样式。 /* 结构:选择器 { 属性: 值; } */ p { color: red; } /* 选择所有<p>元素,…...
【深度学习新浪潮】如何入门人工智能?
入门人工智能(AI)需要结合数学基础、编程技能、机器学习理论和实践项目,逐步深入。以下是一个系统的学习路径,适合零基础或初学者参考: 一、打好基础:数学与编程 1. 数学基础(关键) AI的核心依赖数学,尤其是以下领域: 线性代数:向量、矩阵运算、特征分解等(用于…...
软件工程第六章-详细设计
文章目录 程序流程图PAD图N-S图(盒图)流图根据PDL创建流图把程序流程图映射到流图 程序流程图 PAD图 N-S图(盒图) 流图 根据PDL创建流图 把程序流程图映射到流图...
Profinet转Modbus TCP协议转换技术,打通能耗监控‘最后一公里’
在工业自动化领域,Profinet与Modbus TCP是两种广泛使用的通讯协议。Profinet通常用于实时性要求较高的工业控制系统,而Modbus TCP则因其简单、开放的特性广泛应用于各类设备的通信。然而,由于两者在技术规范上的差异,直接的互联互…...
C++:因子问题
【描述】 任给两个正整数N、M,求一个最小的正整数a,使得a和(M-a)都是N的因子。 【输入】 包括两个整数N、M。N不超过1,000,000。 【输出】 输出一个整数a,表示结果。如果某个案例中满足条件的正整数不存在,则在对应行输出-1 【样例…...
SAP系统的委外业务是什么?委外采购(标准委外)与工序外协的区别有哪些?
【SAP系统研究】 #SAP #委外 #外包 #代工 委外业务是很常见的业务类型。 企业生产过程中,会在自制生产之外,产生委外加工业务,也称之为外包或者代工。还有一些企业,自己只负责设计、市场等业务,而将生产加工环节交给其他公司。 一、委外产生的原因 有的企业由于环评、…...
小乌龟git中的推送账户、作者账户信息修改
文章目录 修改git文档作者信息修改git推送用户信息参考文献 修改git文档作者信息 小乌龟中的用户信息为:作者信息,并非推送用户。 上边用户信息,修改的是文件的作者信息。如果想要修改git服务中记录的推送用户信息需要修改推送用户信息。 …...
vue2.0 组件
个人简介 👨💻个人主页: 魔术师 📖学习方向: 主攻前端方向,正逐渐往全栈发展 🚴个人状态: 研发工程师,现效力于政务服务网事业 🇨🇳人生格言&…...
5月19日笔记
BGP的路由聚合 BGP(Border Gateway Protocol,边界网关协议)是互联网中用于在不同自治系统(AS)之间交换路由信息的一种协议。在BGP中,路由聚合是一种技术,它允许网络管理员通过减少路由表中冗余的…...
【SPIN】PROMELA并发编程(SPIN学习系列--3)
一、active与run:Promela的进程创建基石 在Promela语言中,**active和run**是构建并发模型的核心关键字,分别用于定义主动进程和动态创建被动进程: active proctype <进程名>() 作用:声明主动进程类型࿰…...
深入理解 Redisson 看门狗机制:保障分布式锁自动续期
在分布式系统的开发中,分布式锁是解决资源竞争、数据一致性问题的关键手段。Redisson 作为一个在 Java 领域广泛使用的 Redis 客户端框架,为我们提供了功能强大且易用的分布式锁实现。其中,看门狗(watchDog)机制更是 R…...