基于的epoll模型的简单http服务器

161 阅读6分钟

blog.csdn.net/baudgg1992/…

 epoll模型主要有2种工作方式:水平触发(LT)和边缘触发(ET),本文主要是关于边缘触发的。本文实现的epoll多线程模型主要是,主线程等待事件触发,然后把相关事件放入队列,线程池从队列中取出数据处理事件。

下面是具体实现:

 

 
  1. #include <stdarg.h>
  2. #include <errno.h>
  3. #include <stdio.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <time.h>
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <sys/epoll.h>
  10. #include <sys/sendfile.h>
  11. #include <dirent.h>
  12. #include <netinet/in.h>
  13. #include <sys/socket.h>
  14. #include <resolv.h>
  15. #include <arpa/inet.h>
  16. #include <stdlib.h>
  17. #include <signal.h>
  18. #include <getopt.h>
  19. #include <string.h>
  20. #include <string>
  21. #include <iostream>
  22. #include "ThreadPool.h"
  23. using namespace std;
  24.  
  25. static string strIP="192.168.0.168";
  26. static int nPort=8088;
  27. static string strDir="/home/temp/http_server";
  28. const int MAX_EVENT=10;
  29. struct epoll_event ev, events[MAX_EVENT];
  30. int epfd;
  31. enum {NeedRead_Event,Reading_Event,Error_Req,NeedWrite_Event,Writing_Event};
  32.  
  33. struct Task
  34. {
  35. epoll_event m_oEvent;
  36. string m_strFilePath;
  37. int m_nCurSize;
  38. int m_nType;
  39. int m_nReadOrWritefd;
  40. int m_nFileLen;
  41. int m_nSockfd;
  42. };
  43.  
  44. char* dir_up(char *Path)
  45. {
  46. int len;
  47. len = strlen(Path);
  48. if (len > 1 && Path[len - 1] == '/')
  49. {
  50. len--;
  51. }
  52. while (Path[len - 1] != '/' && len > 1)
  53. {
  54. len--;
  55. }
  56. Path[len] = 0;
  57. return Path;
  58. }
  59.  
  60. char *strsplit(char **s,char del)
  61. {
  62. char *d, *tok;
  63. if (!s || !*s)
  64. return NULL;
  65. tok = *s;
  66. d = strchr(tok, del);
  67. if (d) {
  68. *d = '\0';
  69. *s = d + 1;
  70. } else
  71. *s = NULL;
  72. return tok;
  73. }
  74.  
  75. char* urldecode( char* encd, char* decd)
  76. {
  77. int j,i;
  78. char *cd = encd;
  79. char p[2];
  80. unsigned int num;
  81. j=0;
  82.  
  83. for( i = 0; i < strlen(cd); i++ )
  84. {
  85. memset( p, '\0', 2 );
  86. if( cd[i] != '%' )
  87. {
  88. decd[j++] = cd[i];
  89. continue;
  90. }
  91.  
  92. p[0] = cd[++i];
  93. p[1] = cd[++i];
  94.  
  95. p[0] = p[0] - 48 - ((p[0] >= 'A') ? 7 : 0) - ((p[0] >= 'a') ? 32 : 0);
  96. p[1] = p[1] - 48 - ((p[1] >= 'A') ? 7 : 0) - ((p[1] >= 'a') ? 32 : 0);
  97. decd[j++] = (p[0] * 16 + p[1]);
  98.  
  99. }
  100. decd[j] = '\0';
  101.  
  102. return decd;
  103. }
  104.  
  105.  
  106. void *ReadTask(void *arg)
  107. {
  108.  
  109. Task *pTask=(Task*)arg;
  110. char *cBuffer=new char[128*1024];
  111. if(pTask->m_nType==NeedRead_Event)
  112. {
  113. int nSize=read(pTask->m_nSockfd,cBuffer,128*1024);
  114. if(nSize==0)
  115. {
  116. close(pTask->m_nSockfd);
  117. epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
  118. delete pTask;
  119. return NULL;
  120. }
  121. else if(nSize<0)
  122. {
  123. cout<<errno<<endl;
  124. if (errno == EINTR)
  125. {
  126. pTask->m_oEvent.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
  127. epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
  128. return NULL;
  129. }
  130. if (errno == EAGAIN)
  131. {
  132. return NULL;
  133. }
  134. }
  135. else
  136. {
  137. char *cMethon=strsplit(&cBuffer,' ');
  138. if(cBuffer==NULL)
  139. {
  140. pTask->m_nType=Error_Req;
  141. pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
  142. epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
  143. return NULL;
  144. }
  145. char *cUrl=strsplit(&cBuffer,' ');
  146. if(cUrl==NULL)
  147. {
  148. pTask->m_nType=Error_Req;
  149. pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
  150. epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
  151. return NULL;
  152. }
  153. char *decUrl=new char [strlen(cUrl)];
  154. urldecode(cUrl,decUrl);
  155. pTask->m_nType=NeedWrite_Event;
  156. //pTask->m_strFilePath=UrlDecode(cUrl);
  157. pTask->m_strFilePath=decUrl;
  158. delete [] decUrl;
  159. pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
  160. epoll_ctl(epfd, EPOLL_CTL_MOD, pTask->m_nSockfd, &(pTask->m_oEvent));
  161. return NULL;
  162. }
  163.  
  164. }
  165. else
  166. {
  167. //处理post消息类型或者大文件
  168. return NULL;
  169. }
  170. }
  171. const string strError="HTTP/1.1 404 ERROR\r\nServer:SimpleServer\r\nConnection: close\r\nContent-Type:text/html; charset=UTF-8\r\n";
  172.  
  173. void *WriteTask(void *arg)
  174. {
  175. Task *pTask=(Task*)arg;
  176. char cBuffer[128*1024];
  177. if(pTask->m_nType==NeedWrite_Event)
  178. {
  179. struct dirent *pDir;
  180. struct stat oStat;
  181. DIR *dir;
  182. string strFilePath=strDir+pTask->m_strFilePath;
  183. if(stat(strFilePath.c_str(),&oStat))
  184. {
  185. //需要对返回值进行判断,这边不会溢出所以简单处理,实际上需要处理
  186. 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));
  187. 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));
  188. int nWriteCount=write(pTask->m_nSockfd,cBuffer,nSize);
  189. /*
  190. if(nWriteCount<0)
  191. {
  192. if(errno == EAGAIN)
  193. {
  194. pTask->m_nType=Writing_Event;
  195. pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
  196. epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
  197. }
  198. }
  199. */
  200. close(pTask->m_nSockfd);
  201. epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
  202. delete pTask;
  203. return NULL;
  204. }
  205. if(S_ISREG(oStat.st_mode))
  206. {
  207. pTask->m_nReadOrWritefd = open(strFilePath.c_str(), O_RDONLY);
  208. pTask->m_nFileLen = lseek(pTask->m_nReadOrWritefd, 0, SEEK_END);
  209. lseek(pTask->m_nReadOrWritefd, 0, SEEK_SET);
  210. 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);
  211. write(pTask->m_nSockfd,cBuffer,nSize);
  212. pTask->m_nType=Writing_Event;
  213. pTask->m_nCurSize=0;
  214. pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
  215. epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
  216. }
  217. else if(S_ISDIR(oStat.st_mode))
  218. {
  219. int nSize=0;
  220. dir=opendir(strFilePath.c_str());
  221. 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>"
  222. "<body><font size=+4>Linux directory access server</font><br><hr width=\"100%%\"><br><center>"
  223. "<table border cols=3 width=\"100%%\">", strFilePath.c_str());
  224. nSize+=sprintf(cBuffer+nSize, "<caption><font size=+3>dir %s</font></caption>\n",strFilePath.c_str());
  225. nSize+=sprintf(cBuffer+nSize, "<tr><td>name</td><td>大小</td><td>change time</td></tr>\n");
  226. if(dir==NULL)
  227. {
  228. nSize+=sprintf(cBuffer+nSize,"</table><font color=\"CC0000\" size=+2>%s</font></body></html>",strerror(errno));
  229. write(pTask->m_nSockfd,cBuffer,nSize);
  230. return NULL;
  231. }
  232. while((pDir=readdir(dir))!=NULL)
  233. {
  234. string strFileName=string(pTask->m_strFilePath.c_str())+"/"+string(pDir->d_name);
  235. nSize+=sprintf(cBuffer+nSize,"<tr>");
  236. string strDirFilePath=strFilePath+"/"+pDir->d_name;
  237. if(stat(strDirFilePath.c_str(),&oStat)==0)
  238. {
  239. if(strcmp(pDir->d_name, "..") == 0)
  240. {
  241. char path[PATH_MAX];
  242. strcpy(path,pTask->m_strFilePath.c_str());
  243. nSize+=sprintf(cBuffer+nSize,"<td><a href=\"http://%s:%d%s\">..</a></td>",strIP.c_str(), nPort,dir_up(path));
  244. }
  245. else if(strcmp(pDir->d_name,".")==0)
  246. {
  247. nSize+=sprintf(cBuffer+nSize,"<td><a href=\"http://%s:%d%s\">.</a></td>",strIP.c_str(),nPort, pTask->m_strFilePath.c_str());
  248. }
  249. else
  250. {
  251. nSize+=sprintf(cBuffer+nSize,"<td><a href=\"http://%s:%d%s\">%s</a></td>", strIP.c_str(),nPort, strFileName.c_str(),pDir->d_name);
  252. }
  253. if (S_ISDIR(oStat.st_mode))
  254. {
  255. nSize+=sprintf(cBuffer+nSize, "<td>dir</td>");
  256. }
  257. else if (S_ISREG(oStat.st_mode))
  258. {
  259. nSize+=sprintf(cBuffer+nSize, "<td>%d</td>", oStat.st_size);
  260. }
  261. else if (S_ISLNK(oStat.st_mode))
  262. {
  263. nSize+=sprintf(cBuffer+nSize, "<td>interlinkage</td>");
  264. }
  265. else if (S_ISCHR(oStat.st_mode))
  266. {
  267. nSize+=sprintf(cBuffer+nSize, "<td>char device</td>");
  268. }
  269. else if (S_ISBLK(oStat.st_mode))
  270. {
  271. nSize+=sprintf(cBuffer+nSize, "<td>chunk device</td>");
  272. }
  273. else if (S_ISFIFO(oStat.st_mode))
  274. {
  275. nSize+=sprintf(cBuffer+nSize, "<td>FIFO</td>");
  276. }
  277. else if (S_ISSOCK(oStat.st_mode))
  278. {
  279. nSize+=sprintf(cBuffer+nSize, "<td>Socket</td>");
  280. }
  281. else
  282. {
  283. nSize+=sprintf(cBuffer+nSize, "<td>(unknow)</td>");
  284. nSize+=sprintf(cBuffer+nSize, "<td>%s</td>", ctime(&oStat.st_ctime));
  285. }
  286. }
  287. nSize+=sprintf(cBuffer+nSize, "</tr>\n");
  288. }
  289. closedir(dir);
  290. nSize+=sprintf(cBuffer+nSize, "</tr>\n");
  291. write(pTask->m_nSockfd,cBuffer,nSize);
  292. close(pTask->m_nSockfd);
  293. epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
  294. delete pTask;
  295. }
  296. else
  297. {
  298. int nSize=0;
  299. 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%%\">");
  300. 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());
  301. write(pTask->m_nSockfd,cBuffer,nSize);
  302. close(pTask->m_nSockfd);
  303. epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
  304. delete pTask;
  305. }
  306. }
  307. else if(pTask->m_nType==Writing_Event)
  308. {
  309. int nSize=128*1024;
  310. if(pTask->m_nFileLen-pTask->m_nCurSize<128*1024)
  311. {
  312. nSize=pTask->m_nFileLen-pTask->m_nCurSize;
  313. }
  314. nSize=sendfile(pTask->m_nSockfd,pTask->m_nReadOrWritefd,NULL,nSize);
  315. pTask->m_nCurSize+=nSize;
  316. if(pTask->m_nCurSize!=pTask->m_nFileLen)
  317. {
  318. pTask->m_oEvent.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
  319. epoll_ctl(epfd, EPOLL_CTL_MOD,pTask->m_nSockfd, &(pTask->m_oEvent));
  320. }
  321. else
  322. {
  323. close(pTask->m_nReadOrWritefd);
  324. close(pTask->m_nSockfd);
  325. epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
  326. delete pTask;
  327. }
  328. }
  329. else if(pTask->m_nType==Error_Req)
  330. {
  331. write(pTask->m_nSockfd,strError.c_str(),strError.size());
  332. close(pTask->m_nSockfd);
  333. epoll_ctl(epfd, EPOLL_CTL_DEL,pTask->m_nSockfd, &(pTask->m_oEvent));
  334. delete pTask;
  335. }
  336. return NULL;
  337. }
  338.  
  339.  
  340. void setnonblocking(int sock)
  341. {
  342. int opts;
  343. opts = fcntl(sock, F_GETFL);
  344. if (opts < 0)
  345. {
  346. cout<<"fcntl(sock,F_GETFL) error"<<endl;
  347. return ;
  348. }
  349. opts = opts | O_NONBLOCK;
  350. if (fcntl(sock, F_SETFL, opts) < 0)
  351. {
  352. cout<<"fcntl(sock,F_GETFL) error"<<endl;
  353. return ;
  354. }
  355. }
  356. int main()
  357. {
  358. ThreadPool mPool;
  359. //signal(SIGPIPE,SIG_IGN);
  360. //signal(SIGCHLD,SIG_IGN);
  361. epfd = epoll_create(MAX_EVENT);
  362. struct sockaddr_in addr;
  363. int sock_fd,addrlen;
  364. if((sock_fd=socket(PF_INET,SOCK_STREAM,0))<0)
  365. {
  366. cout<<"socket init failed..."<<endl;
  367. }
  368.  
  369. addrlen = 1;
  370. setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &addrlen, sizeof(addrlen));
  371.  
  372. setnonblocking(sock_fd);
  373. addr.sin_family=AF_INET;
  374. addr.sin_port=htons(nPort);
  375. addr.sin_addr.s_addr=htonl(INADDR_ANY);
  376. addrlen=sizeof(struct sockaddr_in);
  377. if(bind(sock_fd,(struct sockaddr*)&addr,addrlen)<0)
  378. {
  379. cout<<"bind failed..."<<endl;
  380. }
  381. if(listen(sock_fd,MAX_EVENT)<0)
  382. {
  383. cout<<"listen failed..."<<endl;
  384. }
  385. cout<<"server start..."<<endl;
  386.  
  387. ev.data.fd = sock_fd;
  388. // 设置要处理的事件类型
  389. ev.events = EPOLLIN | EPOLLET;
  390. // 注册 epoll 事件
  391. epoll_ctl(epfd, EPOLL_CTL_ADD, sock_fd, &ev);
  392. struct sockaddr_in clientaddr ;
  393. for (;;)
  394. {
  395. int nfds = epoll_wait(epfd, events, MAX_EVENT, -1);
  396. for(int i=0;i<nfds;++i)
  397. {
  398. if(events[i].data.fd==sock_fd)
  399. {
  400. int connfd;
  401. socklen_t clilen = sizeof(clientaddr);
  402. connfd = accept(sock_fd, (sockaddr *)&clientaddr, &clilen);
  403. if(connfd == -1)
  404. {
  405. cout<<"accept error:"<<errno<<endl;
  406. continue;
  407. }
  408. else
  409. {
  410. cout<<"connect from:"<<inet_ntoa(clientaddr.sin_addr)<<":"<<ntohs(clientaddr.sin_port)<<endl;
  411. }
  412. setnonblocking(connfd);
  413. int nRecvBuf=128*1024;
  414. setsockopt(connfd, SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
  415. int nSendBuf=128*1024;
  416. setsockopt(connfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
  417. int nNetTimeout=3000;
  418. setsockopt(connfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&nNetTimeout, sizeof(int));
  419. setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&nNetTimeout, sizeof(int));
  420. Task *pTask=new Task;
  421. pTask->m_nType=NeedRead_Event;
  422. pTask->m_nSockfd=connfd;
  423. pTask->m_oEvent.events = EPOLLIN | EPOLLET |EPOLLONESHOT;
  424. pTask->m_oEvent.data.ptr = pTask;
  425. epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &(pTask->m_oEvent));
  426. }
  427. else if(events[i].events & EPOLLIN)
  428. {
  429.  
  430. mPool.AddWorker(ReadTask,events[i].data.ptr);
  431. }
  432. else if(events[i].events & EPOLLOUT)
  433. {
  434. mPool.AddWorker(WriteTask,events[i].data.ptr);
  435. }
  436.  
  437.  
  438. }
  439.  
  440. }
  441. }