linux中网络编程I/O模型—epoll(异步I/O)
- epoll(异步I/O)模型使用,使用epoll模型需要用到一下3个函数:
//创建一个epoll的句柄,size用来告诉系统内核要监听的文件描述符的数量
//注:使用完epoll_create创建的句柄后需要close
int epoll_create(int size);
//epoll_ctl注册需要监听的事件类型。参数列表:
epfd:epoll_create函数的返回值
opt:可取值
值 | 描述 |
---|---|
EPOLL_CTL_ADD | 注册新的文件描述符到epfd中 |
EPOLL_CTL_MOD | 修改已经注册的文件描述符事件 |
EPOLL_CTL_DEL | 删除已经注册的文件描述符 |
events:参数结构如下
struct epoll_event{
__uint32_t events; //epoll events
epoll_data_t data; //user data avariable
}
其中events可取值如下:
值 | 描述 |
---|---|
EPOLLIN | 可读 |
EPOLLOUT | 可写 |
EPOLLPRI | 有紧急数据可读 |
EPOLLERR | 文件描述符发生错误 |
EPOLLHUP | 对应的文件描述符被挂断 |
EPOLLET | 将EPOLL设为边缘触发模式,相对于水平触发模式而言 |
EPOLLONESHOT | 只监听一次事件,当监听完成这次事件之后,如果还需要继续监听这个socket,需要吧这个socket添加到EPOLL、队列中 |
int epoll_ctl(int epfd, int opt, int fd, struct epoll_event *events);
//等待事件发生
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
- 使用epoll模型实现server、client
server.cpp
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>#define IPADDRESS "127.0.0.1"
#define PORT 6666
#define MAXSIZE 1024
#define LISTENQ 5
#define FDSIZE 1000
#define EPOLLEVENTS 100//创建socket并绑定
int socket_bind(const char *ip, int port);
//IO 多路复用epoll
void do_epoll(int listenfd);
//事件处理
void handle_events(int epollfd, struct epoll_event *events, int num, int listenfd, char *buffer);
//处理新连接
void handle_accept(int epollfd, int listenfd);
//读处理
void do_read(int epollfd, int fd, char *buffer);
//写处理
void do_write(int epollfd, int fd, char *buffer);
//添加事件
void add_event(int epollfd, int fd, int state);
//修改事件
void modify_event(int epollfd, int fd, int state);
//删除事件
void delete_event(int epollfd, int fd, int state);int main()
{
int listenfd;listenfd = socket_bind(IPADDRESS, PORT);listen(listenfd, LISTENQ);do_epoll(listenfd);return 0;
}int socket_bind(const char *ip, int port)
{
int listenfd;if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket");exit(1);}struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(port);inet_pton(AF_INET, ip, &server.sin_addr);if (bind(listenfd, (struct sockaddr*)&server, sizeof(struct sockaddr)) == -1){
perror("bind");exit(2);}return listenfd;
}void do_epoll(int listenfd)
{
int epollfd;struct epoll_event events[EPOLLEVENTS];int iret;char buffer[MAXSIZE];memset(buffer, 0, MAXSIZE);//创建监听描述符epollfd = epoll_create(FDSIZE);//添加监听描述符事件add_event(epollfd, listenfd, EPOLLIN);while (1){
//获取已经准备好的描述符事件iret = epoll_wait(epollfd, events, EPOLLEVENTS, -1);handle_events(epollfd, events, iret, listenfd, buffer);}close(epollfd);
}void handle_events(int epollfd, epoll_event *events, int num, int listenfd, char *buffer)
{
int i;int fd;//进行选好遍历for (i = 0; i < num; i++){
fd = events[i].data.fd;//根据描述符的事件类型进行处理if (fd == listenfd && events[i].events & EPOLLIN){
handle_accept(epollfd, listenfd);}else if (events[i].events & EPOLLIN){
do_read(epollfd, fd, buffer);}else if (events[i].events & EPOLLOUT){
do_write(epollfd, fd, buffer);}}
}void handle_accept(int epollfd, int listenfd)
{
int clientfd;struct sockaddr_in client;socklen_t socklen;if ((clientfd = accept(listenfd, (struct sockaddr*)&client, &socklen)) == -1){
perror("accept");}else{
printf("accept a new client : %s : %d\n", inet_ntoa(client.sin_addr), client.sin_port);add_event(epollfd, clientfd, EPOLLIN);}
}void do_read(int epollfd, int fd, char *buffer)
{
int nread;nread = read(fd, buffer, MAXSIZE);if (nread == -1){
perror("read");close(fd);delete_event(epollfd, fd, EPOLLIN);}else if (nread == 0){
fprintf(stderr, "client close.\n");close(fd);delete_event(epollfd, fd, EPOLLIN);}else{
printf("read message is : %s\n", buffer);//将描述符的对应事件由读改成写modify_event(epollfd, fd, EPOLLOUT);}
}void do_write(int epollfd, int fd, char *buffer)
{
int nwrite;nwrite = write(fd, buffer, strlen(buffer));if (nwrite == -1){
perror("write error : ");close(fd);delete_event(epollfd, fd, EPOLLOUT);}else{
modify_event(epollfd, fd, EPOLLIN);}memset(buffer, 0, MAXSIZE);
}void add_event(int epollfd, int fd, int state)
{
struct epoll_event ev;ev.events = state;ev.data.fd = fd;epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
}void modify_event(int epollfd, int fd, int state)
{
struct epoll_event ev;ev.events = state;ev.data.fd = fd;epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);
}void delete_event(int epollfd, int fd, int state)
{
struct epoll_event ev;ev.events = state;ev.data.fd = fd;epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
}
client.cpp
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MAXSIZE 1024
#define IPADDRESS "127.0.0.1"
#define PORT 6666
#define FDSIZE 1024
#define EPOLLEVENTS 20void handle_connection(int sockfd);
void handle_event(int epollfd, struct epoll_event *events, int num, int sockfd, char *buffer);
void do_read(int epoolfd, int fd, int sockfd, char *buffer);
void do_write(int epollfd, int fd, int sockfd, char *buffer);
void add_event(int epollfd, int fd, int state);
void modify_event(int epollfd, int fd, int state);
void delete_event(int epollfd, int fd, int state);int count = 0;int main()
{
int sockfd;sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in client;client.sin_family = AF_INET;client.sin_port = htons(PORT);inet_pton(AF_INET, IPADDRESS, &client.sin_addr);connect(sockfd, (struct sockaddr*)&client, sizeof(client));handle_connection(sockfd);close(sockfd);return 0;
}void handle_connection(int sockfd)
{
int epollfd;struct epoll_event events[EPOLLEVENTS];char buffer[MAXSIZE];int iret;epollfd = epoll_create(FDSIZE);//add_event(epollfd, sockfd, EPOLLIN | EPOLLOUT);add_event(epollfd, STDIN_FILENO, EPOLLIN);while (1){
iret = epoll_wait(epollfd, events, EPOLLEVENTS, -1);handle_event(epollfd, events, iret, sockfd, buffer);}close(epollfd);
}void handle_event(int epollfd, epoll_event *events, int num, int sockfd, char *buffer)
{
int fd;int i;for (i = 0; i < num; i++){
fd = events[i].data.fd;if (events[i].events & EPOLLIN)do_read(epollfd, fd, sockfd, buffer);else if (events[i].events & EPOLLOUT)do_write(epollfd, fd, sockfd, buffer);}
}void do_read(int epollfd, int fd, int sockfd, char *buffer)
{
int nread;nread = read(fd, buffer, MAXSIZE);if (nread == -1){
perror("read error");close(fd);}else if (nread == 0){
fprintf(stderr, "server close.\n");close(fd);}else{
if (fd == STDIN_FILENO)add_event(epollfd, sockfd, EPOLLOUT);else{
delete_event(epollfd, sockfd, EPOLLIN);add_event(epollfd, STDOUT_FILENO, EPOLLOUT);}}
}void do_write(int epollfd, int fd, int sockfd, char *buffer)
{
int nwrite;char temp[100];buffer[strlen(buffer) - 1] = '\0';snprintf(temp, sizeof(temp), "%s_%02d\n", buffer, count++);nwrite = write(fd, temp, strlen(temp));if (nwrite == -1){
perror("write error");close(fd);}else{
if (fd == STDOUT_FILENO)delete_event(epollfd, fd, EPOLLOUT);elsemodify_event(epollfd, fd, EPOLLIN);}memset(buffer, 0, MAXSIZE);
}void add_event(int epollfd, int fd, int state)
{
struct epoll_event ev;ev.events = state;ev.data.fd = fd;epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
}void modify_event(int epollfd, int fd, int state)
{
struct epoll_event ev;ev.events = state;ev.data.fd = fd;epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);
}void delete_event(int epollfd, int fd, int state)
{
struct epoll_event ev;ev.events = state;ev.data.fd = fd;epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
}