目录
epoll系统调用内核事件表epoll_wait函数LT和ET模式epoll编程实例epoll编程流程头文件tcp_socket.h头文件tcp_epoll.h主文件 tcp_epoll.cpp
参考文献
epoll系统调用
内核事件表
跟select和poll不同,epoll使用一组函数来完成任务,而不是单个函数。epoll把用户关心的文件描述符上的事件放在内核里的一个事件表,
但是,epoll需要使用一个额外的文件描述符来唯一标识内核中的这个事件表。
文件描述符由以下函数创建:
#include <sys/epoll.h>
int epoll_create(int size
);
下面的函数用来操作epoll的内核事件表:
#include <sys/epoll.h>
int epoll_ctl(int epfd
, int op
, int fd
, struct epoll_event
* 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
;
fd参数是要操作得到文件描述符op参数指定操作类型 event参数指定事件,是epoll_event结构指针类型: events成员描述事件类型,与poll基本相同,但是宏前要加‘E’。 data成员用于存储用户数据,一般只使用fd。
epoll_wait函数
epoll_wait函数在一段超时时间内等待一组文件描述符上的事件。
#include <sys/epoll.h>
int epoll_wait(int epfd
, struct epoll_event
* events
, int maxevents
, int timeout
);
maxevents参数指定最多监听多少个事件且必须大于0
epoll_wait函数如果检测到事件,就将所有就绪的事件从内核事件表中复制到它的第二个参数events指向 的数组中。这个数组只用于输出epoll_wait检测到的就绪事件。
LT和ET模式
epoll对文件描述符的操作有两种:LT和ET。 LT相当于高效的poll,ET才是epoll的高效工作模式。
对于采用LT工作模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。这样,当应用程序下一次调用epoll_wait 时,epoll_wait还会再次向应用程序通告此事件,直到该事件被处理。而对于采用ET工作模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应 用程序后,应用程序必须立即处理该事件,因为后续的epoll_ wait 调用将不再向应用程序通知这一事件。
epoll编程实例
epoll编程流程
头文件tcp_socket.h
#include<iostream>
#include<assert.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#define BUFF_SIZE 128
using namespace std
;
class Socket
{
public:
Socket()
{
sockfd_
= socket(PF_INET
, SOCK_STREAM
, 0);
assert(sockfd_
>= 0);
}
int Get_socket()
{
return sockfd_
;
}
~Socket()
{
close(sockfd_
);
}
protected:
int sockfd_
;
};
class Socket_Ser :public Socket
{
public:
Socket_Ser(const char* ip
, int port
= 6000, int backlog
= 5)
{
struct sockaddr_in address
;
memset(&address
, 0, sizeof(address
));
address
.sin_family
= AF_INET
;
address
.sin_port
= htons(port
);
address
.sin_addr
.s_addr
= inet_addr(ip
);
int ret
= bind(sockfd_
, (struct sockaddr
*)&address
, sizeof(address
));
assert(ret
!= -1);
ret
= listen(sockfd_
, backlog
);
assert(ret
!= -1);
}
int Accept()
{
struct sockaddr_in client
;
socklen_t len
= sizeof(client
);
int connfd
= accept(sockfd_
, (struct sockaddr
*)&client
, &len
);
return connfd
;
}
int Recv(int fd
, char* buffer
, int size
)
{
int ret
= recv(fd
, buffer
, size
- 1, 0);
if (ret
== -1 || (strncmp(buffer
, "end", 3) == 0))
{
return -1;
}
}
void Send(int fd
, const char* buffer
, int size
)
{
send(fd
, buffer
, size
, 0);
}
void Close_client(int fd
)
{
close(fd
);
}
};
class Socket_Cli :public Socket
{
public:
Socket_Cli(const char* ip
, int port
= 6000)
{
struct sockaddr_in address
;
memset(&address
, 0, sizeof(address
));
address
.sin_family
= AF_INET
;
address
.sin_port
= htons(port
);
address
.sin_addr
.s_addr
= inet_addr(ip
);
int ret
= connect(sockfd_
, (struct sockaddr
*)&address
, sizeof(address
));
assert(ret
!= -1);
}
int Send(char* buffer
, int size
)
{
send(sockfd_
, buffer
, size
, 0);
}
};
头文件tcp_epoll.h
#include"tcp_socket.h"
#include<sys/epoll.h>
#include <fcntl.h>
#define MAXEVENTS 100
class Epoll
{
public:
Epoll(const char* ip
, int epoll_size
= 5, int port
= 6000, int backlog
= 5) :ser(ip
, port
, backlog
)
{
epollfd
= epoll_create(epoll_size
);
assert(epollfd
!= -1);
Insert(ser
.Get_socket(), EPOLLIN
);
}
int Epoll_Deal(int flag
)
{
struct epoll_event events
[MAXEVENTS
];
int n
= epoll_wait(epollfd
, events
, MAXEVENTS
, -1);
if (n
<= 0)
{
cout
<< "epoll errno!" << endl
;
return -1;
}
Deal_connect(n
, events
,flag
);
return n
;
}
private:
void Deal_connect(int n
,struct epoll_event
* events
,int flag
)
{
for (int i
= 0; i
< n
; i
++)
{
int fd
= events
[i
].data
.fd
;
if (fd
== ser
.Get_socket())
{
Accept(flag
);
}
else
{
if (events
[i
].events
& EPOLLRDHUP
)
{
Delete(events
[i
].data
.fd
);
}
else
{
char buffer
[128] = { 0 };
ser
.Recv(events
[i
].data
.fd
, buffer
, 128);
cout
<< "recv form " << events
[i
].data
.fd
<< ":" << buffer
<< endl
;
}
}
}
}
void Accept(int flag
)
{
int connfd
= ser
.Accept();
Insert(connfd
, EPOLLIN
| EPOLLRDHUP
);
if (flag
== O_NONBLOCK
)
{
SetUnblock(connfd
);
}
}
void Delete(int fd
)
{
ser
.Close_client(fd
);
epoll_ctl(epollfd
, EPOLL_CTL_DEL
, fd
,NULL);
}
void Insert(int fd
, int events
)
{
struct epoll_event event_
;
event_
.events
= events
;
event_
.data
.fd
= fd
;
int res
= epoll_ctl(epollfd
, EPOLL_CTL_ADD
, fd
, &event_
);
}
void SetUnblock(int fd
)
{
int oldoption
= fcntl(fd
, F_GETFL
);
int newoption
= oldoption
| O_NONBLOCK
;
fcntl(fd
, F_SETFL
, newoption
);
}
private:
int epollfd
;
Socket_Ser ser
;
};
主文件 tcp_epoll.cpp
#include "tcp_epoll.h"
int main(int argc
,char* argv
[])
{
if (argc
<= 1)
{
cout
<< "errno! please input again" << endl
;
}
Epoll
mypoll(argv
[1]);
while (1)
{
mypoll
.Epoll_Deal();
}
}
参考文献
[1]游双.Linux高性能服务器编程.机械工业出版社,2043.5.