Blog.csdn.net/baudgg1992/…
Epoll model mainly has two working modes: horizontal triggering (LT) and edge triggering (ET). This paper mainly focuses on edge triggering. The epoll multithreading model implemented in this paper is that the main thread waits for events to be triggered, and then puts related events into a queue, and the thread pool takes out data from the queue to process events.
Here is the implementation:
Copy the code
#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;
The 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
{
// Handle POST message types or large files
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))
{
// We need to check the return value, there is no overflow here, so simple processing, actually need processing
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 > size < / 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;
// Sets the type of event to handle
ev.events = EPOLLIN | EPOLLET;
// Register epoll events
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);
}
}
}
}