IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Linux C/C++之IO多路复用(pollepoll) -> 正文阅读

[C++知识库]Linux C/C++之IO多路复用(pollepoll)

目录

1. poll

1.1 poll与select

1.2 poll的编程模型

1.3?poll监视标准输入设备0

1.4?poll函数原型

1.5?poll实现多个(客户)client端连接(服务器)server端

2. epoll

2.1?epoll相对于poll的优势

2.2?epoll编程模型

2.3?epoll函数原型

2.4?epoll实现多个(客户)client端连接(服务器)server端

1. poll

1.1 poll与select

  • poll与select非常类似
  • poll没有最大描述符号限制
  • select在操作描述符号时使用描述符号集合fd_set,?poll在操作描述符号时使用pollfd结构体链表或者数组

1.2 poll的编程模型

        //1. 创建fd结构体数组
		struct  pollfd  fds[300];
		int	fdNum = 0;   //当前描述符号数量
		//2. 把要监视的描述符号设置好
		fds[0].fd = 0;
		fds[0].events = POLLIN;
		fdNum++;
		//3. 监视
		int r = poll(fds,fdNum,0);
		if(-1 == r){
            //错误
        }else if(0 == r){
            //没有动静
            continue;
        }else{
            //有动静,检查对应事件
            if(fds[0].revents & POLLIN){
                
            }
        }

1.3?poll监视标准输入设备0

#include <stdio.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>

int main(){
	//1. 创建fd结构体数组
	struct pollfd fds[8];
	int fdNum = 0;  //当前描述符号数量
	//2. 把要监视的描述符号设置好
	fds[0].fd = 0;
	fds[0].events = POLLIN;
	fdNum++;
	//3. 监视
	int r;
	char buff[1024];

	while(1){
		r = poll(fds,fdNum,0);
		if(-1 == r){
			//错误
			printf("监视出错!\n");
		}else if(0 == r){
			//没有动静
			continue;
		}else{
			printf("有动静!\n");
			//有动静,检查对应事件
			if(fds[0].revents & POLLIN){
				memset(buff,0,1024);
				scanf("%s",buff);
				printf(">> %s\n",buff);
			}
		}
	}
	
	return 0;
}

1.4?poll函数原型

//需要的头文件
#include <poll.h>

struct pollfd{
    int fd;            //文件描述符号
    short events;      //请求的事件
    short revents;     //返回的事件
}

int poll(struct pollfd *fds, //链表头指针
         nfds_t nfds,        //链表节点数   
         int timeout);       //延时
//事件宏
POLLIN            //有数据可读
POLLRDNORM        //有普通数据可读
POLLRDBAND        //有优先数据可读

POLLPRI           //有紧急数据可读

POLLOUT           //写数据不会阻塞,可写数据
POLLWRNORM        //可以写普通数据
POLLWRBAND        //可以写优先数据
    
POLLMSGSIGPOLL    //消息可以使用
POLLER            //指定的fd出现错误
POLLHUP           //指定的fd被挂起
POLLNVAL          //指定的fd是非法的

1.5?poll实现多个(客户)client端连接(服务器)server端

服务器(server端)

//服务器端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <poll.h>

//最多允许的客户端数量
#define NUM 100

int serverSocket;

