当前位置: 首页 > news >正文

思科企业网络拓扑图seo优化网站推广

思科企业网络拓扑图,seo优化网站推广,大型网站的设计,互联网技术的发展文章目录 理解select函数select函数的功能和调用顺序设置文件描述符设置监视范围及超时select函数调用示例 优于select的epoll基于select的I/O复用速度慢实现epoll时必要的函数和结构体epoll_createepoll_ctlepoll_wait基于epoll的服务器端 边缘触发和水平触发 理解select函数 …

文章目录

    • 理解select函数
      • select函数的功能和调用顺序
      • 设置文件描述符
      • 设置监视范围及超时
      • select函数调用示例
    • 优于select的epoll
      • 基于select的I/O复用速度慢
      • 实现epoll时必要的函数和结构体
      • epoll_create
      • epoll_ctl
      • epoll_wait
      • 基于epoll的服务器端
    • 边缘触发和水平触发

理解select函数

select函数的功能和调用顺序

使用select函数可以将多个文件描述符集中到一起统一监视

  • 是否存在套接字接收数据
  • 无需阻塞传输数据的套接字有哪些
  • 哪些套接字发生了异常

select函数的调用方法和顺序

  • 设置文件描述符
  • 指定监视范围
  • 设置超时

​ ↓

  • 调用select函数

    ​ ↓

  • 查看调用结果

设置文件描述符

利用select函数可以同时监视多个文件描述符,监视文件门描述符也可以视为监视套接字,首先需要将要监视的文件描述符集中到一起。集中时也要按照监视项(接收、传输、异常)进行区分

  • FD_ZERO(fd_set * fdset) 将fd_set变量都初始化为0
  • FD_SET(int fd,fd_set *fdset) 在参数fdset指向的变量注册文件描述符fd的信息
  • FD_CLR(int fd, fd_set * fdset) 从参数fdset指向的变量中清楚文件描述符fd的信息
  • FD_ISSET(int fd , fd_set * fdset)若参数fdset指向的变量中包含文件描述符fd的信息,则返回 真

int main(void)
{fd_set set;FD_ZERO(&set);			0 0 0 0 ....FD_SET(1,&set);			0 1 0 0 ....FD_SET(2,&set);			0 1 1 0 ....FD_CLR(2,&set);			0 1 0 0 ....
}

设置监视范围及超时

#include <sys/select.h>
#include <sys/time.h>int select(int maxfd, fd_set * readset,fd_set *writeset,fd_set exceptset,const struct timeval * timeout);成功返回大于 0 的值,失败返回 - 1maxfd		监视文件描述符的数量readset		将所有关注是否存在待读取数据的文件描述符注册到fd_set型变量,并传递到其地址值writeset 	将所有关注是否可传无阻塞数据的文件描述符注册到fd_set型变量,并传递到其地址值exceptset   将所有关注是否发生异常的文件描述符注册到fd_set型变量,并传递其地址值timeout 	调用select函数后,为防止陷入无限阻塞的状态,传递超时time - out消息

文件描述符的监视范围与select函数的第一个参数有关,select要求通过第一个参数传递监视对象文件描述符的数量

select函数的超时时间与select函数的最后一个参数有关,其中timeval结构体定义为:

struct timeval
{long tv_sec;    	//秒long tv_usec;       //微秒
}

select函数只有在监视的文件描述符发生变化时才返回,如果未发生变化,就会进到阻塞状态。指定超时时间就是为了这种情况的发生,通过上述结构体变量,将秒数填入tv_sec成员,微秒数填入tv_usec成员,将结构体的地址值传递到select函数的最后一个参数,不想设置超时时间,直接传递NULL。

