linux网络编程(三)select、poll和epoll

linux网络编程(三)select、poll和epoll

  • 一、为什么会有多路I/O转接服务器?
  • 二、select
  • 三、poll
  • 三、epoll

一、为什么会有多路I/O转接服务器?

为什么会有多路I/O转接服务器呢?在学这个之前,我们同使用的是多线程或者多进程的方式来连接服务器,那么cpu将会占用大量资源来处理。那么我们试想一下,如果有几千台客户端来连接呢,那么就要创建几千个进程或者线程。有没有一种不怎么占用cpu资源的方式呢。这时候多路I/O转接服务器的这种机制就很符合这种情况。操作系统会用内核来监听文件,而不再用进程或者线程来监听,相当于请了一个“帮手”,当这些客户端请求的时候,这个“帮手”会帮你处理好,再反馈给程序,此时调用accept就无需等待了,内核已经帮你弄好了

二、select

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

nfds: 		监控的文件描述符集里最大文件描述符加1,因为此参数会告诉内核检测前多少个文件描述符的状态
readfds:	监控有读数据到达文件描述符集合,传入传出参数
writefds:	监控写数据到达文件描述符集合,传入传出参数
exceptfds:	监控异常发生达文件描述符集合,如带外数据到达异常,传入传出参数
timeout:	定时阻塞监控时间,3种情况1.NULL,永远等下去2.设置timeval,等待固定时间3.设置timeval里时间均为0,检查描述字后立即返回,轮询
struct timeval {long tv_sec; /* seconds */long tv_usec; /* microseconds */
};
void FD_CLR(int fd, fd_set *set); 	//把文件描述符集合里fd清0
int FD_ISSET(int fd, fd_set *set); 	//测试文件描述符集合里fd是否置1
void FD_SET(int fd, fd_set *set); 	//把文件描述符集合里fd位置1
void FD_ZERO(fd_set *set); 			//把文件描述符集合里所有位清0
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#include "wrap.h"
#define SERV_PORT 6666
#define CONNECT 30int main()
{int listen_fd,ret,maxfd, nready,con_fd,i,sockfd,n;int client[FD_SETSIZE];char buf[BUFSIZ] = { 0 };//创建套接字,已经封装好错误处理listen_fd = Socket(AF_INET,SOCK_STREAM,0);//绑定套接字struct sockaddr_in sockadd;struct sockaddr_in clie_add;socklen_t len;len = sizeof(struct sockaddr_in);bzero(&sockadd,sizeof(struct sockaddr_in));bzero(&clie_add, sizeof(struct sockaddr_in));sockadd.sin_family = AF_INET;sockadd.sin_port = htons(SERV_PORT);sockadd.sin_addr.s_addr = htonl(INADDR_ANY);Bind(listen_fd,(struct sockaddr*)&sockadd,sizeof(struct sockaddr_in));//设置同时3次握手的次数Listen(listen_fd, CONNECT);//初始化最大数量的文件描述符maxfd = listen_fd;fd_set rset;fd_set allset;FD_SET(listen_fd,&allset);FD_SET(listen_fd, &rset);int maxi = -1;					// client[]的下标//初始化for (i = 0; i < FD_SETSIZE; i++)client[i] = -1; 		/* 用-1初始化client[] */while (1){rset = allset;nready = select(maxfd + 1, &rset, NULL, NULL, NULL);if (FD_ISSET(listen_fd, &rset))   //判断是否是新的客户端连接{printf("new client online......\n");con_fd=Accept(listen_fd, (struct sockaddr*)&clie_add,&len);FD_SET(con_fd, &allset);for (i = 0; i < FD_SETSIZE; i++){if (client[i] < 0){client[i] = con_fd;break;}}if (i > maxi){maxi = i;}//最大值更新if (con_fd > maxfd){maxfd = con_fd;}if (--nready == 0)continue;}else  //读数据{for (i = 0; i <= maxi; i++){if ((sockfd = client[i]) < 0){continue;}if (FD_ISSET(sockfd, &rset)){//读数据if ((n = Read(sockfd, buf, sizeof(buf))) == 0){Close(sockfd);		/* 当client关闭链接时,服务器端也关闭对应链接 */FD_CLR(sockfd, &allset); /* 解除select监控此文件描述符 */client[i] = -1;}else{int j;for (j = 0; j < n; j++)buf[j] = toupper(buf[j]);Write(sockfd, buf, n);}if (--nready == 0){break;}}}}}return 0;
}

三、poll

