I/O多路转接(三)——epoll函数
第三篇介绍epoll函数。
介绍先略过,还在整理当中,先贴代码。
使用epoll LT与ET模式的网路服务器
LT
这是一个简单的服务器,三次握手后只完成一次读写就关闭连接。通过浏览器发送一个HTTP请求,服务器传回HELLO EPOLL消息。
server.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
const char *msg = "HTTP/1.1 200 OK\r\n\r\n<html><h1>Hello epoll!</h1></html>";
int startup(char*ip,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0){
perror("socket");
return 2;
}
int opt = 1;
setsockopt(sock, SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
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");
return 3;
}
if(listen(sock,10) < 0){
perror("listen");
return 4;
}
return sock;
}
int main(int argc,char *argv[])
{
if(argc!= 3){
printf("Usage:%s [ip] [port]\n",argv[0]);
return 1;
}
int listen_sock = startup(argv[1],atoi(argv[2]));
printf("listen_sock has been created,the value is %d\n",listen_sock);
//监听套接字创建完成.
int epfd = epoll_create(256);//创建epoll模型,返回一个epoll模型句柄。
if(epfd < 0){
perror("epoll_create");
return 5;
}
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev);//将listen_sock添加到rb_tree;
int nums = -1;
int timeout = 1000;
struct epoll_event revs[64];//epoll事件集
int size = 64;
while(1){
nums = epoll_wait(epfd,revs,size,-1);
switch(nums){
case 0:
printf("timeout\n");
break;
case -1:
perror("epoll_wait");
break;
default:
{
//已经有事件就绪。
int i= 0;
for(;i < nums;i++){
int fd = revs[i].data.fd;
if(fd==listen_sock&&revs[i].events&EPOLLIN{
//listen_sock读事件已经就绪。
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
if(new_sock < 0){
perror("accept");
continue;
}
printf("get a client!ip:%s,port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
//将CLIENT的socket添加到epoll中
ev.events = EPOLLIN;
ev.data.fd = new_sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,new_sock,&ev);
}else if(fd!= listen_sock){//其他关心描述符状态改变。
if(revs[i].events&EPOLLIN){
char buf[4096];
ssize_t s = read(fd,buf,sizeof(buf)-1);
if(s >0){
buf[s]= 0;
printf("client say# %s\n",buf);
ev.events = EPOLLOUT;//读后关心这个fd的写事件。
ev.data.fd = fd;
epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
}else if(s==0){
printf("client is quit!\n");
close(fd);
epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
}else{
perror("read");
close(fd);
epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
}
}else if(revs[i].events&EPOLLOUT){
write(fd,msg,strlen(msg));
close(fd);
epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
}
}
}
}
break;
}
}
使用epoll ET模式的网路服务器
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<errno.h>
#include<fcntl.h>
const char *msg = "HTTP/1.1 200 OK\r\n\r\n<html><h1>Hello epoll!</h1></html>";
struct epoll_event ev;
#define SIZE 64
typedef struct fd_buff{
int fd;
char buf[SIZE];
int size;
}fdbuff,*fdbuff_p;
void* alloc_buff(int fd)
{
fdbuff_p tmp =(fdbuff_p)malloc(sizeof(fdbuff));
if(!tmp){
perror("malloc");
return NULL;
}
tmp->fd = fd;
tmp->size = SIZE;
return tmp;
}
int startup(char*ip,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0){
perror("socket");
return 2;
}
int opt = 1;
setsockopt(sock, SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
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");
return 3;
}
if(listen(sock,10) < 0){
perror("listen");
return 4;
}
return sock;
}
void set_nonblock(int fd)//设置套接字为非阻塞。
{
int fl = fcntl(fd, F_GETFL);//拿到当前套接字状态保存到fl
fcntl(fd,F_SETFL,fl|O_NONBLOCK);//设置为非阻塞态
}
int myread(int fd, char buf[],int size)
{
ssize_t len = 0;
ssize_t total = 0;
int count = 0;
while((len = read(fd,buf+total,100))> 0){
count++;
total += len;
printf("read %d!msg:%s total = %d\n",count,buf,total);
}
if(len < 0||errno== EAGAIN)
printf("read done! total = %d\n",total);
else if(len==0)
return 0;
else
return -1;
return total;
}
int mywrite(int fd, char buf[],int size)
{
ssize_t len = 0;
ssize_t total = 0;
int count = 0;
while(1){
len = write(fd,buf+total,strlen(buf+total));
if(len > 0)
total += len;
if(len < 0||errno== EAGAIN){
printf("write done! total = %d\n",total);
break;
}
}
return total;
}
int myaccept(int listen_sock,int epfd)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = 0;
int count = 0;
while(( new_sock = accept(listen_sock,(struct sockaddr*)&client,&len))>0){
count++;
printf("get a client!ip:%s,port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
//将CLIENT的socket添加到epoll中
ev.events = EPOLLIN|EPOLLET;
set_nonblock(new_sock);
ev.data.ptr = alloc_buff(new_sock);
epoll_ctl(epfd,EPOLL_CTL_ADD,new_sock,&ev);
}
if(new_sock < 0&&errno==EAGAIN){
printf("server has accept all client,count= %d\n",count);
}
}
void del_buff(fdbuff_p p)
{
if(p!=NULL)
free(p);
}
int main(int argc,char *argv[])
{
if(argc!= 3){
printf("Usage:%s [ip] [port]\n",argv[0]);
return 1;
}
int listen_sock = startup(argv[1],atoi(argv[2]));
set_nonblock(listen_sock);
printf("listen_sock has been created,the value is %d\n",listen_sock);
//监听套接字创建完成.
int epfd = epoll_create(256);//创建epoll模型,返回一个epoll模型句柄。
if(epfd < 0){
perror("epoll_create");
return 5;
}
ev.events = EPOLLIN|EPOLLET;
ev.data.ptr =alloc_buff(listen_sock);
epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev);//将listen_sock添加到rb_tree;
int nums = -1;
int timeout = 1000;
struct epoll_event revs[64];//epoll事件集
int size = 64;
while(1){
nums = epoll_wait(epfd,revs,size,-1);
switch(nums){
case 0:
printf("timeout\n");
break;
case -1:
perror("epoll_wait");
break;
default:
{
//已经有事件就绪。
int i= 0;
for(;i < nums;i++){
fdbuff_p fp =(fdbuff_p)revs[i].data.ptr;
if(fp->fd == listen_sock&&revs[i].events&EPOLLIN ){//listen_sock读事件已经就绪。
myaccept(listen_sock, epfd);
}else if(fp->fd!= listen_sock){//其他关心描述符状态改变。
if(revs[i].events&EPOLLIN){
ssize_t s = myread(fp->fd,fp->buf,fp->size);
if(s >0){
fp->buf[s]=0;
printf("client say# %s\n",fp->buf);
ev.events = EPOLLOUT|EPOLLET;//读后关心这个fd的写事件。
ev.data.ptr = fp;
epoll_ctl(epfd,EPOLL_CTL_MOD,fp->fd,&ev);
}else if(s==0){
printf("client is quit!\n");
close(fp->fd);
epoll_ctl(epfd,EPOLL_CTL_DEL,fp->fd,NULL);
del_buff(fp);
}else{
perror("read");
close(fp->fd);
epoll_ctl(epfd,EPOLL_CTL_DEL,fp->fd,NULL);
del_buff(fp);
}
}else if(revs[i].events&EPOLLOUT){
mywrite(fp->fd,fp->buf,fp->size);
close(fp->fd);
epoll_ctl(epfd,EPOLL_CTL_DEL,fp->fd,NULL);
//del_buff(fp);
}
}
}
}
break;
}
}
return 0;
}