【Linux】多路IO复用技术②——poll详解如何使用poll模型实现简易的一对多服务器(附图解与代码实现)
在阅读本篇博客之前,建议大家先去看一下我之前写的这篇博客,否则你很可能会一头雾水
【Linux】多路IO复用技术①——select详解&如何使用select模型在本地主机实现简易的一对多服务器(附图解与代码实现)
http://t.csdnimg.cn/kPjvk
如果你看完了上面这篇博客,或者对select的原理和网络通信方面有一定了解的话,就可以开始阅读下面的内容了。那么废话不多说,我们正式开始了。
目录
poll模型的优缺点
poll模型的相关接口
监听结构体struct pollfd
监听函数poll
poll函数使用时的注意事项
本地主机使用poll模型实现简易一对多服务器的程序实现
程序构成 结果图示
这篇博客为大家讲解的是多路IO复用技术②——poll
PS:由于poll模型的原理和select模型的实现原理基本一致,并且select的原理已经在上述的博客中详细讲过,所以这里不再浪费篇幅再讲一遍了
首先,我们先来了解一下poll模型的优缺点
poll模型的优缺点
优点:
- 在内网场景下,poll也算是不错的IO复用模式
- 对监听集合进行了传入传出分离设置,不需要用户再自己设置传入集合(监听集合)和传出集合(就绪集合)
- 相比select,poll可以监听的事件种类更加丰富(具体可见下面的博客接口部分——struct pollfd)
- 可以为不同的套接字设置不同的监听事件,不像select模型只能批量设置监听事件
- poll模型可以监听的socket数量不受1024的硬限制,允许用户自定义数组作为监听集合,数组想设置多大就设置多大(其实这也不能完全算优点,下面讲缺点时会讲为什么)
缺点:
- poll模型的兼容性极差,甚至部分linux系统都不认识poll模型,更别说windows系统了
- 随着select的持续使用,会产生大量的拷贝开销和挂载开销(原因和select模型一样)
- 与select模型一样,poll的监听也是通过一次次的遍历实现的,非常消耗CPU,会导致服务器吞吐能力会非常差。更可怕的是,select遍历的大小仅为1024,而poll模型遍历的大小是由用户决定的,如果用户设置的监听集合大小为100000,就意味着poll遍历的大小就是100000,服务器很可能会直接瘫痪
在了解了poll模型的优缺点后,我们来了解一下poll模型的相关函数
poll模型的相关接口
以下接口的头文件都是 #include <poll.h>
监听结构体struct pollfd
先来介绍一下poll模型中的监听集合是什么样的
poll中的监听集合是一个结构体数组,这里我们将变量名设为listen_array[size],写法如下所示:
#define SIZE 10000struct pollfd listen_array[SIZE];//用户自定义的监听集合pollfd结构体中的成员
struct pollfd
{int fd; //目标套接字的文件描述符,取消监听就设为-1short events; //想要监听什么事件short revetns;
};
我们来讲解一下revents这个成员的作用
当套接字就绪时触发某相关事件时,系统会将其设置为对应事件的宏定义,用户可以使用该成员判断套接字是否就绪
比如套接字中有数据来了,需要读取处理,触发读事件,系统就会自动将revents设置为读事件对应的常量POLLIN,用户就可以去通过判断revents是不是等于POLLIN来判断是否读事件就绪
events 和 revents 可取的值及对应事件如下图所示:

监听函数poll
先来介绍一下一会会用到的参数:
- #define SIZE 10000; //监听集合的大小
- struct pollfd listen_array[SIZE]; //自定义监听集合
- nfds_t nfds; //最大监听套接字的数量,一般传监听数组的大小
- int timeout; //工作模式
我们来介绍一下这个timeout,timeout有以下几种设置方式:
- timeout = -1:poll 调用后阻塞等待,直到被监视的某个文件描述符上的某个事件就绪。
- timeout = 0:poll 调用后非阻塞等待,无论被监视的文件描述符上的事件是否就绪,poll 检测后都会立即返回。
- timeout = 特定的时间值:poll 调用后在指定的时间内进行阻塞等待,如果被监视的文件描述符上一直没有事件就绪,则在经过长度为timeout的时间后, poll 进行超时返回。(以毫秒为级别)
| 函数 | 功能 | 返回值 |
| int poll(listen_array , nfds , timeout); | 监听集合中是否有套接字就绪 | 1.函数调用成功,则返回就绪的套接字个数 2.如果 timeout 时间耗尽,但没有套接字就绪,则返回 0。 3.如果函数调用失败,则返回 -1,同时会传出错误码。 |
常见的错误码有以下四种:
- EFAULT:数组listen_array不包含在调用程序的地址空间中。
- EINTR:此调用被信号所中断。
- EINVAL:nfds值的大小超过RLIMIT_NOFILE。
- ENOMEM:核心内存不足。
poll函数使用时的注意事项
既然前面都说了:poll模型可以监听的socket数量不受1024的硬限制,允许用户自定义数组作为监听集合,数组想设置多大就设置多大
那我现在写一段小代码,大家看一看这个程序对不对,系统会不会报错(假设已经完成了网络初始化并已经设置了服务器套接字监听)
int ready_num;//阻塞监听socket相关事件
if((ready_num = poll(client_sockfd_array , 4096 , -1)) == -1)
{perror("poll call failed\n");exit(0);
}
else
{printf("1\n");
}
怎么样?有自己的结果了吗?接下来,我们公布正确答案
这个程序是错的!!!!!
为什么呢?这就要牵涉到进程相关的知识了
这是因为一个进程默认打开的最大文件描述符个数就是1024,我们可以通过ulimit -a命令在终端下查看,如下图所示

所以,当我们在poll函数中的最大监听数那个位置,填入比1024更大的数值的话,系统就会报错,警告Invalid argument——无效的参数
想要填入比1024更大的数值,我们就需要去修改默认的文件描述符数量,由于每个系统,甚至每个版本改动文件描述符数量的操作方式不一定一样,所以永久修改文件描述符数量的方式,这里就不多作介绍了,感兴趣的同学可以去查一下对应自己系统、对应自己版本的修改方式
我们这里简单介绍一下只对当前终端生效的修改方式,如下图所示

