https://blog.csdn.net/baudgg1992/article/details/51155095
epoll模型主要有2种工作方式:水平触发(LT)和边缘触发(ET),本文主要是关于边缘触发的。本文实现的epoll多线程模型主要是,主线程等待事件触发,然后把相关事件放入队列,线程池从队列中取出数据处理事件。
下面是具体实现:
-
#include <stdarg.h>
-
#include <errno.h>
-
#include <stdio.h>
-
#include <fcntl.h>
-
#include <unistd.h>
-
#include <time.h>
-
#include <sys/types.h>
-
#include <sys/stat.h>
-
#include <sys/epoll.h>
-
#include <sys/sendfile.h>
-
#include <dirent.h>
-
#include <netinet/in.h>
-
#include <sys/socket.h>
-
#include <resolv.h>
-
#include <arpa/inet.h>
-
#include <stdlib.h>
-
#include <signal.h>
-
#include <getopt.h>
-
#include <string.h>
-
#include <string>
-
#include <iostream>
-
#include "ThreadPool.h"
-
using namespace std;
-
static string strIP="192.168.0.168";
-
static int nPort=8088;
-
static string strDir="/home/temp/http_server";
-
const int MAX_EVENT=10;
-
struct epoll_event ev, events[MAX_EVENT];
-
int epfd;
-
enum {NeedRead_Event,Reading_Event,Error_Req,NeedWrite_Event,Writing_Event};
-
struct Task
-
{
-
epoll_event m_oEvent;
-
string m_strFilePath;
-
int m_nCurSize;
-
int m_nType;
-
int m_nReadOrWritefd;
-
int m_nFileLen;
-
int m_nSockfd;
-
};
-
char* dir_up(char *Path)
-
{
-
int len;
-
len = strlen(Path);
-
if (len > 1 && Path[len - 1] == '/')
-
{
-
len--;
-
}
-
while (Path[len - 1] != '/' && len > 1)
-
{
-
len--;
-
}
-
Path[len] = 0;
-
return Path;
-
}
-
char *strsplit(char **s,char del)
-
{
-
char *d, *tok;
-
if (!s || !*s)
-
return NULL;
-
tok = *s;
-
d = strchr(tok, del);
-
if (d) {
-
*d = '\0';
-
*s = d + 1;
-
} else
-
*s = NULL;
-
return tok;
-
}
-
char* urldecode( char* encd, char* decd)
-
{
-
int j,i;
-
char *cd = encd;
-
char p[2];
-
unsigned int num;
-
j=0;
-
for( i = 0; i < strlen(cd); i++ )
-
{
-
memset( p, '\0', 2 );
-
if( cd[i] != '%' )
-
{
-
decd[j++] = cd[i];
-
continue;
-
}
-
p[0] = cd[++i];
-
p[1] = cd[++i];
-
p[0] = p[0] - 48 - ((p[0] >= 'A') ? 7 : 0) - ((p[0] >= 'a') ? 32 : 0);
-
p[1] = p[1] - 48 - ((p[1] >= 'A') ? 7 : 0) - ((p[1] >= 'a') ? 32 : 0);
-
decd[j++] = (p[0] * 16 + p[1]);
-
}
-
decd[j] = '\0';
-
return decd;
-
}
-
void *ReadTask(void *arg)
-
{
-
Task *pTask=(Task*)arg;
-
char *cBuffer=new char[128*1024];
-
if(pTask->m_nType==NeedRead_Event)
-
{
-
int nSize=read(pTask->m_nSockfd,cBuffer,128*1024);
-
if(nSize==0)
-
{
-
close(pTask->m_nSockfd);
-
epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
-
delete pTask;
-
return NULL;
-
}
-
else if(nSize<0)
-
{
-
cout<<errno<<endl;
-
if (errno == EINTR)
-
{
-
pTask->m_oEvent.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
-
epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
-
return NULL;
-
}
-
if (errno == EAGAIN)
-
{
-
return NULL;
-
}
-
}
-
else
-
{
-
char *cMethon=strsplit(&cBuffer,' ');
-
if(cBuffer==NULL)
-
{
-
pTask->m_nType=Error_Req;
-
pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
-
epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
-
return NULL;
-
}
-
char *cUrl=strsplit(&cBuffer,' ');
-
if(cUrl==NULL)
-
{
-
pTask->m_nType=Error_Req;
-
pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
-
epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
-
return NULL;
-
}
-
char *decUrl=new char [strlen(cUrl)];
-
urldecode(cUrl,decUrl);
-
pTask->m_nType=NeedWrite_Event;
-
//pTask->m_strFilePath=UrlDecode(cUrl);
-
pTask->m_strFilePath=decUrl;
-
delete [] decUrl;
-
pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
-
epoll_ctl(epfd, EPOLL_CTL_MOD, pTask->m_nSockfd, &(pTask->m_oEvent));
-
return NULL;
-
}
-
}
-
else
-
{
-
//处理post消息类型或者大文件
-
return NULL;
-
}
-
}
-
const string strError="HTTP/1.1 404 ERROR\r\nServer:SimpleServer\r\nConnection: close\r\nContent-Type:text/html; charset=UTF-8\r\n";
-
void *WriteTask(void *arg)
-
{
-
Task *pTask=(Task*)arg;
-
char cBuffer[128*1024];
-
if(pTask->m_nType==NeedWrite_Event)
-
{
-
struct dirent *pDir;
-
struct stat oStat;
-
DIR *dir;
-
string strFilePath=strDir+pTask->m_strFilePath;
-
if(stat(strFilePath.c_str(),&oStat))
-
{
-
//需要对返回值进行判断,这边不会溢出所以简单处理,实际上需要处理
-
int nSize=sprintf(cBuffer,"HTTP/1.1 200 OK\r\nServer:SimpleServer\r\nConnection: close\r\nContent-Type:text/html; charset=UTF-8\r\n\r\n<html><head><title>%d - %s</title></head>" "<body><font size=+4>Linux directory access server</font><br><hr width=\"100%%\"><br><center>" "<table border cols=3 width=\"100%%\">", errno, strerror(errno));
-
nSize+=sprintf(cBuffer+nSize,"</table><font color=\"CC0000\" size=+2>Please contact the administrator consulting why appear as follows error message:\n%s %s</font></body></html>", pTask->m_strFilePath.c_str(), strerror(errno));
-
int nWriteCount=write(pTask->m_nSockfd,cBuffer,nSize);
-
/*
-
if(nWriteCount<0)
-
{
-
if(errno == EAGAIN)
-
{
-
pTask->m_nType=Writing_Event;
-
pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
-
epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
-
}
-
}
-
*/
-
close(pTask->m_nSockfd);
-
epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
-
delete pTask;
-
return NULL;
-
}
-
if(S_ISREG(oStat.st_mode))
-
{
-
pTask->m_nReadOrWritefd = open(strFilePath.c_str(), O_RDONLY);
-
pTask->m_nFileLen = lseek(pTask->m_nReadOrWritefd, 0, SEEK_END);
-
lseek(pTask->m_nReadOrWritefd, 0, SEEK_SET);
-
int nSize=sprintf(cBuffer,"HTTP/1.1 200 OK\r\nServer:SimpleServer\r\nConnection: keep-alive\r\nContent-type: application/*\r\nContent-Length:%d\r\n\r\n",pTask->m_nFileLen);
-
write(pTask->m_nSockfd,cBuffer,nSize);
-
pTask->m_nType=Writing_Event;
-
pTask->m_nCurSize=0;
-
pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
-
epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
-
}
-
else if(S_ISDIR(oStat.st_mode))
-
{
-
int nSize=0;
-
dir=opendir(strFilePath.c_str());
-
nSize+=sprintf(cBuffer+nSize, "HTTP/1.1 200 OK\r\nServer:SimpleServer\r\nConnection: close\r\nContent-Type:text/html; charset=UTF-8\r\n\r\n<html><head><title>%s</title></head>"
-
"<body><font size=+4>Linux directory access server</font><br><hr width=\"100%%\"><br><center>"
-
"<table border cols=3 width=\"100%%\">", strFilePath.c_str());
-
nSize+=sprintf(cBuffer+nSize, "<caption><font size=+3>dir %s</font></caption>\n",strFilePath.c_str());
-
nSize+=sprintf(cBuffer+nSize, "<tr><td>name</td><td>大小</td><td>change time</td></tr>\n");
-
if(dir==NULL)
-
{
-
nSize+=sprintf(cBuffer+nSize,"</table><font color=\"CC0000\" size=+2>%s</font></body></html>",strerror(errno));
-
write(pTask->m_nSockfd,cBuffer,nSize);
-
return NULL;
-
}
-
while((pDir=readdir(dir))!=NULL)
-
{
-
string strFileName=string(pTask->m_strFilePath.c_str())+"/"+string(pDir->d_name);
-
nSize+=sprintf(cBuffer+nSize,"<tr>");
-
string strDirFilePath=strFilePath+"/"+pDir->d_name;
-
if(stat(strDirFilePath.c_str(),&oStat)==0)
-
{
-
if(strcmp(pDir->d_name, "..") == 0)
-
{
-
char path[PATH_MAX];
-
strcpy(path,pTask->m_strFilePath.c_str());
-
nSize+=sprintf(cBuffer+nSize,"<td><a href=\"http://%s:%d%s\">..</a></td>",strIP.c_str(), nPort,dir_up(path));
-
}
-
else if(strcmp(pDir->d_name,".")==0)
-
{
-
nSize+=sprintf(cBuffer+nSize,"<td><a href=\"http://%s:%d%s\">.</a></td>",strIP.c_str(),nPort, pTask->m_strFilePath.c_str());
-
}
-
else
-
{
-
nSize+=sprintf(cBuffer+nSize,"<td><a href=\"http://%s:%d%s\">%s</a></td>", strIP.c_str(),nPort, strFileName.c_str(),pDir->d_name);
-
}
-
if (S_ISDIR(oStat.st_mode))
-
{
-
nSize+=sprintf(cBuffer+nSize, "<td>dir</td>");
-
}
-
else if (S_ISREG(oStat.st_mode))
-
{
-
nSize+=sprintf(cBuffer+nSize, "<td>%d</td>", oStat.st_size);
-
}
-
else if (S_ISLNK(oStat.st_mode))
-
{
-
nSize+=sprintf(cBuffer+nSize, "<td>interlinkage</td>");
-
}
-
else if (S_ISCHR(oStat.st_mode))
-
{
-
nSize+=sprintf(cBuffer+nSize, "<td>char device</td>");
-
}
-
else if (S_ISBLK(oStat.st_mode))
-
{
-
nSize+=sprintf(cBuffer+nSize, "<td>chunk device</td>");
-
}
-
else if (S_ISFIFO(oStat.st_mode))
-
{
-
nSize+=sprintf(cBuffer+nSize, "<td>FIFO</td>");
-
}
-
else if (S_ISSOCK(oStat.st_mode))
-
{
-
nSize+=sprintf(cBuffer+nSize, "<td>Socket</td>");
-
}
-
else
-
{
-
nSize+=sprintf(cBuffer+nSize, "<td>(unknow)</td>");
-
nSize+=sprintf(cBuffer+nSize, "<td>%s</td>", ctime(&oStat.st_ctime));
-
}
-
}
-
nSize+=sprintf(cBuffer+nSize, "</tr>\n");
-
}
-
closedir(dir);
-
nSize+=sprintf(cBuffer+nSize, "</tr>\n");
-
write(pTask->m_nSockfd,cBuffer,nSize);
-
close(pTask->m_nSockfd);
-
epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
-
delete pTask;
-
}
-
else
-
{
-
int nSize=0;
-
nSize+=sprintf(cBuffer+nSize,"HTTP/1.1 200 OK\r\nServer:SimpleServer\r\nConnection: close\r\nContent-Type:text/html; charset=UTF-8\r\n<html><head><title>permission denied</title></head>" "<body><font size=+4>Linux directory access server</font><br><hr width=\"100%%\"><br><center>" "<table border cols=3 width=\"100%%\">");
-
nSize+=sprintf(cBuffer+nSize,"</table><font color=\"CC0000\" size=+2>You visit resources '%s' be under an embargo,Please contact the administrator to solve!</font></body></html& gt;", pTask->m_strFilePath.c_str());
-
write(pTask->m_nSockfd,cBuffer,nSize);
-
close(pTask->m_nSockfd);
-
epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
-
delete pTask;
-
}
-
}
-
else if(pTask->m_nType==Writing_Event)
-
{
-
int nSize=128*1024;
-
if(pTask->m_nFileLen-pTask->m_nCurSize<128*1024)
-
{
-
nSize=pTask->m_nFileLen-pTask->m_nCurSize;
-
}
-
nSize=sendfile(pTask->m_nSockfd,pTask->m_nReadOrWritefd,NULL,nSize);
-
pTask->m_nCurSize+=nSize;
-
if(pTask->m_nCurSize!=pTask->m_nFileLen)
-
{
-
pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
-
epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
-
}
-
else
-
{
-
close(pTask->m_nReadOrWritefd);
-
close(pTask->m_nSockfd);
-
epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
-
delete pTask;
-
}
-
}
-
else if(pTask->m_nType==Error_Req)
-
{
-
write(pTask->m_nSockfd,strError.c_str(),strError.size());
-
close(pTask->m_nSockfd);
-
epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
-
delete pTask;
-
}
-
return NULL;
-
}
-
void setnonblocking(int sock)
-
{
-
int opts;
-
opts = fcntl(sock, F_GETFL);
-
if (opts < 0)
-
{
-
cout<<"fcntl(sock,F_GETFL) error"<<endl;
-
return ;
-
}
-
opts = opts | O_NONBLOCK;
-
if (fcntl(sock, F_SETFL, opts) < 0)
-
{
-
cout<<"fcntl(sock,F_GETFL) error"<<endl;
-
return ;
-
}
-
}
-
int main()
-
{
-
ThreadPool mPool;
-
//signal(SIGPIPE,SIG_IGN);
-
//signal(SIGCHLD,SIG_IGN);
-
epfd = epoll_create(MAX_EVENT);
-
struct sockaddr_in addr;
-
int sock_fd,addrlen;
-
if((sock_fd=socket(PF_INET,SOCK_STREAM,0))<0)
-
{
-
cout<<"socket init failed..."<<endl;
-
}
-
addrlen = 1;
-
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &addrlen, sizeof(addrlen));
-
setnonblocking(sock_fd);
-
addr.sin_family=AF_INET;
-
addr.sin_port=htons(nPort);
-
addr.sin_addr.s_addr=htonl(INADDR_ANY);
-
addrlen=sizeof(struct sockaddr_in);
-
if(bind(sock_fd,(struct sockaddr*)&addr,addrlen)<0)
-
{
-
cout<<"bind failed..."<<endl;
-
}
-
if(listen(sock_fd,MAX_EVENT)<0)
-
{
-
cout<<"listen failed..."<<endl;
-
}
-
cout<<"server start..."<<endl;
-
ev.data.fd = sock_fd;
-
// 设置要处理的事件类型
-
ev.events = EPOLLIN | EPOLLET;
-
// 注册 epoll 事件
-
epoll_ctl(epfd, EPOLL_CTL_ADD, sock_fd, &ev);
-
struct sockaddr_in clientaddr ;
-
for (;;)
-
{
-
int nfds = epoll_wait(epfd, events, MAX_EVENT, -1);
-
for(int i=0;i<nfds;++i)
-
{
-
if(events[i].data.fd==sock_fd)
-
{
-
int connfd;
-
socklen_t clilen = sizeof(clientaddr);
-
connfd = accept(sock_fd, (sockaddr *)&clientaddr, &clilen);
-
if(connfd == -1)
-
{
-
cout<<"accept error:"<<errno<<endl;
-
continue;
-
}
-
else
-
{
-
cout<<"connect from:"<<inet_ntoa(clientaddr.sin_addr)<<":"<<ntohs(clientaddr.sin_port)<<endl;
-
}
-
setnonblocking(connfd);
-
int nRecvBuf=128*1024;
-
setsockopt(connfd, SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
-
int nSendBuf=128*1024;
-
setsockopt(connfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
-
int nNetTimeout=3000;
-
setsockopt(connfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&nNetTimeout, sizeof(int));
-
setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&nNetTimeout, sizeof(int));
-
Task *pTask=new Task;
-
pTask->m_nType=NeedRead_Event;
-
pTask->m_nSockfd=connfd;
-
pTask->m_oEvent.events = EPOLLIN | EPOLLET |EPOLLONESHOT;
-
pTask->m_oEvent.data.ptr = pTask;
-
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &(pTask->m_oEvent));
-
}
-
else if(events[i].events & EPOLLIN)
-
{
-
mPool.AddWorker(ReadTask,events[i].data.ptr);
-
}
-
else if(events[i].events & EPOLLOUT)
-
{
-
mPool.AddWorker(WriteTask,events[i].data.ptr);
-
}
-
}
-
}
-
}