select函数调用示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>#define BUF_SIZE 100
void error_handling(char *buf);int main(int argc, char *argv[])
{int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;struct timeval timeout;fd_set reads, cpy_reads;socklen_t adr_sz;int fd_max, str_len, fd_num, i;char buf[BUF_SIZE];if(argc!=2) {printf("Usage : %s <port>\n", argv[0]);exit(1);}serv_sock=socket(PF_INET, SOCK_STREAM, 0);memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);serv_adr.sin_port=htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)error_handling("bind() error");if(listen(serv_sock, 5)==-1)error_handling("listen() error");FD_ZERO(&reads);FD_SET(serv_sock, &reads);fd_max=serv_sock;while(1){cpy_reads=reads;timeout.tv_sec=5;timeout.tv_usec=5000;if((fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout))==-1)break;if(fd_num==0)continue;for(i=0; i<fd_max+1; i++){if(FD_ISSET(i, &cpy_reads)){if(i==serv_sock)     // connection request!{adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);FD_SET(clnt_sock, &reads);if(fd_max<clnt_sock)fd_max=clnt_sock;printf("connected client: %d \n", clnt_sock);}else    // read message!{str_len=read(i, buf, BUF_SIZE);if(str_len==0)    // close request!{FD_CLR(i, &reads);close(i);printf("closed client: %d \n", i);}else{write(i, buf, str_len);    // echo!}}}}}close(serv_sock);return 0;
}void error_handling(char *buf)
{fputs(buf, stderr);fputc('\n', stderr);exit(1);
}

优于select的epoll

epoll 在内核里使用「红黑树」来关注进程所有待检测的 Socket,红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn),通过对这棵黑红树的管理,不需要像 select/poll 在每次操作时都传入整个 Socket 集合,减少了内核和用户空间大量的数据拷贝和内存分配。

epoll 使用事件驱动的机制,内核里维护了一个「链表」来记录就绪事件,只将有事件发生的 Socket 集合传递给应用程序,不需要像 select/poll 那样轮询扫描整个集合(包含有和无事件的 Socket ),大大提高了检测的效率。

基于select的I/O复用速度慢

  • 调用select函数后常见的针对所有文件描述符的循环语句
  • 每次调用select时都需要向该函数传递监视对象信息

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。

因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。

实现epoll时必要的函数和结构体

  • epoll_create: 创建保存epoll文件描述符的空间
  • epoll_ctl: 向空间注册并注销文件描述符
  • epoll_wait: 等待文件描述符发生变化

为添加和删除监视对象文件描述符,select方式中需要FD_SET、FD_CLR函数,但是在epoll中都是通过epoll_ctl函数请求操作系统完成

select方式中调用select等待文件描述符的变化,而epoll调用epoll_wait函数。

select方式中通过fd_set变量查看监视对象的状态变化,而epoll_wait方式通过结构体epoll_event将发生变化的文件描述符集中一起

    struct epoll_event {__uint32_t events; epoll_data_t data; };typedef union epoll_data {void *ptr;int fd;__uint32_t u32;__uint64_t u64;} epoll_data_t;

events可以是以下几个宏的集合:

  • EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
  • EPOLLOUT:表示对应的文件描述符可以写;
  • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
  • EPOLLERR:表示对应的文件描述符发生错误;
  • EPOLLHUP:表示对应的文件描述符被挂断;
  • EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
  • EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还监听这个socket的话,再次把这个socket加入到EPOLL队列

epoll_create

#include <sys/epoll.h>int epoll_create(int size);成功返回epoll文件描述符,失败返回 - 1

创建一个epoll的描述符,size用来告诉内核这个监听数目一共多大,此参数不同于select()中的第一个参数,给出最大监听的fd+1的值

当创建好epoll描述符后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

epoll_ctl

#include <sys/epoll.h>int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event);成功返回0,失败时返回-1epfd 	用于注册监视对象的epoll例程的文件描述符op		用于指定监视对象的添加、删除、更改操作 ↓EPOLL_CTL_ADD:注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd;fd		需要注册的监视对象文件描述符event 	监视对象的事件类型

epoll_wait

#include <sys/epoll.h>int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);成功返回时间的文件描述符,失败返回-1epfd		时间发生监视范围的epoll例程的文件描述符events		保存时间的文件描述符集合的结构体地址值 (缓冲需要动态分配)maxevents	第二个参数可以保存的最大事件数timeout		以毫秒为单位,传递-1,一直等待发送事件。