使用poll模型实现简易一对多服务器的程序实现
程序构成
该服务器与客户端由以下几个程序共同组成:
- func_2th_parcel.h:定义二次包裹的函数名
- func_2th_parcel.c:对网络初始化相关的函数进行二次包裹
- poll_server.c:使用poll模型的服务器程序
- client.c:客户端程序
/*************************************************************************> File Name: func_2th_parcel.h> Author: Nan> Mail: **@qq.com> Created Time: 2023年10月18日 星期三 18时32分22秒************************************************************************/#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <pthread.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/mman.h>
#include <time.h>
#include <ctype.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <poll.h>
#include <sys/epoll.h>//socket函数的二次包裹
int SOCKET(int domain , int type , int protocol);//bind函数的二次包裹
int BIND(int sockfd , const struct sockaddr* addr , socklen_t addrlen);//listen函数的二次包裹
int LISTEN(int sockfd , int backlog);//send函数的二次包裹
ssize_t SEND(int sockfd , const void* buf , size_t len , int flags);//recv函数的二次包裹
ssize_t RECV(int sockfd , void* buf , size_t len , int flags);//connect函数的二次包裹
int CONNECT(int sockfd , const struct sockaddr* addr , socklen_t addrlen);//accept函数的二次包裹
int ACCEPT(int sockfd , struct sockaddr* addr , socklen_t addrlen);//网络初始化函数
int SOCKET_NET_CREATE(const char* ip , int port);//服务端与客户端建立连接并返回客户端套接字文件描述符
int SERVER_ACCEPTING(int server_fd);
/*************************************************************************> File Name: func_2th_parcel.c> Author: Nan> Mail: **@qq.com> Created Time: 2023年10月18日 星期三 18时32分42秒************************************************************************/#include <func_2th_parcel.h>int SOCKET(int domain , int type , int protocol){int return_value;if((return_value = socket(domain , type , protocol)) == -1){perror("socket call failed!\n");return return_value;}return return_value;
}int BIND(int sockfd , const struct sockaddr* addr , socklen_t addrlen){int return_value; if((return_value = bind(sockfd , addr , addrlen)) == -1){perror("bind call failed!\n");return return_value;} return return_value;
}int LISTEN(int sockfd , int backlog){int return_value; if((return_value = listen(sockfd , backlog)) == -1){perror("listen call failed!\n");return return_value;} return return_value;
}ssize_t SEND(int sockfd , const void* buf , size_t len , int flags){ssize_t return_value;if((return_value = send(sockfd , buf , len , flags)) == -1){perror("send call failed!\n");return return_value;}return return_value;
}ssize_t RECV(int sockfd , void* buf , size_t len , int flags){ssize_t return_value; if((return_value = recv(sockfd , buf , len , flags)) == -1){perror("recv call failed!\n");return return_value;} return return_value;
}int CONNECT(int sockfd , const struct sockaddr* addr , socklen_t addrlen){int return_value; if((return_value = connect(sockfd , addr , addrlen)) == -1){perror("connect call failed!\n");return return_value;} return return_value;
}int ACCEPT(int sockfd , struct sockaddr* addr , socklen_t addrlen){int return_value; if((return_value = accept(sockfd , addr , &addrlen)) == -1){perror("accept call failed!\n");return return_value;} return return_value;
}int SOCKET_NET_CREATE(const char* ip , int port){int sockfd;struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);inet_pton(AF_INET , ip , &addr.sin_addr.s_addr);sockfd = SOCKET(AF_INET , SOCK_STREAM , 0);BIND(sockfd , (struct sockaddr*)&addr , sizeof(addr));LISTEN(sockfd , 128);return sockfd;
}int SERVER_ACCEPTING(int server_fd)
{int client_sockfd;struct sockaddr_in client_addr;char client_ip[16];char buffer[1500];bzero(buffer , sizeof(buffer));bzero(&client_addr , sizeof(client_addr));socklen_t addrlen = sizeof(client_addr);client_sockfd = ACCEPT(server_fd , (struct sockaddr*)&client_addr , addrlen);bzero(client_ip , 16);//将客户端的IP地址转成CPU可以识别的序列并存储到client_ip数组中inet_ntop(AF_INET , &client_addr.sin_addr.s_addr , client_ip , 16);sprintf(buffer , "Hi , %s welcome tcp test server service..\n" , client_ip);printf("client %s , %d , connection success , client sockfd is %d\n" , client_ip , ntohs(client_addr.sin_port) , client_sockfd);SEND(client_sockfd , buffer , strlen(buffer) , MSG_NOSIGNAL);return client_sockfd;
}
/*************************************************************************> File Name: poll_server.c> Author: Nan> Mail: **@qq.com> Created Time: 2023年10月25日 星期三 18时53分30秒************************************************************************/#include <func_2th_parcel.h>int main(void)
{//一、进行网络初始化int server_sockfd;//服务器套接字文件描述符struct pollfd client_sockfd_array[1024];//存放客户端套接字相关结构体的数组 int client_sockfd;//客户端套接字文件描述符int ready_num;//获取处于就绪状态的套接字数目char rw_buffer[1500];//读写缓冲区int flag;int recv_len;//客户端发来的数据长度//将结构体数组中对应套接字文件描述符的那一位置为-1,方便后面查找就绪套接字for(int i = 1 ; i < 1024 ; i++){//从1开始初始化是因为,0那一位要留给服务器套接字client_sockfd_array[i].fd = -1;client_sockfd_array[i].events = POLLIN;//都设置为监听读事件}bzero(rw_buffer , sizeof(rw_buffer));server_sockfd = SOCKET_NET_CREATE("192.168.79.128" , 6060);//初始化服务器套接字网络信息结构体//将服务器套接字结构体初始化一下client_sockfd_array[0].fd = server_sockfd;client_sockfd_array[0].events = POLLIN;printf("poll_server wait TCP connect\n");//二、启动监听,等待socket相关事件while(1){//阻塞等待socket读相关事件if((ready_num = poll(client_sockfd_array , 1024 , -1)) == -1){perror("poll call failed\n");exit(0);}//printf("readynum = %d\n" , ready_num);while(ready_num){//辨别就绪,如果是服务端套接字就绪if(client_sockfd_array[0].revents == POLLIN){client_sockfd = SERVER_ACCEPTING(client_sockfd_array[0].fd);//与客户端建立TCP链接for(int i = 1 ; i < 1024 ; i++){//将该客户端套接字,放到数组中有空缺的地方if(client_sockfd_array[i].fd == -1){client_sockfd_array[i].fd = client_sockfd;break;}}client_sockfd_array[0].revents = 0;//清0,防止ready_num > 1时会多次误判断就绪套接字为服务器套接字}//如果是客户端套接字就绪else{for(int i = 1 ; i < 1024 ; i++){//检测数组中下标位为i的地方是否存放的有客户端套接字文件描述符if(client_sockfd_array[i].fd != -1){//如果存放的有客户端套接字文件描述符,且该套接字处于就绪状态if(client_sockfd_array[i].revents == POLLIN){recv_len = RECV(client_sockfd_array[i].fd , rw_buffer , sizeof(rw_buffer) , 0);//获取数据长度printf("客户端%d 发来数据 : %s , 现在进行处理\n" , client_sockfd_array[i].fd , rw_buffer);flag = 0;//如果recv_len = 0,就说明与客户端套接字对应的客户端退出了,将对应客户端套接字移出监听集合if(recv_len == 0){printf("客户端%d 已下线\n" , client_sockfd_array[i].fd);close(client_sockfd_array[i].fd);client_sockfd_array[i].fd = -1;break;}//如果recv_len > 0,说明需要进行业务处理:小写字母转大写字母while(recv_len > flag){rw_buffer[flag] = toupper(rw_buffer[flag]);flag++;}printf("已向客户端%d 发送处理后的数据 : %s\n" , client_sockfd_array[i].fd , rw_buffer);SEND(client_sockfd_array[i].fd , rw_buffer , recv_len , MSG_NOSIGNAL);//发送处理后的数据给客户端bzero(rw_buffer , sizeof(rw_buffer));//清空读写缓冲区recv_len = 0;//重置数据长度client_sockfd_array[i].revents = 0;//清0,防止ready_num > 1时会多次误判断就绪套接字为该客户端套接字break;}}}}ready_num--;//已经处理一个,就绪套接字数量-1}}close(server_sockfd);printf("server shutdown\n");return 0;
}
/*************************************************************************> File Name: client.c> Author: Nan> Mail: **@qq.com> Created Time: 2023年10月19日 星期四 18时29分12秒************************************************************************/#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <time.h>//服务器实现大小写转换业务int main()
{//1.定义网络信息结构体与读写缓冲区并初始化struct sockaddr_in dest_addr;char buffer[1500];bzero(&dest_addr , sizeof(dest_addr));bzero(buffer , sizeof(buffer));dest_addr.sin_family = AF_INET;dest_addr.sin_port = htons(6060);//字符串ip转大端序列inet_pton(AF_INET , "192.168.79.128" , &dest_addr.sin_addr.s_addr);int sockfd = socket(AF_INET , SOCK_STREAM , 0);int i;//2.判断连接是否成功if((connect(sockfd , (struct sockaddr*) &dest_addr , sizeof(dest_addr))) == -1){perror("connect failed!\n");exit(0);}recv(sockfd , buffer , sizeof(buffer) , 0);printf("%s" , buffer);bzero(buffer , sizeof(buffer));//3.循环读取终端输入的数据while( (fgets(buffer , sizeof(buffer) , stdin) ) != NULL){i = strlen(buffer);buffer[i-1] = '\0';//向服务端发送消息send(sockfd , buffer , strlen(buffer) , MSG_NOSIGNAL);//接收服务端发来的消息recv(sockfd , buffer , sizeof(buffer) , 0);//打印服务端发来的信息printf("response : %s\n" , buffer);//清空读写缓冲区,以便下一次放入数据bzero(buffer , sizeof(buffer));}//4.关闭套接字,断开连接close(sockfd);return 0;
}
结果图示

