├── htdocs ├── introduction.html ├── readme.md ├── index2.html ├── index.html ├── date.cgi ├── color.cgi ├── check.cgi ├── register.cgi └── register.html ├── Makefile ├── README.md ├── ThreadPool.h └── httpd.cpp /htdocs/introduction.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fltflt/Tinyhttpd_with_threadpool_epoll/HEAD/htdocs/introduction.html -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: httpd 2 | 3 | httpd: httpd.cpp 4 | g++ -W -Wall -o httpd httpd.cpp -lpthread 5 | 6 | clean: 7 | rm httpd 8 | -------------------------------------------------------------------------------- /htdocs/readme.md: -------------------------------------------------------------------------------- 1 | # 文件含义 2 | 3 | ## 动态页面请求 4 | (1) date.cgi与index2.html对应,用于显示当前时间 5 | 6 | (2) color.cgi与index.html对应,用于提交颜色,并显示这个颜色页面 7 | 8 | (3) register.cgi与register.html对应,用于显示当前时间 9 | 10 | ## 静态页面请求 11 | 12 | (1) introduction.html对应,用于显示个人资料 13 | -------------------------------------------------------------------------------- /htdocs/index2.html: -------------------------------------------------------------------------------- 1 | 2 |
TODAY is 5 |
Welcome to J. David's webserver. 5 |
Your browser sent a bad request, "); 186 | send(client, buf, sizeof(buf), 0); 187 | sprintf(buf, "such as a POST without a Content-Length.\r\n"); 188 | send(client, buf, sizeof(buf), 0); 189 | } 190 | 191 | /**********************************************************************/ 192 | /* Put the entire contents of a file out on a socket. This function 193 | * is named after the UNIX "cat" command, because it might have been 194 | * easier just to do something like pipe, fork, and exec("cat"). 195 | * Parameters: the client socket descriptor 196 | * FILE pointer for the file to cat */ 197 | /**********************************************************************/ 198 | void cat(int client, FILE *resource) 199 | { 200 | //发送文件的内容 201 | char buf[1024]; 202 | //读取文件到buf中 203 | fgets(buf, sizeof(buf), resource); 204 | while (!feof(resource))//判断文件是否读取到末尾 205 | { 206 | //读取并发送文件内容 207 | send(client, buf, strlen(buf), 0); 208 | fgets(buf, sizeof(buf), resource); 209 | } 210 | } 211 | 212 | /**********************************************************************/ 213 | /* Inform the client that a CGI script could not be executed. 214 | * Parameter: the client socket descriptor. */ 215 | /**********************************************************************/ 216 | void cannot_execute(int client) 217 | { 218 | char buf[1024]; 219 | //发送500 220 | sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); 221 | send(client, buf, strlen(buf), 0); 222 | sprintf(buf, "Content-type: text/html\r\n"); 223 | send(client, buf, strlen(buf), 0); 224 | sprintf(buf, "\r\n"); 225 | send(client, buf, strlen(buf), 0); 226 | sprintf(buf, "
Error prohibited CGI execution.\r\n");
227 | send(client, buf, strlen(buf), 0);
228 | }
229 |
230 | /**********************************************************************/
231 | /* Print out an error message with perror() (for system errors; based
232 | * on value of errno, which indicates system call errors) and exit the
233 | * program indicating an error. */
234 | /**********************************************************************/
235 | void error_die(const char *sc)
236 | {
237 | perror(sc);
238 | exit(1);
239 | }
240 |
241 | /**********************************************************************/
242 | /* Execute a CGI script. Will need to set environment variables as
243 | * appropriate.
244 | * Parameters: client socket descriptor
245 | * path to the CGI script */
246 | /**********************************************************************/
247 | //执行cgi动态解析
248 | void execute_cgi(int client, const char *path,
249 | const char *method, const char *query_string)
250 | {
251 | char buf[1024];
252 | int cgi_output[2];//声明的读写管道,切莫被名称给忽悠,会给出图进行说明
253 | int cgi_input[2];//
254 | pid_t pid;
255 | int status;
256 | int i;
257 | char c;
258 | int numchars = 1;
259 | int content_length = -1;
260 |
261 | buf[0] = 'A'; buf[1] = '\0';
262 | if (strcasecmp(method, "GET") == 0)
263 | //如果是GET请求
264 | //读取并且丢弃头信息
265 | while ((numchars > 0) && strcmp("\n", buf))
266 | numchars = get_line(client, buf, sizeof(buf));
267 | else
268 | {
269 | //处理的请求为POST
270 | numchars = get_line(client, buf, sizeof(buf));
271 | while ((numchars > 0) && strcmp("\n", buf))
272 | {//循环读取头信息找到Content-Length字段的值
273 | buf[15] = '\0';//目的是为了截取Content-Length:
274 |
275 | if (strcasecmp(buf, "Content-Length:") == 0)
276 | //"Content-Length: 15"
277 | content_length = atoi(&(buf[16]));//获取Content-Length的值
278 | numchars = get_line(client, buf, sizeof(buf));
279 | }
280 | if (content_length == -1) {
281 | //错误请求
282 | bad_request(client);
283 | return;
284 | }
285 | }
286 | //返回正确响应码200
287 | sprintf(buf, "HTTP/1.0 200 OK\r\n");
288 | send(client, buf, strlen(buf), 0);
289 | //#include The server could not fulfill\r\n");
457 | send(client, buf, strlen(buf), 0);
458 | sprintf(buf, "your request because the resource specified\r\n");
459 | send(client, buf, strlen(buf), 0);
460 | sprintf(buf, "is unavailable or nonexistent.\r\n");
461 | send(client, buf, strlen(buf), 0);
462 | sprintf(buf, "\r\n");
463 | send(client, buf, strlen(buf), 0);
464 | }
465 |
466 | /**********************************************************************/
467 | /* Send a regular file to the client. Use headers, and report
468 | * errors to client if they occur.
469 | * Parameters: a pointer to a file structure produced from the socket
470 | * file descriptor
471 | * the name of the file to serve */
472 | /**********************************************************************/
473 | //将请求的文件发送回浏览器客户端
474 | void serve_file(int client, const char *filename)
475 | {
476 | FILE *resource = NULL;
477 | int numchars = 1;
478 | char buf[1024];
479 | buf[0] = 'A'; buf[1] = '\0';//这个赋值不清楚是干什么的
480 | while ((numchars > 0) && strcmp("\n", buf)) //将HTTP请求头读取并丢弃
481 | numchars = get_line(client, buf, sizeof(buf));
482 | //打开文件
483 | resource = fopen(filename, "r");
484 | if (resource == NULL)
485 | //如果文件不存在,则返回not_found
486 | not_found(client);
487 | else
488 | {
489 | //添加HTTP头
490 | headers(client, filename);
491 | //并发送文件内容
492 | cat(client, resource);
493 | }
494 | fclose(resource);//关闭文件句柄
495 | }
496 |
497 | /**********************************************************************/
498 | /* This function starts the process of listening for web connections
499 | * on a specified port. If the port is 0, then dynamically allocate a
500 | * port and modify the original port variable to reflect the actual
501 | * port.
502 | * Parameters: pointer to variable containing the port to connect on
503 | * Returns: the socket */
504 | /**********************************************************************/
505 | //启动服务端
506 | int startup(u_short *port)
507 | {
508 |
509 | //服务器处理步骤
510 | /*
511 | 第一步:创建端口号,使用函数int socketid = socket(family, type, protocol);;
512 | 第二步:将地址结构体绑定在端口号上,使用函数int bind(int fd, const struct sockaddr *, socklen_t);
513 | 第三步:循环监听客户端请求,使用函数listen(sockfd, 5);(第一个参数是 socket 描述符,第二个参数是最大连接数)
514 |
515 | 第四步:接受客户端的请求,new_socket = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
516 | 第五步:使用read()/write()进行数据交换
517 | 第六步:关闭连接,使用close(socketid);
518 | */
519 |
520 |
521 | //客户端处理步骤
522 | /*
523 | 第一步:创建端口号,使用函数int socketid = socket(family, type, protocol);
524 | 第二步:将地址结构体绑定在端口号上,使用函数int bind(int fd, const struct sockaddr *, socklen_t);
525 | 第三步:请求连接,使用函数int connect(int socket, const struct sockaddr* address, size_t address_len);
526 |
527 | 第四步:使用read()/write()进行数据交换;
528 | 第五步:关闭连接,使用close(socketid);
529 | */
530 | int httpd = 0;
531 | struct sockaddr_in name;
532 | //设置http socket
533 | httpd = socket(PF_INET, SOCK_STREAM, 0);
534 | if (httpd == -1)
535 | error_die("socket");
536 | memset(&name, 0, sizeof(name));
537 |
538 | //创建地址结构体struct sockaddr_in,主要包含地址和端口号
539 | name.sin_family = AF_INET;
540 | name.sin_port = htons(*port);
541 | name.sin_addr.s_addr = htonl(INADDR_ANY);
542 |
543 |
544 | //绑定端口
545 | if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
546 | error_die("bind");
547 | if (*port == 0) /*动态分配一个端口 */
548 | {
549 | socklen_t namelen = sizeof(name);
550 | if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
551 | error_die("getsockname");
552 | *port = ntohs(name.sin_port);
553 | }
554 | //监听连接
555 | if (listen(httpd, 5) < 0)
556 | error_die("listen");
557 | return(httpd);
558 | }
559 |
560 | /**********************************************************************/
561 | /* Inform the client that the requested web method has not been
562 | * implemented.
563 | * Parameter: the client socket */
564 | /**********************************************************************/
565 | void unimplemented(int client)
566 | {
567 | char buf[1024];
568 | //发送501说明相应方法没有实现
569 | sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
570 | send(client, buf, strlen(buf), 0);
571 | sprintf(buf, SERVER_STRING);
572 | send(client, buf, strlen(buf), 0);
573 | sprintf(buf, "Content-Type: text/html\r\n");
574 | send(client, buf, strlen(buf), 0);
575 | sprintf(buf, "\r\n");
576 | send(client, buf, strlen(buf), 0);
577 | sprintf(buf, " HTTP request method not supported.\r\n");
582 | send(client, buf, strlen(buf), 0);
583 | sprintf(buf, "\r\n");
584 | send(client, buf, strlen(buf), 0);
585 | }
586 |
587 | /**********************************************************************/
588 |
589 | int main(void)
590 | {
591 | int server_sock = -1;
592 | u_short port = 3000;
593 | int connfd_fd = -1;
594 | struct sockaddr_in client_name;
595 | socklen_t client_name_len = sizeof(client_name);
596 |
597 |
598 | //创建线程池
599 | ThreadPool pool(4);
600 | printf("线程池创建成功!\n");
601 | //pthread_t newthread;
602 | //启动server socket
603 | server_sock = startup(&port);
604 |
605 |
606 | //创建epoll事件
607 | int epfd, nfds;
608 | //生成用于处理accept的epoll专用的文件描述符
609 | epfd=epoll_create(5);
610 | struct epoll_event ev,events[20];
611 | ev.data.fd = server_sock;
612 | //设置要处理的事件类型
613 | ev.events=EPOLLIN|EPOLLET;
614 | //注册epoll事件
615 | epoll_ctl(epfd,EPOLL_CTL_ADD,server_sock,&ev);
616 |
617 | printf("httpd running on port:%d\n", port);
618 |
619 |
620 |
621 |
622 | for ( ; ; )
623 | {
624 | //等待epoll事件的发生
625 | nfds = epoll_wait(epfd,events,20,500);
626 | //处理所发生的所有事件
627 | for (int i = 0; i < nfds; ++i)
628 | {
629 | if (events[i].data.fd == server_sock)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
630 | {
631 | connfd_fd = accept(server_sock,(sockaddr *)&client_name, &client_name_len);
632 | if (connfd_fd < 0)
633 | {
634 | perror("connfd_fd < 0");
635 | exit(1);
636 | }
637 | char *str = inet_ntoa(client_name.sin_addr);
638 | cout << "accapt a connection from " << str << endl;
639 |
640 |
641 | ev.data.fd = connfd_fd;
642 | //设置用于注测的读操作事件
643 | ev.events = EPOLLIN|EPOLLET;
644 | //注册ev
645 | epoll_ctl(epfd,EPOLL_CTL_ADD,connfd_fd,&ev);
646 | }
647 | else if (events[i].events&EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。
648 | {
649 | std::cout<<"start worker thread ID:"<