epoll实现I/O多路复用

epoll是Linux特有的I/O复用函数,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率;并且epoll使用一组函数来完成任务,而不是单个函数,它无须遍历整个被侦听的描述符集,只要遍历那些内核I/O时间异步唤醒而加入ready队列的描述符集合即可。但epoll需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表。

1、这个文件描述符使用epoll_create函数来创建:


size参数现在并不起作用,只是给内核一个提示,告诉它事件表需要多大。该函数返回的文件描述符将用作其他所有epoll系统调用的第一个参数,以指定要访问的内核事件表。

2、使用epoll_create函数来操作内核事件表


epoll的事件注册函数,它不同与select函数是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

参数:

epfd:要操作的事件表的文件描述符

op:指定要操作的类型

1)EPOLL_CTL_ADD:往事件表中注册fd上的事件

2)EPOLL_CTL_MOD:修改fd上的注册事件

3)EPOLL_CTL_DEL:删除fd上的注册事件

event:指定事件,它是epoll_event结构类型的指针。

其中events成员描述事件类型。epoll支持的事件类型和poll基本相同。表示epoll事件类型的宏是在poll对应的宏前加上“E”;data成员用于存储用户数据。

3、epoll_wait函数:在一段超时时间内等待一组文件描述符上的事件。


该函数成功时返回就绪的文件描述符的个数,失败时返回-1并设置errno。

epoll_wait函数如果检测到事件,就将所有就绪事件从内核事件表(由epfd参数指定)中复制到它的第二个参数events指向的数组中。这个数组只用于输出epoll_wait检测到的就绪事件。

maxevents:告诉内核这个events参数有多大,这个maxevents的值不能小于创建epoll_create()时的size。

/*************************************************************************
	> File Name: epoll.c
	> Author: fucang_zxx
	> Mail: fucang_zxx@163.com 
	> Created Time: Tue 02 Aug 2016 01:21:26 PM CST
 ************************************************************************/

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

//创建监听套接字
int statup(char* _ip, int _port)
{
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if(sock < 0)
	{
		perror("socket");
		exit(3);
	}
	struct sockaddr_in local;
	local.sin_family = AF_INET;
	local.sin_port = htons(_port);
	local.sin_addr.s_addr = inet_addr(_ip);

	if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
	{
		perror("bind");
		exit(4);
	}

	if(listen(sock, 5) < 0)
	{
		perror("listen");
		exit(5);
	}

	return sock;
}

int main(int argc, char* argv[])
{
	if(argc != 3)
	{
		printf("Usage#: %s [ip] [port]\n", argv[0]);
		exit(1);
	}

	int listen_sock = statup(argv[1], atoi(argv[2]));

	//创建事件表
	int epfd = epoll_create(128);
	if(epfd < 0)
	{
		perror("epoll_create");
		exit(2);
	}
	
	struct epoll_event event;

	event.events = EPOLLIN;
	event.data.fd = listen_sock;

	//往事件表中增加事件
	epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &event);

	struct epoll_event fd_events[128];
	int size = sizeof(fd_events)/sizeof(fd_events[0]);
	int i = 0;
	for(i = 0; i < size; ++i)
	{
		fd_events[i].events = 0;
		fd_events[i].data.fd = -1;
	}

	int nums = 0;

	int timeout = 5000;

	int done = 0;

	while(!done)
	{
		//返回就绪的文件的个数
		nums = epoll_wait(epfd, fd_events, size, timeout);
		switch(nums)
		{
			case 0:
				printf("timeout...\n");
				break;
			case -1:
				perror("epoll_wait");
				break;
			default:
				{
					for(i = 0; i < nums; ++i)
					{
						int fd = fd_events[i].data.fd;
						if( (fd == listen_sock)  && (fd_events[i].events & EPOLLIN) ) 
						{
							//listen socket
							struct sockaddr_in peer;
							socklen_t len = sizeof(peer);
							int new_sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
							if(new_sock < 0)
							{
								perror("accept");
								continue;
							}
						    
							printf("get a new client, socket-> %s:%d\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
							event.events = EPOLLIN;
							event.data.fd = new_sock;
							//将new_sock添加进内核事件表
							epoll_ctl(epfd, EPOLL_CTL_ADD, new_sock, &event);
						}
						else
						{
							//other socket
							if(fd_events[i].events & EPOLLIN)
							{
								char buf[1024];
								memset(buf, '\0', sizeof(buf));
								//文件描述符为fd的文件就绪
								ssize_t _s = recv(fd, buf, sizeof(buf) - 1, 0);
								if(_s > 0)
								{
									buf[_s] = '\0';
									printf("client#: %s\n", buf);
									event.events = EPOLLOUT;//将fd所感兴趣的事件改为写
									epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &event);
								}
								else if(_s == 0)
								{
									printf("client close...\n");
									epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
									close(fd);
								}
								else
								{
									perror("recv");
									continue;
								}
							}
							else if(fd_events[i].events & EPOLLOUT)
							{
								const char *msg = "HTTP/1.1 200 OK\r\n\r\n<html><h1>hello fucang = =|| </h1></html>\r\n";
								send(fd, msg, strlen(msg), 0);
								epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
								close(fd);
							}
							else
							{	}
						}
					}
				}
				break;
		}
	}

	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值