在linux下我们都知道可以使用select模型实现异步通信,但是使用select有限制,最多只能管理1024个端口,而epoll可以管理65535个端口,同时epoll比select效率更高,缺点是select支持跨平台windows下也可以使用而epoll是linux专属,windows下有类似于epoll的解决方案即IOCP(I/O Completion Port),常称I/O完成端口。下面是epoll示例代码:
#include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#define MAXLINE 100
#define OPEN_MAX 100
#define LISTENQ 20
#define INFTIM 1000
using namespace std;
void setnonblocking(int sock)
{
int opts;
opts = fcntl(sock, F_GETFL);
if(opts < 0)
{
perror("fcntl(sock, GETFL)");
exit(1);
}
opts = opts | O_NONBLOCK;
if(fcntl(sock, F_SETFL, opts) < 0)
{
perror("fcntl(sock,SETFL,opts)");
exit(1);
}
}
int main()
{
int i, maxi, listenfd, connfd, sockfd, epfd, nfds;
ssize_t n;
char line[MAXLINE];
socklen_t clientaddr_len;
struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;
listenfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
perror("create socket failure!\n");
exit(-1);
}
//准备服务器地址
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(12345);
inet_aton("10.11.1.250", &serveraddr.sin_addr);
//绑定
int res = bind(listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
if(res==-1)
{
perror("绑定失败\n");
exit(-1);
}
else
{
printf("绑定成功\n");
}
//监听
if(listen(listenfd, 100)==-1)
{
perror("监听失败\n");
exit(-1);
}
printf("监听启动\n");
//声明epoll_event结构体的变量, ev用于注册事件, events数组用于回传要处理的事件
struct epoll_event ev, events[20];
//生成用于处理epoll专用的文件描述符, 参数256可随意,现在该参数的作用被遗弃。
epfd = epoll_create(256);
ev.data.fd = listenfd; //设置epoll管理的socket,
ev.events = EPOLLIN | EPOLLET; //设置要处理的事件类型为可读事件,并且工作方式为边沿触发
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); //注册epoll事件
for( ; ; )
{
nfds = epoll_wait(epfd, events, 20, 500); //等待epoll事件的发生
for(i = 0; i < nfds; ++i) //处理所发生的所有事件
{
if(events[i].data.fd == listenfd) //监听事件
{
clientaddr_len = sizeof(clientaddr);
connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddr_len);
if(connfd < 0)
{
perror("connfd<0");
exit(1);
}
//setnonblocking(connfd); //把客户端的socket设置为非阻塞方式
char *str = inet_ntoa(clientaddr.sin_addr);
std::cout << "connect from " << str <<std::endl;
ev.data.fd = connfd; //设置epoll管理的socket
ev.events = EPOLLIN | EPOLLOUT | EPOLLET; //设置要处理的事件类型为可读事件,并且工作方式为边沿触发
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);//注册epoll事件
}
else
{
if( events[i].events & EPOLLIN ) //读事件
{
if ( (sockfd = events[i].data.fd) < 0)
{
continue;
}
memset(line, 0x00, MAXLINE);
if ( (n = read(sockfd, line, MAXLINE)) < 0)
{
printf("readsize = %d\n", n);
std::cout<<"readline error"<<std::endl;
}
else if (n == 0)
{
printf("client close the socket!\n");
close(sockfd);
}
else
{
char *str = inet_ntoa(clientaddr.sin_addr);
printf("recv data: %s, from %s\n", line, str);
}
}
if( events[i].events & EPOLLOUT )//写事件
{
sockfd = events[i].data.fd;
write(sockfd, "hello epoll example", strlen("hello epoll example"));
}
}
}
}
return 0;
}