以上就是本篇博客的全部内容了,大家有什么地方没有看懂的话,可以在评论区留言给我,咱要力所能及的话就帮大家解答解答
今天的学习记录到此结束啦,咱们下篇文章见,ByeBye!

相关文章:
【Linux】多路IO复用技术②——poll详解如何使用poll模型实现简易的一对多服务器(附图解与代码实现)
在阅读本篇博客之前,建议大家先去看一下我之前写的这篇博客,否则你很可能会一头雾水 【Linux】多路IO复用技术①——select详解&如何使用select模型在本地主机实现简易的一对多服务器(附图解与代码实现)http://t.csdnimg.cn/…...
CSS 滚动捕获 Scroll Snap
CSS 滚动捕获 Scroll Snap CSS 滚动捕获允许开发者通过声明一些位置(或叫作捕获位置)来创建精准控制的滚动体验. 通常来说轮播图就是这种体验的例子, 在轮播图中, 用户只能停在图 A 或者图 B, 而不能停在 A 和 B 的中间. 比如平时用淘宝或小红书, 当你上滑到下一个推荐内容时…...
【带头学C++】----- 三、指针章 ---- 3.9 数组作为函数的参数
当数组作为函数参数时,有几种常见的方式可以传递数组给函数: 数组作为指针传递: 数组名在函数调用时会自动转换为指向数组第一个元素的指针。通过指针可以访问数组元素,但无法获取数组的大小。在函数中修改指针指向的值会影响原始…...
完美处理 Android App 的 apk 输出路径与文件名
实现代码 buildTypes {// ...applicationVariants.all {variant ->variant.outputs.all {Calendar calendar Calendar.getInstance(Locale.CHINA);def buildDate String.format(Locale.CHINA, "%04d%02d%02d", calendar.get(Calendar.YEAR), calendar.get(Cale…...
【技术干货】开源库 Com.Gitusme.Net.Extensiones.Core 的使用
目录 1、项目介绍 2、为项目添加依赖 3、代码中导入命名空间 4、代码中使用 示例 1:string转换 示例 2:object转换 1、项目介绍 Com.Gitusme.Net.Extensiones.Core是一个.Net扩展库。当前最新版本1.0.4,提供了常见类型转换,…...
大厂面试题-b树和b+树的理解
为了更清晰的解答这个问题,从三个方面来回答: a.了解二叉树、AVL树、B树的概念 b.B树和B树的应用场景 1.B树是一种多路平衡查找树,为了更形象的理解,我们来看这张图。 二叉树,每个节点支持两个分支的树结构ÿ…...
NeRF-SLAM部署运行(3060Ti)
记录在部署运行期间遇到的一些问题,分享给大家~ 一、环境 RTX 3060 Ti、8G显存、Ubuntu18.04 二、部署 1. 下载代码 git clone https://github.com/jrpowers/NeRF-SLAM.git --recurse-submodules git submodule update --init --recursive cd thirdparty/insta…...
零基础编程入门教程软件推荐,零基础编程自学
零基础编程入门教程软件推荐,零基础编程自学 给大家分享一款中文编程工具,零基础轻松学编程,不需英语基础,编程工具可下载。 这款工具不但可以连接部分硬件,而且可以开发大型的软件,象如图这个实例就是用…...
Amazon EC2 安全可调用的云虚拟主机服务器
💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Amazon EC2 打造全新的科技链 Amazon Elastic Compute Cloud(Amazon EC2)提供最广泛、最深入的计算平台,拥有超过 500 个实例&…...
HTTP/HTTPS、SSL/TLS、WS/WSS 都是什么?
有同学问我,HTTP/HTTPS、SSL/TLS、WS/WSS 这都是些什么?那我们就先从概念说起: HTTP 是超文本传输协议,信息是通过明文传输。HTTPS 是在 HTTP 的基础上信息通过加密后再传输。SSL 是实现 HTTPS 信息传输加密的算法。TLS 是 SSL 的…...
软考之系统安全理论基础+例题
系统安全 系统安全性分析与设计 作为全方位的、整体的系统安全防范体系也是分层次的,不同层次反映了不同的安全问题,根据网络的应用现状情况和结构,可以将安全防范体系的层次划分为 物理层安全系统层安全网络层安全应用层安全安全管理。 …...
棱镜七彩亮相工控中国大会,以软件供应链安全助力新型工业化高质量发展
2023年11月1日-3日,2023第三届工控中国大会在苏州国际会议中心举办,本届大会由中国电子信息产业发展研究院、中国工业经济联合会、国家智能制造专家委员会、国家产业基础专家委员会、江苏省工业和信息化厅、江苏省国有资产监督管理委员会、苏州市人民政府…...
数据可视化:动态柱状图
终于来到最后一个数据可视化的文章拿啦~~~ 在这里学习如何绘制动态柱状图 我先整个活 (๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤ 什么是pyecharts? 答: Python的Pyecharts软件包。它是一个用于Python数据可视化和图表绘制的库,可用于制作…...
vue3 自定义loading
使用antdv 后发现只有button支持loaidng属性,而其他元素不能使用loading来显示是否加载中,需要套一层 a-spin 才能支持,非常不方便。 所以写了个自定义的指令来进行处理 新建loading.vue文件用来页面显示 <template><div class&q…...
Ceph-deploy跳过gpg-key验证(离线环境安装Ceph)
问题 CentOS-7.6.1810离线环境搭建Ceph环境时出现gpg-key安装源公钥检查错误。原因是执行ceph-deploy install 命令的服务器无法访问互联网。具体报错如下图: 解决 安装命令后新增--no-adjust-repos参数即可跳过安装 GPG 密钥。 命令如下: ceph-deplo…...
想入行单片机开发的学生们的忠告
想入行单片机开发的学生们的忠告 做嵌入式单片机开发十来年。想给那些想入行单片机开发的同学一些建议。 1.想做这行,做好坚持学习的准备。最近很多小伙伴找我,说想要一些单片机的资料,然后我根据自己从业十年经验,熬夜搞了几个通…...
【番外篇】C++语法学习笔记
学习目标:C的一些高级操作 根据C菜鸟教程自学的笔记,大家有想学习C的话可以根据这个网站进行学习。这个推荐有一定基础的再去进行自学。新手的话还是建议直接看一些视频跟着学 学习内容: 1. 运算符重载 说到C中的运算符重载,首…...
js 字符串转数字
在 JavaScript 中,可以使用以下方法将字符串转换为数字: parseInt parseInt():将字符串转换为整数。它会从字符串的开头开始解析,直到遇到非数字字符为止。如果第一个字符不能转换为数字,则返回 NaN。 let str &qu…...
【NI-DAQmx入门】外部采样时钟相关
1.时钟的作用 时钟在几乎所有测量系统中都起着至关重要的作用。通过硬件定时测量,时钟控制采样或更新的发生时间。与依赖软件计时测量相比,您可以选择硬件定时测量来实现采样或更新之间更一致的时间间隔。以数模转换器特性分析为例。该应用由三个基本部分…...
Amazon EC2 Hpc7g 实例现已在更多区域推出
即日起,Amazon Elastic Compute Cloud (Amazon EC2) Hpc7g 实例将在亚太地区(东京)、欧洲地区(爱尔兰)和 Amazon GovCloud(美国西部)区域推出。Amazon EC2 Hpc7g 实例由 Amazon Graviton 处理器…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