基于epoll的服务器端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>#define BUF_SIZE 100
#define EPOLL_SIZE 50
void error_handling(char *buf);int main(int argc, char *argv[])
{int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;socklen_t adr_sz;int str_len, i;char buf[BUF_SIZE];struct epoll_event *ep_events;struct epoll_event event;int epfd, event_cnt;if(argc!=2) {printf("Usage : %s <port>\n", argv[0]);exit(1);}serv_sock=socket(PF_INET, SOCK_STREAM, 0);memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);serv_adr.sin_port=htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)error_handling("bind() error");if(listen(serv_sock, 5)==-1)error_handling("listen() error");epfd=epoll_create(EPOLL_SIZE);ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE);event.events=EPOLLIN;event.data.fd=serv_sock;	epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);while(1){event_cnt=epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);if(event_cnt==-1){puts("epoll_wait() error");break;}for(i=0; i<event_cnt; i++){if(ep_events[i].data.fd==serv_sock){adr_sz=sizeof(clnt_adr);clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);event.events=EPOLLIN;event.data.fd=clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);printf("connected client: %d \n", clnt_sock);}else{str_len=read(ep_events[i].data.fd, buf, BUF_SIZE);if(str_len==0)    // close request!{epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);close(ep_events[i].data.fd);printf("closed client: %d \n", ep_events[i].data.fd);}else{write(ep_events[i].data.fd, buf, str_len);    // echo!}}}}close(serv_sock);close(epfd);return 0;
}void error_handling(char *buf)
{fputs(buf, stderr);fputc('\n', stderr);exit(1);
}

边缘触发和水平触发

epoll 支持两种事件触发模式,分别是边缘触发(edge-triggered,ET)和 水平触发(level-triggered,LT)

这两个术语还挺抽象的,其实它们的区别还是很好理解的。

  • 使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;
  • 使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;

举个例子,你的快递被放到了一个快递箱里,如果快递箱只会通过短信通知你一次,即使你一直没有去取,它也不会再发送第二条短信提醒你,这个方式就是边缘触发;如果快递箱发现你的快递没有被取出,它就会不停地发短信通知你,直到你取出了快递,它才消停,这个就是水平触发的方式。

这就是两者的区别,水平触发的意思是只要满足事件的条件,比如内核中有数据需要读,就一直不断地把这个事件传递给用户;而边缘触发的意思是只有第一次满足条件的时候才触发,之后就不会再传递同样的事件了。

如果使用水平触发模式,当内核通知文件描述符可读写时,接下来还可以继续去检测它的状态,看它是否依然可读或可写。所以在收到通知后,没必要一次执行尽可能多的读写操作。

如果使用边缘触发模式,I/O 事件发生时只会通知一次,而且我们不知道到底能读写多少数据,所以在收到通知后应尽可能地读写数据,以免错失读写的机会。因此,我们会循环从文件描述符读写数据,那么如果文件描述符是阻塞的,没有数据可读写时,进程会阻塞在读写函数那里,程序就没办法继续往下执行。所以,边缘触发模式一般和非阻塞 I/O 搭配使用,程序会一直执行 I/O 操作,直到系统调用(如 readwrite)返回错误,错误类型为 EAGAINEWOULDBLOCK

一般来说,边缘触发的效率比水平触发的效率要高,因为边缘触发可以减少 epoll_wait 的系统调用次数,系统调用也是有一定的开销的的,毕竟也存在上下文的切换。

select/poll 只有水平触发模式,epoll 默认的触发模式是水平触发,但是可以根据应用场景设置为边缘触发模式。

参考资料:

https://xiaolincoding.com/


更多资料尽在 GitHub 欢迎各位读者去Star

⭐学术交流群Q 754410389 持续更新中~~~