void hand(int val){
	close(serverSocket);
	printf("bye bye!\n");
	exit(0);
}
int main(int argc,char* argv[]){
	if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
	printf("ip: %s     port:%d\n",argv[1],atoi(argv[2]));

	signal(SIGINT,hand);

	//1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
	serverSocket = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == serverSocket) printf("创建socket失败:%m\n"),exit(-1);
	printf("创建socket成功!\n");

	//2. 创建服务器协议地址簇
	struct sockaddr_in sAddr = { 0 };
	sAddr.sin_family = AF_INET;        //协议类型 和socket函数第一个参数一致
	sAddr.sin_addr.s_addr = inet_addr(argv[1]);  //将字符串转整数
	sAddr.sin_port = htons(atoi(argv[2]));    //将字符串转整数,再将小端转换成大端

	//3. 绑定服务器协议地址簇
	int r = bind(serverSocket,(struct sockaddr*)&sAddr,sizeof sAddr);
	if(-1 == r) printf("绑定失败:%m\n"),close(serverSocket),exit(-2);
	printf("绑定成功!\n");

	//4. 监听  
	r = listen(serverSocket,10);   //数量
	if(-1 == r) printf("监听失败:%m\n"),close(serverSocket),exit(-3);
	printf("监听成功!\n");

	//监视serverSocket和ClientSocket
	struct sockaddr_in cAddr = {0};
	int len = sizeof(cAddr);
	int cfd;        //临时保存一个客户端fd
	char buff[1024];
	int fdNum = 0;

	//准备一个pollfd数组
	struct pollfd fds[NUM];
	//初始化数组
	for(int i = 0;i < NUM; i++){
		fds[i].fd = -1;
		fds[i].events = POLLIN;
	}
	
	//将serverSocket放进去
	fds[0].fd = serverSocket;
	fds[0].events = POLLIN;    //监视是否有数据可读
	fdNum++;

	while(1){
		r = poll(fds,fdNum,10);   //延时10ms
		if(-1 == r){
			printf("监视失败:%m\n"),close(serverSocket),exit(-1);
		}else if(0 == r){
			continue;
		}else{
			//有动静
			if(fds[0].revents & POLLIN){
				//有客户端连接服务器的动作
				cfd = accept(serverSocket,(struct sockaddr*)&cAddr,&len);
				if(-1 == cfd){
					printf("server Error!\n");
					continue;
				}else{
					printf("客户端%d -- %s连接上服务器了!\n",cfd,inet_ntoa(cAddr.sin_addr));
				}
				//将客户端的描述符号添加到监视数组中
				for(int i = 1; i < NUM; i++){
					if(-1 == fds[fdNum].fd){
						fds[fdNum].fd = cfd;
						fds[fdNum].events = POLLIN;
						fdNum++;
						break;
					}
				}
			}
		}
		//检查客户端动静,看是否有数据发送过来
		for(int i = 1; i < NUM; i++){
			if(-1 != fds[i].fd && (fds[i].revents & POLLIN)){
				r = recv(fds[i].fd,buff,1023,0);
				if(r > 0){
					buff[r] = 0;
					printf("%d >> %s\n",fds[i].fd,buff);
				}else{
					//客户端掉线了
					fds[i].fd = -1;
					fdNum--;
				}
			}
		}
	}

	return 0;
}

?客户(client)端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>

int clientSocket;
void hand(int val){
	//5. 关闭连接
	close(clientSocket);
	printf("bye bye!\n");
	exit(0);
}
int main(int argc,char* argv[]){
	if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
	printf("ip: %s     port:%d\n",argv[1],atoi(argv[2]));

	signal(SIGINT,hand);

	//1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
	clientSocket = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == clientSocket) printf("创建socket失败:%m\n"),exit(-1);
	printf("创建socket成功!\n");

	//2. 创建服务器协议地址簇
	struct sockaddr_in cAddr = { 0 };
	cAddr.sin_family = AF_INET;
	cAddr.sin_addr.s_addr = inet_addr(argv[1]);  //将字符串转整数
	cAddr.sin_port = htons(atoi(argv[2]));    //将字符串转整数,再将小端转换成大端

	//3.连接服务器
	int r = connect(clientSocket,(struct sockaddr*)&cAddr,sizeof cAddr);
	if(-1 == r) printf("连接服务器失败:%m\n"),close(clientSocket),exit(-2);
	printf("连接服务器成功!\n");

	//4. 通信
	char buff[256] = {0};
	while(1){
		printf("你想要发送:");
		scanf("%s",buff);
		send(clientSocket,buff,strlen(buff),0);
	}

	return 0;
}

2. epoll

2.1?epoll相对于poll的优势

  • select与poll没有太大的区别,?均是轮循fd监视实现,?因此效率受描述符号数量影响(监视的fd越多,速率越慢) --- 就相当于一直盯着手机看是否有人打电话
  • epoll是优化后的poll,通过注册事件实现,?不需要再轮循监视,因此效率不受监视的描述符号数量影响 ---?就相当于设置了来电提示看是否有人打电话

2.2?epoll编程模型

  1. 创建epoll? ? ? ? ? ? ? ? ? ?epoll_create

  2. 注册描述符号事件? ? ?epoll_ctl

  3. 等待,挨个处理事件? ? epoll_wait 检查?使用&EPOLLIN(与poll相同)

2.3?epoll函数原型

//需要的头文件
#include <sys/epoll.h>

int epoll_create(int size);    //数量

int epoll_ctl(int epfd,        //epoll_create的返回值,处理过的文件描述符
              int op,          //操作方式
              int fd,          //需要监视的文件描述符
struct epoll_event *event);    //监视的描述符号链表表头地址(或者数组首地址)

//参数二op
EPOLL_CTL_ADD                  //添加
EPOLL_CTL_MOD                  //修改
EPOLL_CTL_DEL                  //删除

int epoll_wait(int epfd,       //epoll_create的返回值,处理过的文件描述符
  struct epoll_event *events,  //监视的描述符号链表表头地址(或者数组首地址)
               int maxevents,  //事件结构体数量(第二个参数的数量)
               int timeout);   //延时