poll是select进阶的函数,
int poll(struct pollfd fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /
文件描述符 /
short events; /
监控的事件 /
short revents; /
监控事件中满足条件返回的事件 */
};

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#include "wrap.h"
#include <poll.h>
#define SERV_PORT 6666
#define CONNECT 30
#define OPEN_MAX 1024
int main()
{int listen_fd, ret, maxfd, nready, con_fd, i, sockfd, n;struct pollfd  client[OPEN_MAX];char buf[BUFSIZ] = { 0 };//创建套接字,已经封装好错误处理listen_fd = Socket(AF_INET, SOCK_STREAM, 0);//绑定套接字struct sockaddr_in sockadd;struct sockaddr_in clie_add;socklen_t len;len = sizeof(struct sockaddr_in);bzero(&sockadd, sizeof(struct sockaddr_in));bzero(&clie_add, sizeof(struct sockaddr_in));sockadd.sin_family = AF_INET;sockadd.sin_port = htons(SERV_PORT);sockadd.sin_addr.s_addr = htonl(INADDR_ANY);Bind(listen_fd, (struct sockaddr*)&sockadd, sizeof(struct sockaddr_in));//设置同时3次握手的次数Listen(listen_fd, CONNECT);//初始化最大数量的文件描述符maxfd = listen_fd;fd_set rset;fd_set allset;FD_SET(listen_fd, &allset);FD_SET(listen_fd, &rset);int maxi = 0;					// client[]的下标//初始化for (i = 0; i < OPEN_MAX; i++)client[i].fd = -1; 		/* 用-1初始化client[] */while (1){nready = poll(client, maxi+1,-1);if (client[0].revents & POLLIN)   //判断是否是新的客户端连接{printf("new client online......\n");con_fd = Accept(listen_fd, (struct sockaddr*)&clie_add, &len);FD_SET(con_fd, &allset);for (i = 1; i < FD_SETSIZE; i++){if (client[i].fd < 0){client[i].fd = con_fd;break;}}client[i].events = POLLIN;if (i > maxi){maxi = i;}//最大值更新if (con_fd > maxfd){maxfd = con_fd;}if (--nready == 0)continue;}else  //读数据{for (i = 1; i <= maxi; i++){if ((sockfd = client[i].fd) < 0){continue;}if (client[i].revents & POLLIN){//读数据if ((n = Read(sockfd, buf, sizeof(buf))) < 0){if (errno = ECONNRESET)  //收到RST标志{printf("client[%d] aborted connection\n",i);Close(sockfd);client[i].fd = -1;//不监听该文件描述符}}if ((n = Read(sockfd, buf, sizeof(buf))) == 0){Close(sockfd);		/* 当client关闭链接时,服务器端也关闭对应链接 */FD_CLR(sockfd, &allset); /* 解除select监控此文件描述符 */client[i].fd = -1;}else{int j;for (j = 0; j < n; j++)buf[j] = toupper(buf[j]);Write(sockfd, buf, n);}if (--nready == 0){break;}}}}}return 0;
}

三、epoll

epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)epfd:	为epoll_creat的句柄op:		表示动作,用3个宏来表示:EPOLL_CTL_ADD (注册新的fd到epfd),EPOLL_CTL_MOD (修改已经注册的fd的监听事件),EPOLL_CTL_DEL (从epfd删除一个fd);event:	告诉内核需要监听的事件struct epoll_event {__uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */};typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;} epoll_data_t;EPOLLIN :	表示对应的文件描述符可以读(包括对端SOCKET正常关闭)EPOLLOUT:	表示对应的文件描述符可以写EPOLLPRI:	表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)EPOLLERR:	表示对应的文件描述符发生错误EPOLLHUP:	表示对应的文件描述符被挂断;EPOLLET: 	将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)而言的EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#include "wrap.h"
#include <poll.h>
#include <sys/epoll.h>
#define SERV_PORT 6666
#define CONNECT 30
#define OPEN_MAX 1024
int main()
{int listen_fd, ret, maxfd, nready, con_fd, i, sockfd, n, efd,res;struct epoll_event tep, ep[OPEN_MAX];struct pollfd  client[OPEN_MAX];char buf[BUFSIZ] = { 0 };char str[BUFSIZ] = { 0 };//创建套接字,已经封装好错误处理listen_fd = Socket(AF_INET, SOCK_STREAM, 0);//绑定套接字struct sockaddr_in sockadd;struct sockaddr_in clie_add;socklen_t len;len = sizeof(struct sockaddr_in);bzero(&sockadd, sizeof(struct sockaddr_in));bzero(&clie_add, sizeof(struct sockaddr_in));sockadd.sin_family = AF_INET;sockadd.sin_port = htons(SERV_PORT);sockadd.sin_addr.s_addr = htonl(INADDR_ANY);Bind(listen_fd, (struct sockaddr*)&sockadd, sizeof(struct sockaddr_in));//设置同时3次握手的次数Listen(listen_fd, CONNECT);//初始化最大数量的文件描述符maxfd = listen_fd;fd_set rset;fd_set allset;FD_SET(listen_fd, &allset);FD_SET(listen_fd, &rset);int maxi = 0;					// client[]的下标efd = epoll_create(OPEN_MAX);if (efd == -1){perror("epoll error:");exit(1);}tep.events = EPOLLIN;tep.data.fd = listen_fd;res = epoll_ctl(efd,EPOLL_CTL_ADD,listen_fd,&tep);if (res == -1){perror("epoll_ctl error:");exit(1);}while (1){nready = epoll_wait(efd,ep,OPEN_MAX,-1);if (nready == -1){perror("epoll_wait error:");exit(1);}for (i = 0; i < nready; i++){if (!(ep[i].events & EPOLLIN)){continue;}if (ep[i].data.fd == listen_fd){len = sizeof(clie_add);con_fd = Accept(listen_fd, (struct sockaddr*)&clie_add, &len);printf("received from %s at PORT %d \n",inet_ntop(AF_INET, &clie_add.sin_addr.s_addr, str, sizeof(BUFSIZ)),ntohs(clie_add.sin_port));tep.events = EPOLLIN;tep.data.fd = con_fd;res = epoll_ctl(efd, EPOLL_CTL_ADD, con_fd, &tep);if (res == -1){perror("epoll_ctl error:");exit(1);}}else{sockfd = ep[i].data.fd;n = Read(sockfd, buf, BUFSIZ);if (n == 0){res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);if (res == -1){perror("epoll_ctl error:");exit(1);}close(sockfd);printf("client[%d] closed connection\n", sockfd);}else if (n < 0){perror("read n<0 error:");res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);close(sockfd);}else{for (i = 0; i < n; i++){buf[i] = toupper(buf[i]);}Write(STDOUT_FILENO, buf, n);Write(sockfd, buf, n);}}}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384386.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux socket编程(一) 对套接字操作的封装

转载:http://www.cnblogs.com/-Lei/archive/2012/09/04/2670942.html 以前写的&#xff0c;现在回顾一下&#xff1a; 下面是对socket操作的封装&#xff0c;因为在Linux下写中文到了windows里面会乱码&#xff0c;所以注释用英文来写&#xff0c;有空再查下解决方法吧 socket.…

数据结构(二)栈

栈一、栈顺序栈线性栈(不带头结点)线性栈(带头结点)共享栈一、栈 栈是只允许在一端进行插入或删除操作的线性表。 栈顶&#xff1a;线性表允许进行插入删除的那一端 栈底&#xff1a;固定的&#xff0c;不允许进行插入和删除的那一端 空栈&#xff1a;不含如何元素的空表 顺序…

如何在linux上安装sqlite数据库

如何在linux上安装sqlite数据库一、下载二、解压三、配置&#xff08;configure&#xff09;四、编译和安装五、执行sqlite3程序六、测试代码一、下载 首先要先下载sqlite3源码包 链接&#xff1a;https://pan.baidu.com/s/1_70342ZLlPjLlqGzpy5IHw 提取码&#xff1a;84ne …

数据结构(三)队列

数据结构&#xff08;三&#xff09;队列队列队列&#xff08;顺序存储&#xff09;循环队列&#xff08;顺序存储&#xff09;队列&#xff08;链式存储&#xff09;队列 队列是一种受限制的线性表&#xff0c;只允许表的一端插入&#xff0c;在表的另一端删除 队列&#xf…

Linux fcntl函数详解

转载&#xff1a;http://www.cnblogs.com/xuyh/p/3273082.html 功能描述&#xff1a;根据文件描述词来操作文件的特性。 文件控制函数 fcntl -- file control 头文件&#xff1a; #include <unistd.h> #include <fcntl.h> 函数原型&#xff1a; …

vs2019使用sqlite数据库远程连接linux

vs2019使用sqlite数据库远程连接linux一、sqlite3添加到目录二、添加依赖库三、测试一、sqlite3添加到目录 将两个sqlite3头文件放入目录中 二、添加依赖库 打开项目属性 添加完成 三、测试 #include <stdio.h> #include <sqlite3.h>int main(int argc, cha…

linux网络编程(四)线程池

linux网络编程&#xff08;四&#xff09;线程池为什么会有线程池&#xff1f;实现简单的线程池为什么会有线程池&#xff1f; 大多数的服务器可能都有这样一种情况&#xff0c;就是会在单位时间内接收到大量客户端请求&#xff0c;我们可以采取接受到客户端请求创建一个线程的…

AIGC:大语言模型LLM的幻觉问题

引言 在使用ChatGPT或者其他大模型时&#xff0c;我们经常会遇到模型答非所问、知识错误、甚至自相矛盾的问题。 虽然大语言模型&#xff08;LLMs&#xff09;在各种下游任务中展示出了卓越的能力&#xff0c;在多个领域有广泛应用&#xff0c;但存在着幻觉的问题&#xff1a…

关于C++子类父类成员函数的覆盖和隐藏

转载&#xff1a;http://blog.csdn.net/worldmakewayfordream/article/details/46827161 函数的覆盖 覆盖发生的条件&#xff1a; &#xff08;1&#xff09; 基类必须是虚函数&#xff08;使用virtual 关键字来进行声明&#xff09; &#xff08;2&#xff09;发生覆盖的两个函…

数据结构(四)串的顺序存储

#include <stdio.h> #include <stdlib.h>#define MAXLEN 255 //定长顺序存储 typedef struct {char ch[MAXLEN]; //每个分量存储一个字符int length; //串的实际长度 }SString;//串的初始化 bool StrAssign(SString& T, char* chars) {int i 0, len;char* …

数据结构(四)串的动态数组存储

#include <stdio.h> #include <stdlib.h>#define MAXLEN 255 //定长顺序存储 typedef struct {char* ch; //每个分量存储一个字符int length; //串的实际长度 }SString;//串的初始化 bool StrAssign(SString& T, char* chars) {int i 0, len;T.ch (char*)m…

C++名字隐藏

转载&#xff1a;http://www.weixueyuan.net/view/6361.html 如果派生类中新增一个成员变量&#xff0c;该成员变量与基类中的成员变量同名&#xff0c;则新增的成员变量就会遮蔽从基类中继承过来的成员变量。同理&#xff0c;如果派生类中新增的成员函数与基类中的成员函数同…

c语言深入浅出(一)strcpy和memcpy的区别

c语言深入浅出&#xff08;一&#xff09;strcpy和memcpy的区别strcpy和memcpy都是c语言的库函数 strcpy:只用于字符串的复制&#xff0c;当碰到‘\0’就停止了 memcpy:用于这个内存的拷贝&#xff0c;适用于结构体、字符数组、类等 char * strcpy(char * dest, const char * s…

C++ dynamic_cast操作符

转载&#xff1a;http://www.weixueyuan.net/view/6377.html 在C中&#xff0c;编译期的类型转换有可能会在运行时出现错误&#xff0c;特别是涉及到类对象的指针或引用操作时&#xff0c;更容易产生错误。Dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试。…

数据结构(五)树

数据结构&#xff08;五&#xff09;树一、基本操作树是n个节点的有限集&#xff0c;它是一种递归的数据结构 一、基本操作 #include <stdio.h> #include <stdlib.h> #include <iostream>#define Elemtype charusing namespace std; typedef struct BiTNod…

C++ typeid操作符

转载&#xff1a;http://www.weixueyuan.net/view/6378.html typeid操作符用于判断表达式的类型&#xff0c;注意它和sizeof一样是一个操作符而不是函数。如果需要使用typeid操作符&#xff0c;最好加上typeinfo头文件。 给出以下定义 int a;double b;char * c;long d; 下表列…

数据结构(五)层次遍历

数据结构&#xff08;五&#xff09;层次遍历// linear_listqueue.cpp : This file contains the main function. Program execution begins and ends there. //#include <iostream> #include <stdlib.h> #include <stdio.h> #define ElemType BiTree using …

C++成员函数指针的应用

转载&#xff1a;http://www.cppblog.com/colys/archive/2009/08/18/25785.html C中&#xff0c;成员指针是最为复杂的语法结构。但在事件驱动和多线程应用中被广泛用于调用回叫函数。在多线程应用中&#xff0c;每个线程都通过指向成员函数的指针来调用该函数。在这样的应用中…

cv2.VideoCapture()无法打开视频解决方法

cv2.VideoCapture无法打开视频解决方法问题解决方法问题 cv2.VideoCapture打开mp4文件&#xff0c;直接报错 解决方法 我们打开D:\opencv_3.4.2_Qt\opencv_3.4.2_Qt\x86\bin\&#xff08;opencv的dll动态库中找到&#xff09; 找到opencv_ffmpeg342.dll文件&#xff0c;放入…

函数指针指向类的静态成员函数

转载&#xff1a;http://www.cnblogs.com/dongyanxia1000/p/4906592.html 1. 代码 1 #include<iostream>2 #include<stdio.h>3 using namespace std;4 class Point5 {6 public:7 Point(int x0,int y0):x(x),y(y)8 { 9 count; 10 } 11 P…