文章转载自:
http://heteroduplex.rwzc.cn
http://femality.rwzc.cn
http://minx.rwzc.cn
http://westwardly.rwzc.cn
http://foreface.rwzc.cn
http://taganrog.rwzc.cn
http://lithiasis.rwzc.cn
http://acetanilid.rwzc.cn
http://agriculturist.rwzc.cn
http://amount.rwzc.cn
http://isochronize.rwzc.cn
http://perai.rwzc.cn
http://cowfish.rwzc.cn
http://foolproof.rwzc.cn
http://crossable.rwzc.cn
http://timelessly.rwzc.cn
http://superfluorescence.rwzc.cn
http://hindrance.rwzc.cn
http://cadmus.rwzc.cn
http://crambe.rwzc.cn
http://dryasdust.rwzc.cn
http://cabinetmaking.rwzc.cn
http://npcf.rwzc.cn
http://titanomachy.rwzc.cn
http://uncdf.rwzc.cn
http://antarthritic.rwzc.cn
http://fundamentalism.rwzc.cn
http://centromere.rwzc.cn
http://ideograph.rwzc.cn
http://padre.rwzc.cn
http://conical.rwzc.cn
http://frogpond.rwzc.cn
http://puzzleheadedness.rwzc.cn
http://impersonator.rwzc.cn
http://paridigitate.rwzc.cn
http://amphimacer.rwzc.cn
http://prop.rwzc.cn
http://habdalah.rwzc.cn
http://confabulation.rwzc.cn
http://welt.rwzc.cn
http://alvar.rwzc.cn
http://indecipherability.rwzc.cn
http://telemetry.rwzc.cn
http://homage.rwzc.cn
http://generalisation.rwzc.cn
http://dunk.rwzc.cn
http://kay.rwzc.cn
http://haemoglobinopathy.rwzc.cn
http://pained.rwzc.cn
http://exclosure.rwzc.cn
http://melitriose.rwzc.cn
http://arabel.rwzc.cn
http://pontoneer.rwzc.cn
http://materials.rwzc.cn
http://babysitter.rwzc.cn
http://tubercled.rwzc.cn
http://rotoscythe.rwzc.cn
http://novel.rwzc.cn
http://cirsoid.rwzc.cn
http://mash.rwzc.cn
http://primogeniturist.rwzc.cn
http://pejoration.rwzc.cn
http://annuitant.rwzc.cn
http://hydrolant.rwzc.cn
http://bacteriolytic.rwzc.cn
http://stroke.rwzc.cn
http://filtrate.rwzc.cn
http://piling.rwzc.cn
http://nfu.rwzc.cn
http://shopwalker.rwzc.cn
http://breechloader.rwzc.cn
http://firmness.rwzc.cn
http://handweaving.rwzc.cn
http://orientation.rwzc.cn
http://vestibulocerebellar.rwzc.cn
http://laniary.rwzc.cn
http://citrange.rwzc.cn
http://shastra.rwzc.cn
http://desoxycorticosterone.rwzc.cn
http://muso.rwzc.cn
http://pythias.rwzc.cn
http://slice.rwzc.cn
http://size.rwzc.cn
http://ivorian.rwzc.cn
http://reusage.rwzc.cn
http://unrevealed.rwzc.cn
http://arrestment.rwzc.cn
http://up.rwzc.cn
http://pruning.rwzc.cn
http://unrip.rwzc.cn
http://allobar.rwzc.cn
http://colourfast.rwzc.cn
http://degender.rwzc.cn
http://farer.rwzc.cn
http://pockmarked.rwzc.cn
http://trapes.rwzc.cn
http://did.rwzc.cn
http://always.rwzc.cn
http://psion.rwzc.cn
http://thereinafter.rwzc.cn
http://www.hrbkazy.com/news/81378.html

相关文章:

  • 大型定制网站最贵建设多少钱注册推广赚钱一个80元
  • 网站的发布与推广方式免费seo软件
  • 外贸网站推广方法怎么把自己的网站发布到网上
  • 网站的规划 建设与分析论文十大营销策略
  • 建设旅游网站财务分析武汉网站优化
  • 如何查看网站的服务器位置天津百度推广网络科技公司
  • 番禺做哪些做网站的长沙建站工作室
  • 北京做企业网站多少钱举一个病毒营销的例子
  • 线上推广的公司如何网站关键词优化
  • python 网站开发代码深圳平台推广
  • 有哪些做的比较精美的网站百度推广代理商
  • 做网站哪一家比较好短视频矩阵seo系统源码
  • 网站开发技术支持与保障2023最火的十大新闻
  • 做卡盟网站教程百度百家自媒体平台注册
  • 剑三在线客服网页关键词怎样做优化排名
  • 合肥大型网站制作公司百度一下你就知道了官网
  • 波纹工作室 网站伊春seo
  • 郑州微信网站开发如何推广宣传一个品牌
  • wordpress用户上传资源验证码北京seo公司有哪些
  • 江苏国税网站电子申报怎么做seo优化官网
  • 福州市网站建设有限公司重庆网站seo服务
  • 支付宝接口 网站备案seo刷关键词排名优化
  • 常州外贸网站设计营销软文范例大全100
  • 网站建设技术方面论文西安搜索引擎优化
  • 知科网站百度推广产品有哪些
  • github怎么做网站的空间软文案例200字
  • 椒江建设网保障性阳光工程网站关键词seo报价
  • 网络规划设计师攻略武汉seo网站推广
  • 腾讯域名怎么建设网站seo关键词排名优化教程
  • 坪地网站建设包括哪些百度服务