//使用的数据类型
typedef union epoll_data{
    void *ptr;
    int fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

struct epoll_event{
    uint32_t events;
    epoll_data_t data;
};

2.4?epoll实现多个(客户)client端连接(服务器)server端

服务器(server)端

//服务器端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <sys/epoll.h>

//最多允许的客户端数量
#define NUM 100

int serverSocket;

void hand(int val){
	close(serverSocket);
	printf("bye bye!\n");
	exit(0);
}
int main(int argc,char* argv[]){
	if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
	printf("ip: %s     port:%d\n",argv[1],atoi(argv[2]));

	signal(SIGINT,hand);

	//1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
	serverSocket = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == serverSocket) printf("创建socket失败:%m\n"),exit(-1);
	printf("创建socket成功!\n");

	//2. 创建服务器协议地址簇
	struct sockaddr_in sAddr = { 0 };
	sAddr.sin_family = AF_INET;        //协议类型 和socket函数第一个参数一致
	sAddr.sin_addr.s_addr = inet_addr(argv[1]);  //将字符串转整数
	sAddr.sin_port = htons(atoi(argv[2]));    //将字符串转整数,再将小端转换成大端

	//3. 绑定服务器协议地址簇
	int r = bind(serverSocket,(struct sockaddr*)&sAddr,sizeof sAddr);
	if(-1 == r) printf("绑定失败:%m\n"),close(serverSocket),exit(-2);
	printf("绑定成功!\n");

	//4. 监听  
	r = listen(serverSocket,10);   //数量
	if(-1 == r) printf("监听失败:%m\n"),close(serverSocket),exit(-3);
	printf("监听成功!\n");

	//监视serverSocket和ClientSocket
	struct sockaddr_in cAddr = {0};
	int len = sizeof(cAddr);
	int cfd;        //临时保存一个客户端fd
	char buff[1024];
	int fdNum = 0;

	//创建epoll 参数可缺省(只需设置最大描述符号即可)
	int epfd = epoll_create(NUM);
	if(-1 == epfd){
		printf("创建epoll失败:%m\n");
		close(serverSocket);
		exit(-1);
	}
	printf("创建epoll成功!\n");

	//注册描述符号事件
	struct epoll_event ev;  //注册时使用
	struct epoll_event events[NUM]; //等待处理事件时使用

	ev.events = EPOLLIN;
	ev.data.fd = serverSocket;

	r = epoll_ctl(epfd,EPOLL_CTL_ADD,serverSocket,&ev);
	if(-1 == r){
		printf("注册serverSocket事件失败:%m\n");
		close(epfd);
		close(serverSocket);
		exit(-2);
	}
	printf("注册serverSocket事件成功!\n");
	//等待,挨个处理事件
	//epoll_wait 成功返回有动静的描述符号数量
	int curCfd;  //用于保存当前发数据的客户端epoll_wait的返回值
	int nfds;    //用于保存epoll_wait的返回值
	while(1){
		nfds = epoll_wait(epfd,events,NUM,1000);  //延时1000ms
		if(nfds < 0){
			printf("epoll_wait失败:%m\n");
			close(epfd);
			close(serverSocket);
			exit(-3);
		}else if(0 == nfds){
			//没有动静
			continue;
		}else{
			//有动静
			printf("有动静发生----\n");
			for(int i = 0;i < nfds; i++){
				if(serverSocket == events[i].data.fd){
					cfd = accept(serverSocket,(struct sockaddr*)&cAddr,&len);
					if(-1 == cfd){
						printf("服务器崩溃:%m\n");
						close(epfd);
						close(serverSocket);
						exit(-4);
					}
					printf("有客户端%d连接上服务器了:%s\n",cfd,
						inet_ntoa(cAddr.sin_addr));

					//注册客户端描述符号事件
					ev.events = EPOLLIN;
					ev.data.fd = cfd;

					r = epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
					if(-1 == r){
						printf("注册clientSocket失败:%m\n");
						close(epfd);
						close(serverSocket);
						exit(-5);
					}
					printf("注册clientSocket事件成功!\n");
				}else if(events[i].events && EPOLLIN){
					//有客户端发数据过来
					curCfd = events[i].data.fd;
					r = recv(curCfd,buff,1023,0);
					if(r > 0){
						buff[r] = 0;
						printf("%d >> %s\n",curCfd,buff);
					}
				}
			}
		}
	}

	return 0;
}

客户(client)端? (与上方poll代码相同)

?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-14 23:31:36  更:2022-04-14 23:36:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:09:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码