├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── datum ├── A Design Framework for Highly Concurrent Systems.pdf ├── ClusterCall.pdf ├── ClusterCall_full.jpg ├── ClusterCall_full.pdf ├── RFC2616.pdf ├── Reactor.png ├── info.png ├── 压测结果.png ├── 压测结果(8worker).png ├── 压测负载.png ├── 压测负载(8worker).png └── 标准页面性能测试.png ├── old_version ├── TKeed.c └── TKeed.h ├── src_code ├── .idea │ ├── misc.xml │ ├── modules.xml │ ├── src_code.iml │ └── workspace.xml ├── CMakeLists.txt ├── README.md ├── cmake-build-debug │ ├── CMakeCache.txt │ ├── CMakeFiles │ │ ├── 3.8.2 │ │ │ ├── CMakeCCompiler.cmake │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ ├── CMakeDetermineCompilerABI_C.bin │ │ │ ├── CMakeDetermineCompilerABI_CXX.bin │ │ │ ├── CMakeRCCompiler.cmake │ │ │ ├── CMakeSystem.cmake │ │ │ ├── CompilerIdC │ │ │ │ └── CMakeCCompilerId.c │ │ │ └── CompilerIdCXX │ │ │ │ └── CMakeCXXCompilerId.cpp │ │ ├── CMakeDirectoryInformation.cmake │ │ ├── CMakeOutput.log │ │ ├── Makefile.cmake │ │ ├── Makefile2 │ │ ├── TargetDirectories.txt │ │ ├── clion-environment.txt │ │ ├── clion-log.txt │ │ ├── cmake.check_cache │ │ ├── feature_tests.bin │ │ ├── feature_tests.c │ │ ├── feature_tests.cxx │ │ ├── progress.marks │ │ └── src_code.dir │ │ │ ├── DependInfo.cmake │ │ │ ├── build.make │ │ │ ├── cmake_clean.cmake │ │ │ ├── depend.make │ │ │ ├── flags.make │ │ │ ├── link.txt │ │ │ ├── linklibs.rsp │ │ │ ├── objects1.rsp │ │ │ └── progress.make │ ├── Makefile │ ├── cmake_install.cmake │ └── src_code.cbp ├── epoll.c ├── epoll.h ├── error.h ├── http.c ├── http.h ├── http_parse.c ├── http_parse.h ├── http_request.c ├── http_request.h ├── index.html ├── list.h ├── main.c ├── makefile ├── priority_queue.c ├── priority_queue.h ├── rio.c ├── rio.h ├── threadpool.c ├── threadpool.h ├── timer.c ├── timer.h ├── tkeed.conf ├── util.c └── util.h ├── test_unit ├── list.h ├── list_test ├── list_test.c ├── priority_queue.c ├── priority_queue.h ├── priority_queue_test ├── priority_queue_test.c └── webbench │ ├── socket.c │ └── webbench.c ├── usage ├── index.html └── obj_file │ ├── epoll.o │ ├── http.o │ ├── http_parse.o │ ├── http_request.o │ ├── main.o │ ├── priority_queue.o │ ├── rio.o │ ├── server │ ├── threadpool.o │ ├── timer.o │ └── util.o ├── 主要函数.md ├── 启示录.md ├── 并发模型.md ├── 架构分析.md ├── 核心结构体.md ├── 测试及改进.md ├── 背景知识.md └── 项目目的.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Libraries 15 | *.lib 16 | *.a 17 | *.la 18 | *.lo 19 | 20 | # Shared objects (inc. Windows DLLs) 21 | *.dll 22 | *.so 23 | *.so.* 24 | *.dylib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | *.i*86 31 | *.x86_64 32 | *.hex 33 | 34 | # Debug files 35 | *.dSYM/ 36 | *.su 37 | 38 | # vscode 39 | .vscode 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: precise 2 | 3 | language: c 4 | 5 | script: 6 | - cd ./src_code/ && make 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 wgtdkp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TKeed WebServer 2 | 3 | **TKeed is a high performance HTTP WebServer uses the Reactor model. Code normative and functional scalability are close to the industry level. The project will be updated until feature have been completed. Have a fun. SYSU, TeeKee.** 4 | 5 | ![info](./datum/info.png) 6 | 7 | ## Dev Document 8 | 9 | | Part Ⅰ | Part Ⅱ | Part Ⅲ | Part Ⅳ | Part Ⅴ | Part Ⅵ | Part Ⅷ | Part Ⅸ | Part Ⅹ | 10 | | :--------: | :---------: | :---------: | :---------: | :---------: | :---------: |:--------:| :--------:|:--------:| 11 | | [项目目的](https://github.com/linw7/TKeed/blob/master/%E9%A1%B9%E7%9B%AE%E7%9B%AE%E7%9A%84.md) | [并发模型](https://github.com/linw7/TKeed/blob/master/%E5%B9%B6%E5%8F%91%E6%A8%A1%E5%9E%8B.md)|[核心结构](https://github.com/linw7/TKeed/blob/master/%E6%A0%B8%E5%BF%83%E7%BB%93%E6%9E%84%E4%BD%93.md)|[整体架构](https://github.com/linw7/TKeed/blob/master/%E6%9E%B6%E6%9E%84%E5%88%86%E6%9E%90.md)| [主要函数](https://github.com/linw7/TKeed/blob/master/%E4%B8%BB%E8%A6%81%E5%87%BD%E6%95%B0.md)| [遇到的困难](https://github.com/linw7/TKeed/blob/master/%E5%90%AF%E7%A4%BA%E5%BD%95.md) | [测试及改进](https://github.com/linw7/TKeed/blob/master/%E6%B5%8B%E8%AF%95%E5%8F%8A%E6%94%B9%E8%BF%9B.md) | [背景知识](https://github.com/linw7/TKeed/blob/master/%E8%83%8C%E6%99%AF%E7%9F%A5%E8%AF%86.md)|[使用教程](https://asciinema.org/a/132577)| 12 | 13 | --- 14 | 15 | ## Dev Environment 16 | 17 | **Dev Tool** 18 | 19 | - 操作系统:Ubuntu 16.04 20 | 21 | - 编辑器:Sublime + Vim 22 | 23 | - 编译器:gcc 5.4.0 24 | 25 | - 单元测试:~~gtest~~ 26 | 27 | - 版本控制:git 28 | 29 | - 代码结构:[Understand](https://scitools.com/) + [callgraph](http://blog.csdn.net/solstice/article/details/488865) 30 | 31 | - 集成环境:[Clion](https://www.jetbrains.com/clion/) 32 | 33 | **Other** 34 | 35 | - 自动化构建:[Travis CI](https://travis-ci.org/linw7/TKeed) 36 | 37 | - 压测工具:[WebBench](https://github.com/EZLippi/WebBench) 38 | 39 | --- 40 | 41 | ## Timeline 42 | 43 | **Now** 44 | 45 | - v1.0已经完成,本地已调试通过。提交到GitHub上的代码会由Travis自动构建。 46 | 47 | 特性: 48 | 49 | - 添加Timer定时器,定时回调handler处理超时请求 50 | 51 | - 高效的小根堆结构 52 | 53 | - 惰性删除方式 54 | 55 | - 实现了HTTP长连接传输数据 56 | 57 | - 非阻塞I/O 58 | 59 | - epoll边缘触发模式(ET) 60 | 61 | - 线程池操作及其同步互斥管理 62 | 63 | - 调度选项 64 | 65 | - 队列式FIFO调度模式 66 | 67 | - 加入优先级的优先队列 (+) 68 | 69 | - 使用状态机解析HTTP协议,非简单字符串匹配方式解析请求 70 | 71 | - v1.1修改了CPU负载较高问题,修改后1000并发各线程(4worker)CPU使用率10%左右。 72 | 73 | **Feature** 74 | 75 | - v2.0实现Json解释器解析配置 76 | 77 | - v3.0实现FastCGI(功能扩展) 78 | 79 | - v4.0实现服务器缓存(性能加速) 80 | 81 | - v5.0实现反向代理(安全性及负载均衡) 82 | 83 | --- 84 | 85 | [![Build Status](https://travis-ci.org/linw7/TKeed.svg?branch=master)](https://travis-ci.org/linw7/TKeed) 86 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) 87 | 88 | --- -------------------------------------------------------------------------------- /datum/A Design Framework for Highly Concurrent Systems.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/A Design Framework for Highly Concurrent Systems.pdf -------------------------------------------------------------------------------- /datum/ClusterCall.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/ClusterCall.pdf -------------------------------------------------------------------------------- /datum/ClusterCall_full.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/ClusterCall_full.jpg -------------------------------------------------------------------------------- /datum/ClusterCall_full.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/ClusterCall_full.pdf -------------------------------------------------------------------------------- /datum/RFC2616.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/RFC2616.pdf -------------------------------------------------------------------------------- /datum/Reactor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/Reactor.png -------------------------------------------------------------------------------- /datum/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/info.png -------------------------------------------------------------------------------- /datum/压测结果.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/压测结果.png -------------------------------------------------------------------------------- /datum/压测结果(8worker).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/压测结果(8worker).png -------------------------------------------------------------------------------- /datum/压测负载.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/压测负载.png -------------------------------------------------------------------------------- /datum/压测负载(8worker).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/压测负载(8worker).png -------------------------------------------------------------------------------- /datum/标准页面性能测试.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/datum/标准页面性能测试.png -------------------------------------------------------------------------------- /old_version/TKeed.c: -------------------------------------------------------------------------------- 1 | #include "TKEED.h" 2 | 3 | void doit(int fd); 4 | void read_requesthdrs(rio_t *rp); 5 | int parse_uri(char *uri, char *filename, char *cgiargs); 6 | void serve_static(int fd, char *filename, int filesize); 7 | void get_filetype(char *filename, char *filetype); 8 | void serve_dynamic(int fd, char *filename, char *cgiargs); 9 | void clienterror(int fd, char *cause, char *errnum, 10 | char *shortmsg, char *longmsg); 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | int listenfd, connfd; 15 | char hostname[MAXLINE], port[MAXLINE]; 16 | socklen_t clientlen; 17 | struct sockaddr_storage clientaddr; 18 | 19 | if(argc != 2){ 20 | fprintf(stderr, "usage : %s \n", argv[0]); 21 | exit(1); 22 | } 23 | 24 | listenfd = Open_listenfd(argv[1]); 25 | 26 | while(1){ 27 | clientlen = sizeof(clientaddr); 28 | connfd = Accept(listenfd, (SA*)&clientaddr, &clientlen); 29 | Getnameinfo((SA*)&clientaddr, clientlen, hostname ,MAXLINE, 30 | port, MAXLINE, 0); 31 | printf("Accepted connection from (%s, %s)\n", hostname, port); 32 | doit(connfd); 33 | Close(connfd); 34 | } 35 | } 36 | 37 | void read_requesthdrs(rio_t *rp) 38 | { 39 | char buf[MAXLINE]; 40 | 41 | Rio_readlineb(rp, buf, MAXLINE); 42 | printf("%s", buf); 43 | while(strcmp(buf, "\r\n")){ 44 | Rio_readlineb(rp, buf, MAXLINE); 45 | printf("%s", buf); 46 | } 47 | return; 48 | } 49 | 50 | void doit(int fd) 51 | { 52 | int is_static; 53 | struct stat sbuf; 54 | char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE]; 55 | char filename[MAXLINE], cgiargs[MAXLINE]; 56 | rio_t rio; 57 | 58 | Rio_readinitb(&rio, fd); 59 | if (!Rio_readlineb(&rio, buf, MAXLINE)) 60 | return; 61 | printf("%s", buf); 62 | sscanf(buf, "%s %s %s", method, uri, version); 63 | if (strcasecmp(method, "GET")) { 64 | clienterror(fd, method, "501", "Not Implemented", 65 | "Tiny does not implement this method"); 66 | return; 67 | } 68 | read_requesthdrs(&rio); 69 | 70 | is_static = parse_uri(uri, filename, cgiargs); 71 | if (stat(filename, &sbuf) < 0){ 72 | clienterror(fd, filename, "404", "Not found", 73 | "Tiny couldn't find this file"); 74 | return; 75 | } 76 | if (is_static){ 77 | if(!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)){ 78 | clienterror(fd, filename, "403", "Forbidden", 79 | "Tiny couldn't read the file"); 80 | return; 81 | } 82 | serve_static(fd, filename, sbuf.st_size); 83 | } 84 | else{ 85 | if(!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)){ 86 | clienterror(fd, filename, "403", "Forbidden", 87 | "Tiny couldn't run the CGI program"); 88 | return; 89 | } 90 | serve_dynamic(fd, filename, cgiargs); 91 | } 92 | } 93 | 94 | int parse_uri(char *uri, char *filename, char *cgiargs) 95 | { 96 | char *ptr; 97 | 98 | if(!strstr(uri, "cgi-bin")){ 99 | strcpy(cgiargs, ""); 100 | strcpy(filename, "."); 101 | strcat(filename, uri); 102 | if (uri[strlen(uri)-1] == '/') 103 | strcat(filename, "demo.html"); 104 | return 1; 105 | } 106 | else{ 107 | ptr = index(uri, '?'); 108 | if(ptr){ 109 | strcpy(cgiargs, ptr+1); 110 | *ptr = '\0'; 111 | } 112 | else 113 | strcpy(cgiargs, ""); 114 | strcpy(filename, "."); 115 | strcat(filename, uri); 116 | return 0; 117 | } 118 | } 119 | 120 | void serve_static(int fd, char *filename, int filesize) 121 | { 122 | int srcfd; 123 | char *srcp, filetype[MAXLINE], buf[MAXBUF]; 124 | 125 | get_filetype(filename, filetype); 126 | sprintf(buf, "HTTP/1.0 200 OK\r\n"); 127 | sprintf(buf, "%sServer: TKeed Web Server\r\n", buf); 128 | sprintf(buf, "%sConnection: close\r\n", buf); 129 | sprintf(buf, "%sContent-length: %d\r\n", buf, filesize); 130 | sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype); 131 | Rio_writen(fd, buf, strlen(buf)); 132 | printf("Response headers:\n"); 133 | printf("%s", buf); 134 | 135 | srcfd = Open(filename, O_RDONLY, 0); 136 | srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0); 137 | Close(srcfd); 138 | Rio_writen(fd, srcp, filesize); 139 | Munmap(srcp, filesize); 140 | } 141 | 142 | void get_filetype(char *filename, char *filetype)\ 143 | { 144 | if(strstr(filename, ".html")) 145 | strcpy(filetype, "text/html"); 146 | else if(strstr(filename, ".gif")) 147 | strcpy(filetype, "image/gif"); 148 | else if(strstr(filename, ".png")) 149 | strcpy(filetype, "image/png"); 150 | else if(strstr(filename, ".jpg")) 151 | strcpy(filetype, "image/jpeg"); 152 | else 153 | strcpy(filetype, "text/plain"); 154 | } 155 | 156 | void serve_dynamic(int fd, char *filename, char *cgiargs) 157 | { 158 | char buf[MAXLINE], *emptylist[] = { NULL }; 159 | 160 | sprintf(buf, "HTTP/1.0 200 OK\r\n"); 161 | Rio_writen(fd, buf, strlen(buf)); 162 | sprintf(buf, "Server: TKeed Web Server\r\n"); 163 | Rio_writen(fd, buf, strlen(buf)); 164 | 165 | if(Fork() == 0){ 166 | setenv("QUERY_STRING", cgiargs, 1); 167 | Dup2(fd, STDOUT_FILENO); 168 | Execve(filename, emptylist, environ); 169 | } 170 | Wait(NULL); 171 | } 172 | 173 | void clienterror(int fd, char *cause, char *errnum, 174 | char *shortmsg, char *longmsg) 175 | { 176 | char buf[MAXLINE], body[MAXBUF]; 177 | 178 | sprintf(body, "TKeed Error"); 179 | sprintf(body, "%s\r\n", body); 180 | sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg); 181 | sprintf(body, "%s

%s: %s\r\n", body, longmsg, cause); 182 | sprintf(body, "%s


The TKeed Web server\r\n", body); 183 | 184 | sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg); 185 | Rio_writen(fd, buf, strlen(buf)); 186 | sprintf(buf, "Content-type: text/html\r\n"); 187 | Rio_writen(fd, buf, strlen(buf)); 188 | sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body)); 189 | Rio_writen(fd, buf, strlen(buf)); 190 | Rio_writen(fd, body, strlen(body)); 191 | } 192 | -------------------------------------------------------------------------------- /src_code/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src_code/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src_code/.idea/src_code.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src_code/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(src_code) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | set(SOURCE_FILES 7 | epoll.c 8 | epoll.h 9 | error.h 10 | http.c 11 | http.h 12 | http_parse.c 13 | http_parse.h 14 | http_request.c 15 | http_request.h 16 | list.h 17 | main.c 18 | priority_queue.c 19 | priority_queue.h 20 | rio.c 21 | rio.h 22 | threadpool.c 23 | threadpool.h 24 | timer.c 25 | timer.h 26 | util.c 27 | util.h) 28 | 29 | add_executable(src_code ${SOURCE_FILES}) -------------------------------------------------------------------------------- /src_code/README.md: -------------------------------------------------------------------------------- 1 | # TKeed WebServer 2 | 3 | **TKeed is a high performance HTTP WebServer uses the Reactor model. Code normative and functional scalability are close to the industry level. The project will be updated until feature have been completed. Have a fun. SYSU, TeeKee.** 4 | 5 | ## Dev Document 6 | 7 | | Part Ⅰ | Part Ⅱ | Part Ⅲ | Part Ⅳ | Part Ⅴ | Part Ⅵ | Part Ⅷ | Part Ⅸ | Part Ⅹ | 8 | | :--------: | :---------: | :---------: | :---------: | :---------: | :---------: |:--------:| :--------:|:--------:| 9 | | [项目目的](https://github.com/linw7/TKeed/blob/master/%E9%A1%B9%E7%9B%AE%E7%9B%AE%E7%9A%84.md) | [并发模型](https://github.com/linw7/TKeed/blob/master/%E5%B9%B6%E5%8F%91%E6%A8%A1%E5%9E%8B.md)|[核心结构](https://github.com/linw7/TKeed/blob/master/%E6%A0%B8%E5%BF%83%E7%BB%93%E6%9E%84%E4%BD%93.md)|[整体架构](https://github.com/linw7/TKeed/blob/master/%E6%9E%B6%E6%9E%84%E5%88%86%E6%9E%90.md)| [主要函数](https://github.com/linw7/TKeed/blob/master/%E4%B8%BB%E8%A6%81%E5%87%BD%E6%95%B0.md)| [遇到的困难](https://github.com/linw7/TKeed/blob/master/%E5%90%AF%E7%A4%BA%E5%BD%95.md) | [测试及改进](https://github.com/linw7/TKeed/blob/master/%E6%B5%8B%E8%AF%95%E5%8F%8A%E6%94%B9%E8%BF%9B.md) | [背景知识](https://github.com/linw7/TKeed/blob/master/%E8%83%8C%E6%99%AF%E7%9F%A5%E8%AF%86.md)|[使用教程](https://asciinema.org/a/132577)| 10 | 11 | --- 12 | 13 | ## Dev Environment 14 | 15 | **Dev Tool** 16 | 17 | - 操作系统:Ubuntu 16.04 18 | 19 | - 编辑器:Sublime + Vim 20 | 21 | - 编译器:gcc 5.4.0 22 | 23 | - 单元测试:~~gtest~~ 24 | 25 | - 版本控制:git 26 | 27 | - 代码结构:[Understand](https://scitools.com/) + [callgraph](http://blog.csdn.net/solstice/article/details/488865) 28 | 29 | - 集成环境:[Clion](https://www.jetbrains.com/clion/) 30 | 31 | **Other** 32 | 33 | - 自动化构建:[Travis CI](https://travis-ci.org/linw7/TKeed) 34 | 35 | - 压测工具:[WebBench](https://github.com/EZLippi/WebBench) 36 | 37 | --- 38 | 39 | ## Timeline 40 | 41 | **Now** 42 | 43 | - v1.0已经完成,本地已调试通过。提交到GitHub上的代码会由Travis自动构建。 44 | 45 | 特性: 46 | 47 | - 添加Timer定时器,定时回调handler处理超时请求 48 | 49 | - 高效的小根堆结构 50 | 51 | - 惰性删除方式 52 | 53 | - 实现了HTTP长连接传输数据 54 | 55 | - 非阻塞I/O 56 | 57 | - epoll边缘触发模式(ET) 58 | 59 | - 线程池操作及其同步互斥管理 60 | 61 | - 调度选项 62 | 63 | - 队列式FIFO调度模式 64 | 65 | - 加入优先级的优先队列 (+) 66 | 67 | - 使用状态机解析HTTP协议,非简单字符串匹配方式解析请求 68 | 69 | - v1.1修改了CPU负载较高问题,修改后1000并发各线程(4worker)CPU使用率10%左右。 70 | 71 | **Feature** 72 | 73 | - v2.0实现Json解释器解析配置 74 | 75 | - v3.0实现FastCGI(功能扩展) 76 | 77 | - v4.0实现服务器缓存(性能加速) 78 | 79 | - v5.0实现反向代理(安全性及负载均衡) 80 | 81 | --- 82 | 83 | [![Build Status](https://travis-ci.org/linw7/TKeed.svg?branch=master)](https://travis-ci.org/linw7/TKeed) 84 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) 85 | 86 | --- -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/3.8.2/CMakeCCompiler.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_C_COMPILER "C:/MinGW/bin/gcc.exe") 2 | set(CMAKE_C_COMPILER_ARG1 "") 3 | set(CMAKE_C_COMPILER_ID "GNU") 4 | set(CMAKE_C_COMPILER_VERSION "5.3.0") 5 | set(CMAKE_C_COMPILER_WRAPPER "") 6 | set(CMAKE_C_STANDARD_COMPUTED_DEFAULT "11") 7 | set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert") 8 | set(CMAKE_C90_COMPILE_FEATURES "c_std_90;c_function_prototypes") 9 | set(CMAKE_C99_COMPILE_FEATURES "c_std_99;c_restrict;c_variadic_macros") 10 | set(CMAKE_C11_COMPILE_FEATURES "c_std_11;c_static_assert") 11 | 12 | set(CMAKE_C_PLATFORM_ID "MinGW") 13 | set(CMAKE_C_SIMULATE_ID "") 14 | set(CMAKE_C_SIMULATE_VERSION "") 15 | 16 | set(CMAKE_AR "C:/MinGW/bin/ar.exe") 17 | set(CMAKE_RANLIB "C:/MinGW/bin/ranlib.exe") 18 | set(CMAKE_LINKER "C:/MinGW/bin/ld.exe") 19 | set(CMAKE_COMPILER_IS_GNUCC 1) 20 | set(CMAKE_C_COMPILER_LOADED 1) 21 | set(CMAKE_C_COMPILER_WORKS TRUE) 22 | set(CMAKE_C_ABI_COMPILED TRUE) 23 | set(CMAKE_COMPILER_IS_MINGW 1) 24 | set(CMAKE_COMPILER_IS_CYGWIN ) 25 | if(CMAKE_COMPILER_IS_CYGWIN) 26 | set(CYGWIN 1) 27 | set(UNIX 1) 28 | endif() 29 | 30 | set(CMAKE_C_COMPILER_ENV_VAR "CC") 31 | 32 | if(CMAKE_COMPILER_IS_MINGW) 33 | set(MINGW 1) 34 | endif() 35 | set(CMAKE_C_COMPILER_ID_RUN 1) 36 | set(CMAKE_C_SOURCE_FILE_EXTENSIONS c;m) 37 | set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC) 38 | set(CMAKE_C_LINKER_PREFERENCE 10) 39 | 40 | # Save compiler ABI information. 41 | set(CMAKE_C_SIZEOF_DATA_PTR "4") 42 | set(CMAKE_C_COMPILER_ABI "") 43 | set(CMAKE_C_LIBRARY_ARCHITECTURE "") 44 | 45 | if(CMAKE_C_SIZEOF_DATA_PTR) 46 | set(CMAKE_SIZEOF_VOID_P "${CMAKE_C_SIZEOF_DATA_PTR}") 47 | endif() 48 | 49 | if(CMAKE_C_COMPILER_ABI) 50 | set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_C_COMPILER_ABI}") 51 | endif() 52 | 53 | if(CMAKE_C_LIBRARY_ARCHITECTURE) 54 | set(CMAKE_LIBRARY_ARCHITECTURE "") 55 | endif() 56 | 57 | set(CMAKE_C_CL_SHOWINCLUDES_PREFIX "") 58 | if(CMAKE_C_CL_SHOWINCLUDES_PREFIX) 59 | set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_C_CL_SHOWINCLUDES_PREFIX}") 60 | endif() 61 | 62 | 63 | 64 | 65 | 66 | set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "mingw32;moldname;mingwex;msvcrt;advapi32;shell32;user32;kernel32;mingw32;moldname;mingwex;msvcrt") 67 | set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "C:/MinGW/lib/gcc/mingw32/5.3.0;C:/MinGW/lib/gcc;C:/MinGW/mingw32/lib;C:/MinGW/lib") 68 | set(CMAKE_C_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "") 69 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/3.8.2/CMakeCXXCompiler.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_COMPILER "C:/MinGW/bin/g++.exe") 2 | set(CMAKE_CXX_COMPILER_ARG1 "") 3 | set(CMAKE_CXX_COMPILER_ID "GNU") 4 | set(CMAKE_CXX_COMPILER_VERSION "5.3.0") 5 | set(CMAKE_CXX_COMPILER_WRAPPER "") 6 | set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "98") 7 | set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17") 8 | set(CMAKE_CXX98_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters") 9 | set(CMAKE_CXX11_COMPILE_FEATURES "cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") 10 | set(CMAKE_CXX14_COMPILE_FEATURES "cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") 11 | set(CMAKE_CXX17_COMPILE_FEATURES "cxx_std_17") 12 | 13 | set(CMAKE_CXX_PLATFORM_ID "MinGW") 14 | set(CMAKE_CXX_SIMULATE_ID "") 15 | set(CMAKE_CXX_SIMULATE_VERSION "") 16 | 17 | set(CMAKE_AR "C:/MinGW/bin/ar.exe") 18 | set(CMAKE_RANLIB "C:/MinGW/bin/ranlib.exe") 19 | set(CMAKE_LINKER "C:/MinGW/bin/ld.exe") 20 | set(CMAKE_COMPILER_IS_GNUCXX 1) 21 | set(CMAKE_CXX_COMPILER_LOADED 1) 22 | set(CMAKE_CXX_COMPILER_WORKS TRUE) 23 | set(CMAKE_CXX_ABI_COMPILED TRUE) 24 | set(CMAKE_COMPILER_IS_MINGW 1) 25 | set(CMAKE_COMPILER_IS_CYGWIN ) 26 | if(CMAKE_COMPILER_IS_CYGWIN) 27 | set(CYGWIN 1) 28 | set(UNIX 1) 29 | endif() 30 | 31 | set(CMAKE_CXX_COMPILER_ENV_VAR "CXX") 32 | 33 | if(CMAKE_COMPILER_IS_MINGW) 34 | set(MINGW 1) 35 | endif() 36 | set(CMAKE_CXX_COMPILER_ID_RUN 1) 37 | set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC) 38 | set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;mm;CPP) 39 | set(CMAKE_CXX_LINKER_PREFERENCE 30) 40 | set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1) 41 | 42 | # Save compiler ABI information. 43 | set(CMAKE_CXX_SIZEOF_DATA_PTR "4") 44 | set(CMAKE_CXX_COMPILER_ABI "") 45 | set(CMAKE_CXX_LIBRARY_ARCHITECTURE "") 46 | 47 | if(CMAKE_CXX_SIZEOF_DATA_PTR) 48 | set(CMAKE_SIZEOF_VOID_P "${CMAKE_CXX_SIZEOF_DATA_PTR}") 49 | endif() 50 | 51 | if(CMAKE_CXX_COMPILER_ABI) 52 | set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_CXX_COMPILER_ABI}") 53 | endif() 54 | 55 | if(CMAKE_CXX_LIBRARY_ARCHITECTURE) 56 | set(CMAKE_LIBRARY_ARCHITECTURE "") 57 | endif() 58 | 59 | set(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX "") 60 | if(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX) 61 | set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_CXX_CL_SHOWINCLUDES_PREFIX}") 62 | endif() 63 | 64 | 65 | 66 | 67 | 68 | set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "stdc++;mingw32;moldname;mingwex;msvcrt;advapi32;shell32;user32;kernel32;mingw32;moldname;mingwex;msvcrt") 69 | set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "C:/MinGW/lib/gcc/mingw32/5.3.0;C:/MinGW/lib/gcc;C:/MinGW/mingw32/lib;C:/MinGW/lib") 70 | set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "") 71 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/3.8.2/CMakeDetermineCompilerABI_C.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/src_code/cmake-build-debug/CMakeFiles/3.8.2/CMakeDetermineCompilerABI_C.bin -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/3.8.2/CMakeDetermineCompilerABI_CXX.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/src_code/cmake-build-debug/CMakeFiles/3.8.2/CMakeDetermineCompilerABI_CXX.bin -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/3.8.2/CMakeRCCompiler.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_RC_COMPILER "C:/MinGW/bin/windres.exe") 2 | set(CMAKE_RC_COMPILER_ARG1 "") 3 | set(CMAKE_RC_COMPILER_LOADED 1) 4 | set(CMAKE_RC_SOURCE_FILE_EXTENSIONS rc;RC) 5 | set(CMAKE_RC_OUTPUT_EXTENSION .obj) 6 | set(CMAKE_RC_COMPILER_ENV_VAR "RC") 7 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/3.8.2/CMakeSystem.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_HOST_SYSTEM "Windows-10.0.10240") 2 | set(CMAKE_HOST_SYSTEM_NAME "Windows") 3 | set(CMAKE_HOST_SYSTEM_VERSION "10.0.10240") 4 | set(CMAKE_HOST_SYSTEM_PROCESSOR "AMD64") 5 | 6 | 7 | 8 | set(CMAKE_SYSTEM "Windows-10.0.10240") 9 | set(CMAKE_SYSTEM_NAME "Windows") 10 | set(CMAKE_SYSTEM_VERSION "10.0.10240") 11 | set(CMAKE_SYSTEM_PROCESSOR "AMD64") 12 | 13 | set(CMAKE_CROSSCOMPILING "FALSE") 14 | 15 | set(CMAKE_SYSTEM_LOADED 1) 16 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/CMakeDirectoryInformation.cmake: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "MinGW Makefiles" Generator, CMake Version 3.8 3 | 4 | # Relative path conversion top directories. 5 | set(CMAKE_RELATIVE_PATH_TOP_SOURCE "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code") 6 | set(CMAKE_RELATIVE_PATH_TOP_BINARY "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug") 7 | 8 | # Force unix paths in dependencies. 9 | set(CMAKE_FORCE_UNIX_PATHS 1) 10 | 11 | 12 | # The C and CXX include file regular expressions for this directory. 13 | set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") 14 | set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") 15 | set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) 16 | set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) 17 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/Makefile.cmake: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "MinGW Makefiles" Generator, CMake Version 3.8 3 | 4 | # The generator used is: 5 | set(CMAKE_DEPENDS_GENERATOR "MinGW Makefiles") 6 | 7 | # The top level Makefile was generated from the following files: 8 | set(CMAKE_MAKEFILE_DEPENDS 9 | "CMakeCache.txt" 10 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeCInformation.cmake" 11 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeCXXInformation.cmake" 12 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeCommonLanguageInclude.cmake" 13 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeExtraGeneratorDetermineCompilerMacrosAndIncludeDirs.cmake" 14 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeFindCodeBlocks.cmake" 15 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeGenericSystem.cmake" 16 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeLanguageInformation.cmake" 17 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeRCInformation.cmake" 18 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeSystemSpecificInformation.cmake" 19 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/CMakeSystemSpecificInitialize.cmake" 20 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Compiler/GNU-C.cmake" 21 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Compiler/GNU-CXX.cmake" 22 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Compiler/GNU.cmake" 23 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Platform/Windows-GNU-C-ABI.cmake" 24 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Platform/Windows-GNU-C.cmake" 25 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Platform/Windows-GNU-CXX-ABI.cmake" 26 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Platform/Windows-GNU-CXX.cmake" 27 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Platform/Windows-GNU.cmake" 28 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Platform/Windows-windres.cmake" 29 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Platform/Windows.cmake" 30 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/Platform/WindowsPaths.cmake" 31 | "C:/Program Files/JetBrains/CLion 2017.2.1/bin/cmake/share/cmake-3.8/Modules/ProcessorCount.cmake" 32 | "../CMakeLists.txt" 33 | "CMakeFiles/3.8.2/CMakeCCompiler.cmake" 34 | "CMakeFiles/3.8.2/CMakeCXXCompiler.cmake" 35 | "CMakeFiles/3.8.2/CMakeRCCompiler.cmake" 36 | "CMakeFiles/3.8.2/CMakeSystem.cmake" 37 | ) 38 | 39 | # The corresponding makefile is: 40 | set(CMAKE_MAKEFILE_OUTPUTS 41 | "Makefile" 42 | "CMakeFiles/cmake.check_cache" 43 | ) 44 | 45 | # Byproducts of CMake generate step: 46 | set(CMAKE_MAKEFILE_PRODUCTS 47 | "CMakeFiles/CMakeDirectoryInformation.cmake" 48 | ) 49 | 50 | # Dependency information for all targets: 51 | set(CMAKE_DEPEND_INFO_FILES 52 | "CMakeFiles/src_code.dir/DependInfo.cmake" 53 | ) 54 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/Makefile2: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "MinGW Makefiles" Generator, CMake Version 3.8 3 | 4 | # Default target executed when no arguments are given to make. 5 | default_target: all 6 | 7 | .PHONY : default_target 8 | 9 | # The main recursive all target 10 | all: 11 | 12 | .PHONY : all 13 | 14 | # The main recursive preinstall target 15 | preinstall: 16 | 17 | .PHONY : preinstall 18 | 19 | #============================================================================= 20 | # Special targets provided by cmake. 21 | 22 | # Disable implicit rules so canonical targets will work. 23 | .SUFFIXES: 24 | 25 | 26 | # Remove some rules from gmake that .SUFFIXES does not remove. 27 | SUFFIXES = 28 | 29 | .SUFFIXES: .hpux_make_needs_suffix_list 30 | 31 | 32 | # Suppress display of executed commands. 33 | $(VERBOSE).SILENT: 34 | 35 | 36 | # A target that is always out of date. 37 | cmake_force: 38 | 39 | .PHONY : cmake_force 40 | 41 | #============================================================================= 42 | # Set environment variables for the build. 43 | 44 | SHELL = cmd.exe 45 | 46 | # The CMake executable. 47 | CMAKE_COMMAND = "C:\Program Files\JetBrains\CLion 2017.2.1\bin\cmake\bin\cmake.exe" 48 | 49 | # The command to remove a file. 50 | RM = "C:\Program Files\JetBrains\CLion 2017.2.1\bin\cmake\bin\cmake.exe" -E remove -f 51 | 52 | # Escaping for special characters. 53 | EQUALS = = 54 | 55 | # The top-level source directory on which CMake was run. 56 | CMAKE_SOURCE_DIR = C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code 57 | 58 | # The top-level build directory on which CMake was run. 59 | CMAKE_BINARY_DIR = C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code\cmake-build-debug 60 | 61 | #============================================================================= 62 | # Target rules for target CMakeFiles/src_code.dir 63 | 64 | # All Build rule for target. 65 | CMakeFiles/src_code.dir/all: 66 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/depend 67 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/build 68 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --progress-dir=C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code\cmake-build-debug\CMakeFiles --progress-num=1,2,3,4,5,6,7,8,9,10,11 "Built target src_code" 69 | .PHONY : CMakeFiles/src_code.dir/all 70 | 71 | # Include target in all. 72 | all: CMakeFiles/src_code.dir/all 73 | 74 | .PHONY : all 75 | 76 | # Build rule for subdir invocation for target. 77 | CMakeFiles/src_code.dir/rule: cmake_check_build_system 78 | $(CMAKE_COMMAND) -E cmake_progress_start C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code\cmake-build-debug\CMakeFiles 11 79 | $(MAKE) -f CMakeFiles\Makefile2 CMakeFiles/src_code.dir/all 80 | $(CMAKE_COMMAND) -E cmake_progress_start C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code\cmake-build-debug\CMakeFiles 0 81 | .PHONY : CMakeFiles/src_code.dir/rule 82 | 83 | # Convenience name for target. 84 | src_code: CMakeFiles/src_code.dir/rule 85 | 86 | .PHONY : src_code 87 | 88 | # clean rule for target. 89 | CMakeFiles/src_code.dir/clean: 90 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/clean 91 | .PHONY : CMakeFiles/src_code.dir/clean 92 | 93 | # clean rule for target. 94 | clean: CMakeFiles/src_code.dir/clean 95 | 96 | .PHONY : clean 97 | 98 | #============================================================================= 99 | # Special targets to cleanup operation of make. 100 | 101 | # Special rule to run CMake to check the build system integrity. 102 | # No rule that depends on this can have commands that come from listfiles 103 | # because they might be regenerated. 104 | cmake_check_build_system: 105 | $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles\Makefile.cmake 0 106 | .PHONY : cmake_check_build_system 107 | 108 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/TargetDirectories.txt: -------------------------------------------------------------------------------- 1 | C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir 2 | C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/edit_cache.dir 3 | C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/rebuild_cache.dir 4 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/clion-environment.txt: -------------------------------------------------------------------------------- 1 | ToolSet: 5.0@C:\MinGW 2 | Options: 3 | CMake: 3.8.2@C:\Program Files\JetBrains\CLion 2017.2.1\bin\cmake\bin\cmake.exe 4 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/clion-log.txt: -------------------------------------------------------------------------------- 1 | "C:\Program Files\JetBrains\CLion 2017.2.1\bin\cmake\bin\cmake.exe" -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - MinGW Makefiles" C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code 2 | -- Configuring done 3 | -- Generating done 4 | -- Build files have been written to: C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug 5 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/cmake.check_cache: -------------------------------------------------------------------------------- 1 | # This file is generated by cmake for dependency checking of the CMakeCache.txt file 2 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/feature_tests.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/src_code/cmake-build-debug/CMakeFiles/feature_tests.bin -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/feature_tests.c: -------------------------------------------------------------------------------- 1 | 2 | const char features[] = {"\n" 3 | "C_FEATURE:" 4 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 304 5 | "1" 6 | #else 7 | "0" 8 | #endif 9 | "c_function_prototypes\n" 10 | "C_FEATURE:" 11 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 304 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L 12 | "1" 13 | #else 14 | "0" 15 | #endif 16 | "c_restrict\n" 17 | "C_FEATURE:" 18 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201000L 19 | "1" 20 | #else 21 | "0" 22 | #endif 23 | "c_static_assert\n" 24 | "C_FEATURE:" 25 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 304 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L 26 | "1" 27 | #else 28 | "0" 29 | #endif 30 | "c_variadic_macros\n" 31 | 32 | }; 33 | 34 | int main(int argc, char** argv) { (void)argv; return features[argc]; } 35 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/feature_tests.cxx: -------------------------------------------------------------------------------- 1 | 2 | const char features[] = {"\n" 3 | "CXX_FEATURE:" 4 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L 5 | "1" 6 | #else 7 | "0" 8 | #endif 9 | "cxx_aggregate_default_initializers\n" 10 | "CXX_FEATURE:" 11 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L 12 | "1" 13 | #else 14 | "0" 15 | #endif 16 | "cxx_alias_templates\n" 17 | "CXX_FEATURE:" 18 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L 19 | "1" 20 | #else 21 | "0" 22 | #endif 23 | "cxx_alignas\n" 24 | "CXX_FEATURE:" 25 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L 26 | "1" 27 | #else 28 | "0" 29 | #endif 30 | "cxx_alignof\n" 31 | "CXX_FEATURE:" 32 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L 33 | "1" 34 | #else 35 | "0" 36 | #endif 37 | "cxx_attributes\n" 38 | "CXX_FEATURE:" 39 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L 40 | "1" 41 | #else 42 | "0" 43 | #endif 44 | "cxx_attribute_deprecated\n" 45 | "CXX_FEATURE:" 46 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 47 | "1" 48 | #else 49 | "0" 50 | #endif 51 | "cxx_auto_type\n" 52 | "CXX_FEATURE:" 53 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L 54 | "1" 55 | #else 56 | "0" 57 | #endif 58 | "cxx_binary_literals\n" 59 | "CXX_FEATURE:" 60 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 61 | "1" 62 | #else 63 | "0" 64 | #endif 65 | "cxx_constexpr\n" 66 | "CXX_FEATURE:" 67 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L 68 | "1" 69 | #else 70 | "0" 71 | #endif 72 | "cxx_contextual_conversions\n" 73 | "CXX_FEATURE:" 74 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 75 | "1" 76 | #else 77 | "0" 78 | #endif 79 | "cxx_decltype\n" 80 | "CXX_FEATURE:" 81 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L 82 | "1" 83 | #else 84 | "0" 85 | #endif 86 | "cxx_decltype_auto\n" 87 | "CXX_FEATURE:" 88 | #if ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L 89 | "1" 90 | #else 91 | "0" 92 | #endif 93 | "cxx_decltype_incomplete_return_types\n" 94 | "CXX_FEATURE:" 95 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 96 | "1" 97 | #else 98 | "0" 99 | #endif 100 | "cxx_default_function_template_args\n" 101 | "CXX_FEATURE:" 102 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 103 | "1" 104 | #else 105 | "0" 106 | #endif 107 | "cxx_defaulted_functions\n" 108 | "CXX_FEATURE:" 109 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 110 | "1" 111 | #else 112 | "0" 113 | #endif 114 | "cxx_defaulted_move_initializers\n" 115 | "CXX_FEATURE:" 116 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L 117 | "1" 118 | #else 119 | "0" 120 | #endif 121 | "cxx_delegating_constructors\n" 122 | "CXX_FEATURE:" 123 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 124 | "1" 125 | #else 126 | "0" 127 | #endif 128 | "cxx_deleted_functions\n" 129 | "CXX_FEATURE:" 130 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L 131 | "1" 132 | #else 133 | "0" 134 | #endif 135 | "cxx_digit_separators\n" 136 | "CXX_FEATURE:" 137 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 138 | "1" 139 | #else 140 | "0" 141 | #endif 142 | "cxx_enum_forward_declarations\n" 143 | "CXX_FEATURE:" 144 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 145 | "1" 146 | #else 147 | "0" 148 | #endif 149 | "cxx_explicit_conversions\n" 150 | "CXX_FEATURE:" 151 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L 152 | "1" 153 | #else 154 | "0" 155 | #endif 156 | "cxx_extended_friend_declarations\n" 157 | "CXX_FEATURE:" 158 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 159 | "1" 160 | #else 161 | "0" 162 | #endif 163 | "cxx_extern_templates\n" 164 | "CXX_FEATURE:" 165 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L 166 | "1" 167 | #else 168 | "0" 169 | #endif 170 | "cxx_final\n" 171 | "CXX_FEATURE:" 172 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 173 | "1" 174 | #else 175 | "0" 176 | #endif 177 | "cxx_func_identifier\n" 178 | "CXX_FEATURE:" 179 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 180 | "1" 181 | #else 182 | "0" 183 | #endif 184 | "cxx_generalized_initializers\n" 185 | "CXX_FEATURE:" 186 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L 187 | "1" 188 | #else 189 | "0" 190 | #endif 191 | "cxx_generic_lambdas\n" 192 | "CXX_FEATURE:" 193 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L 194 | "1" 195 | #else 196 | "0" 197 | #endif 198 | "cxx_inheriting_constructors\n" 199 | "CXX_FEATURE:" 200 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 201 | "1" 202 | #else 203 | "0" 204 | #endif 205 | "cxx_inline_namespaces\n" 206 | "CXX_FEATURE:" 207 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 208 | "1" 209 | #else 210 | "0" 211 | #endif 212 | "cxx_lambdas\n" 213 | "CXX_FEATURE:" 214 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L 215 | "1" 216 | #else 217 | "0" 218 | #endif 219 | "cxx_lambda_init_captures\n" 220 | "CXX_FEATURE:" 221 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 222 | "1" 223 | #else 224 | "0" 225 | #endif 226 | "cxx_local_type_template_args\n" 227 | "CXX_FEATURE:" 228 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 229 | "1" 230 | #else 231 | "0" 232 | #endif 233 | "cxx_long_long_type\n" 234 | "CXX_FEATURE:" 235 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 236 | "1" 237 | #else 238 | "0" 239 | #endif 240 | "cxx_noexcept\n" 241 | "CXX_FEATURE:" 242 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L 243 | "1" 244 | #else 245 | "0" 246 | #endif 247 | "cxx_nonstatic_member_init\n" 248 | "CXX_FEATURE:" 249 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 250 | "1" 251 | #else 252 | "0" 253 | #endif 254 | "cxx_nullptr\n" 255 | "CXX_FEATURE:" 256 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L 257 | "1" 258 | #else 259 | "0" 260 | #endif 261 | "cxx_override\n" 262 | "CXX_FEATURE:" 263 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 264 | "1" 265 | #else 266 | "0" 267 | #endif 268 | "cxx_range_for\n" 269 | "CXX_FEATURE:" 270 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 271 | "1" 272 | #else 273 | "0" 274 | #endif 275 | "cxx_raw_string_literals\n" 276 | "CXX_FEATURE:" 277 | #if ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L 278 | "1" 279 | #else 280 | "0" 281 | #endif 282 | "cxx_reference_qualified_functions\n" 283 | "CXX_FEATURE:" 284 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L 285 | "1" 286 | #else 287 | "0" 288 | #endif 289 | "cxx_relaxed_constexpr\n" 290 | "CXX_FEATURE:" 291 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L 292 | "1" 293 | #else 294 | "0" 295 | #endif 296 | "cxx_return_type_deduction\n" 297 | "CXX_FEATURE:" 298 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 299 | "1" 300 | #else 301 | "0" 302 | #endif 303 | "cxx_right_angle_brackets\n" 304 | "CXX_FEATURE:" 305 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 306 | "1" 307 | #else 308 | "0" 309 | #endif 310 | "cxx_rvalue_references\n" 311 | "CXX_FEATURE:" 312 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 313 | "1" 314 | #else 315 | "0" 316 | #endif 317 | "cxx_sizeof_member\n" 318 | "CXX_FEATURE:" 319 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 320 | "1" 321 | #else 322 | "0" 323 | #endif 324 | "cxx_static_assert\n" 325 | "CXX_FEATURE:" 326 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 327 | "1" 328 | #else 329 | "0" 330 | #endif 331 | "cxx_strong_enums\n" 332 | "CXX_FEATURE:" 333 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && __cplusplus 334 | "1" 335 | #else 336 | "0" 337 | #endif 338 | "cxx_template_template_parameters\n" 339 | "CXX_FEATURE:" 340 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L 341 | "1" 342 | #else 343 | "0" 344 | #endif 345 | "cxx_thread_local\n" 346 | "CXX_FEATURE:" 347 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 348 | "1" 349 | #else 350 | "0" 351 | #endif 352 | "cxx_trailing_return_types\n" 353 | "CXX_FEATURE:" 354 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 355 | "1" 356 | #else 357 | "0" 358 | #endif 359 | "cxx_unicode_literals\n" 360 | "CXX_FEATURE:" 361 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 362 | "1" 363 | #else 364 | "0" 365 | #endif 366 | "cxx_uniform_initialization\n" 367 | "CXX_FEATURE:" 368 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 369 | "1" 370 | #else 371 | "0" 372 | #endif 373 | "cxx_unrestricted_unions\n" 374 | "CXX_FEATURE:" 375 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L 376 | "1" 377 | #else 378 | "0" 379 | #endif 380 | "cxx_user_literals\n" 381 | "CXX_FEATURE:" 382 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L 383 | "1" 384 | #else 385 | "0" 386 | #endif 387 | "cxx_variable_templates\n" 388 | "CXX_FEATURE:" 389 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 390 | "1" 391 | #else 392 | "0" 393 | #endif 394 | "cxx_variadic_macros\n" 395 | "CXX_FEATURE:" 396 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) 397 | "1" 398 | #else 399 | "0" 400 | #endif 401 | "cxx_variadic_templates\n" 402 | 403 | }; 404 | 405 | int main(int argc, char** argv) { (void)argv; return features[argc]; } 406 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/progress.marks: -------------------------------------------------------------------------------- 1 | 11 2 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/src_code.dir/DependInfo.cmake: -------------------------------------------------------------------------------- 1 | # The set of languages for which implicit dependencies are needed: 2 | set(CMAKE_DEPENDS_LANGUAGES 3 | "C" 4 | ) 5 | # The set of files for implicit dependencies of each language: 6 | set(CMAKE_DEPENDS_CHECK_C 7 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/epoll.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/epoll.c.obj" 8 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/http.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/http.c.obj" 9 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/http_parse.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/http_parse.c.obj" 10 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/http_request.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/http_request.c.obj" 11 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/main.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/main.c.obj" 12 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/priority_queue.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/priority_queue.c.obj" 13 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/rio.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/rio.c.obj" 14 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/threadpool.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/threadpool.c.obj" 15 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/timer.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/timer.c.obj" 16 | "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/util.c" "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/CMakeFiles/src_code.dir/util.c.obj" 17 | ) 18 | set(CMAKE_C_COMPILER_ID "GNU") 19 | 20 | # The include file search paths: 21 | set(CMAKE_C_TARGET_INCLUDE_PATH 22 | ) 23 | 24 | # Targets to which this target links. 25 | set(CMAKE_TARGET_LINKED_INFO_FILES 26 | ) 27 | 28 | # Fortran module output directory. 29 | set(CMAKE_Fortran_TARGET_MODULE_DIR "") 30 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/src_code.dir/cmake_clean.cmake: -------------------------------------------------------------------------------- 1 | file(REMOVE_RECURSE 2 | "CMakeFiles/src_code.dir/epoll.c.obj" 3 | "CMakeFiles/src_code.dir/http.c.obj" 4 | "CMakeFiles/src_code.dir/http_parse.c.obj" 5 | "CMakeFiles/src_code.dir/http_request.c.obj" 6 | "CMakeFiles/src_code.dir/main.c.obj" 7 | "CMakeFiles/src_code.dir/priority_queue.c.obj" 8 | "CMakeFiles/src_code.dir/rio.c.obj" 9 | "CMakeFiles/src_code.dir/threadpool.c.obj" 10 | "CMakeFiles/src_code.dir/timer.c.obj" 11 | "CMakeFiles/src_code.dir/util.c.obj" 12 | "src_code.pdb" 13 | "src_code.exe" 14 | "src_code.exe.manifest" 15 | "libsrc_code.dll.a" 16 | ) 17 | 18 | # Per-language clean rules from dependency scanning. 19 | foreach(lang C) 20 | include(CMakeFiles/src_code.dir/cmake_clean_${lang}.cmake OPTIONAL) 21 | endforeach() 22 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/src_code.dir/depend.make: -------------------------------------------------------------------------------- 1 | # Empty dependencies file for src_code. 2 | # This may be replaced when dependencies are built. 3 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/src_code.dir/flags.make: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "MinGW Makefiles" Generator, CMake Version 3.8 3 | 4 | # compile C with C:/MinGW/bin/gcc.exe 5 | C_FLAGS = -g 6 | 7 | C_DEFINES = 8 | 9 | C_INCLUDES = 10 | 11 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/src_code.dir/link.txt: -------------------------------------------------------------------------------- 1 | "C:\Program Files\JetBrains\CLion 2017.2.1\bin\cmake\bin\cmake.exe" -E remove -f CMakeFiles\src_code.dir/objects.a 2 | C:\MinGW\bin\ar.exe cr CMakeFiles\src_code.dir/objects.a @CMakeFiles\src_code.dir\objects1.rsp 3 | C:\MinGW\bin\gcc.exe -g -Wl,--whole-archive CMakeFiles\src_code.dir/objects.a -Wl,--no-whole-archive -o src_code.exe -Wl,--out-implib,libsrc_code.dll.a -Wl,--major-image-version,0,--minor-image-version,0 @CMakeFiles\src_code.dir\linklibs.rsp 4 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/src_code.dir/linklibs.rsp: -------------------------------------------------------------------------------- 1 | -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 2 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/src_code.dir/objects1.rsp: -------------------------------------------------------------------------------- 1 | CMakeFiles/src_code.dir/epoll.c.obj CMakeFiles/src_code.dir/http.c.obj CMakeFiles/src_code.dir/http_parse.c.obj CMakeFiles/src_code.dir/http_request.c.obj CMakeFiles/src_code.dir/main.c.obj CMakeFiles/src_code.dir/priority_queue.c.obj CMakeFiles/src_code.dir/rio.c.obj CMakeFiles/src_code.dir/threadpool.c.obj CMakeFiles/src_code.dir/timer.c.obj CMakeFiles/src_code.dir/util.c.obj 2 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/CMakeFiles/src_code.dir/progress.make: -------------------------------------------------------------------------------- 1 | CMAKE_PROGRESS_1 = 1 2 | CMAKE_PROGRESS_2 = 2 3 | CMAKE_PROGRESS_3 = 3 4 | CMAKE_PROGRESS_4 = 4 5 | CMAKE_PROGRESS_5 = 5 6 | CMAKE_PROGRESS_6 = 6 7 | CMAKE_PROGRESS_7 = 7 8 | CMAKE_PROGRESS_8 = 8 9 | CMAKE_PROGRESS_9 = 9 10 | CMAKE_PROGRESS_10 = 10 11 | CMAKE_PROGRESS_11 = 11 12 | 13 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/Makefile: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "MinGW Makefiles" Generator, CMake Version 3.8 3 | 4 | # Default target executed when no arguments are given to make. 5 | default_target: all 6 | 7 | .PHONY : default_target 8 | 9 | # Allow only one "make -f Makefile2" at a time, but pass parallelism. 10 | .NOTPARALLEL: 11 | 12 | 13 | #============================================================================= 14 | # Special targets provided by cmake. 15 | 16 | # Disable implicit rules so canonical targets will work. 17 | .SUFFIXES: 18 | 19 | 20 | # Remove some rules from gmake that .SUFFIXES does not remove. 21 | SUFFIXES = 22 | 23 | .SUFFIXES: .hpux_make_needs_suffix_list 24 | 25 | 26 | # Suppress display of executed commands. 27 | $(VERBOSE).SILENT: 28 | 29 | 30 | # A target that is always out of date. 31 | cmake_force: 32 | 33 | .PHONY : cmake_force 34 | 35 | #============================================================================= 36 | # Set environment variables for the build. 37 | 38 | SHELL = cmd.exe 39 | 40 | # The CMake executable. 41 | CMAKE_COMMAND = "C:\Program Files\JetBrains\CLion 2017.2.1\bin\cmake\bin\cmake.exe" 42 | 43 | # The command to remove a file. 44 | RM = "C:\Program Files\JetBrains\CLion 2017.2.1\bin\cmake\bin\cmake.exe" -E remove -f 45 | 46 | # Escaping for special characters. 47 | EQUALS = = 48 | 49 | # The top-level source directory on which CMake was run. 50 | CMAKE_SOURCE_DIR = C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code 51 | 52 | # The top-level build directory on which CMake was run. 53 | CMAKE_BINARY_DIR = C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code\cmake-build-debug 54 | 55 | #============================================================================= 56 | # Targets provided globally by CMake. 57 | 58 | # Special rule for the target edit_cache 59 | edit_cache: 60 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." 61 | "C:\Program Files\JetBrains\CLion 2017.2.1\bin\cmake\bin\cmake.exe" -E echo "No interactive CMake dialog available." 62 | .PHONY : edit_cache 63 | 64 | # Special rule for the target edit_cache 65 | edit_cache/fast: edit_cache 66 | 67 | .PHONY : edit_cache/fast 68 | 69 | # Special rule for the target rebuild_cache 70 | rebuild_cache: 71 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." 72 | "C:\Program Files\JetBrains\CLion 2017.2.1\bin\cmake\bin\cmake.exe" -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) 73 | .PHONY : rebuild_cache 74 | 75 | # Special rule for the target rebuild_cache 76 | rebuild_cache/fast: rebuild_cache 77 | 78 | .PHONY : rebuild_cache/fast 79 | 80 | # The main all target 81 | all: cmake_check_build_system 82 | $(CMAKE_COMMAND) -E cmake_progress_start C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code\cmake-build-debug\CMakeFiles C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code\cmake-build-debug\CMakeFiles\progress.marks 83 | $(MAKE) -f CMakeFiles\Makefile2 all 84 | $(CMAKE_COMMAND) -E cmake_progress_start C:\Users\TeeKee\Desktop\WorkSpace\TKeed\src_code\cmake-build-debug\CMakeFiles 0 85 | .PHONY : all 86 | 87 | # The main clean target 88 | clean: 89 | $(MAKE) -f CMakeFiles\Makefile2 clean 90 | .PHONY : clean 91 | 92 | # The main clean target 93 | clean/fast: clean 94 | 95 | .PHONY : clean/fast 96 | 97 | # Prepare targets for installation. 98 | preinstall: all 99 | $(MAKE) -f CMakeFiles\Makefile2 preinstall 100 | .PHONY : preinstall 101 | 102 | # Prepare targets for installation. 103 | preinstall/fast: 104 | $(MAKE) -f CMakeFiles\Makefile2 preinstall 105 | .PHONY : preinstall/fast 106 | 107 | # clear depends 108 | depend: 109 | $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles\Makefile.cmake 1 110 | .PHONY : depend 111 | 112 | #============================================================================= 113 | # Target rules for targets named src_code 114 | 115 | # Build rule for target. 116 | src_code: cmake_check_build_system 117 | $(MAKE) -f CMakeFiles\Makefile2 src_code 118 | .PHONY : src_code 119 | 120 | # fast build rule for target. 121 | src_code/fast: 122 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/build 123 | .PHONY : src_code/fast 124 | 125 | epoll.obj: epoll.c.obj 126 | 127 | .PHONY : epoll.obj 128 | 129 | # target to build an object file 130 | epoll.c.obj: 131 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/epoll.c.obj 132 | .PHONY : epoll.c.obj 133 | 134 | epoll.i: epoll.c.i 135 | 136 | .PHONY : epoll.i 137 | 138 | # target to preprocess a source file 139 | epoll.c.i: 140 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/epoll.c.i 141 | .PHONY : epoll.c.i 142 | 143 | epoll.s: epoll.c.s 144 | 145 | .PHONY : epoll.s 146 | 147 | # target to generate assembly for a file 148 | epoll.c.s: 149 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/epoll.c.s 150 | .PHONY : epoll.c.s 151 | 152 | http.obj: http.c.obj 153 | 154 | .PHONY : http.obj 155 | 156 | # target to build an object file 157 | http.c.obj: 158 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/http.c.obj 159 | .PHONY : http.c.obj 160 | 161 | http.i: http.c.i 162 | 163 | .PHONY : http.i 164 | 165 | # target to preprocess a source file 166 | http.c.i: 167 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/http.c.i 168 | .PHONY : http.c.i 169 | 170 | http.s: http.c.s 171 | 172 | .PHONY : http.s 173 | 174 | # target to generate assembly for a file 175 | http.c.s: 176 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/http.c.s 177 | .PHONY : http.c.s 178 | 179 | http_parse.obj: http_parse.c.obj 180 | 181 | .PHONY : http_parse.obj 182 | 183 | # target to build an object file 184 | http_parse.c.obj: 185 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/http_parse.c.obj 186 | .PHONY : http_parse.c.obj 187 | 188 | http_parse.i: http_parse.c.i 189 | 190 | .PHONY : http_parse.i 191 | 192 | # target to preprocess a source file 193 | http_parse.c.i: 194 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/http_parse.c.i 195 | .PHONY : http_parse.c.i 196 | 197 | http_parse.s: http_parse.c.s 198 | 199 | .PHONY : http_parse.s 200 | 201 | # target to generate assembly for a file 202 | http_parse.c.s: 203 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/http_parse.c.s 204 | .PHONY : http_parse.c.s 205 | 206 | http_request.obj: http_request.c.obj 207 | 208 | .PHONY : http_request.obj 209 | 210 | # target to build an object file 211 | http_request.c.obj: 212 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/http_request.c.obj 213 | .PHONY : http_request.c.obj 214 | 215 | http_request.i: http_request.c.i 216 | 217 | .PHONY : http_request.i 218 | 219 | # target to preprocess a source file 220 | http_request.c.i: 221 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/http_request.c.i 222 | .PHONY : http_request.c.i 223 | 224 | http_request.s: http_request.c.s 225 | 226 | .PHONY : http_request.s 227 | 228 | # target to generate assembly for a file 229 | http_request.c.s: 230 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/http_request.c.s 231 | .PHONY : http_request.c.s 232 | 233 | main.obj: main.c.obj 234 | 235 | .PHONY : main.obj 236 | 237 | # target to build an object file 238 | main.c.obj: 239 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/main.c.obj 240 | .PHONY : main.c.obj 241 | 242 | main.i: main.c.i 243 | 244 | .PHONY : main.i 245 | 246 | # target to preprocess a source file 247 | main.c.i: 248 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/main.c.i 249 | .PHONY : main.c.i 250 | 251 | main.s: main.c.s 252 | 253 | .PHONY : main.s 254 | 255 | # target to generate assembly for a file 256 | main.c.s: 257 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/main.c.s 258 | .PHONY : main.c.s 259 | 260 | priority_queue.obj: priority_queue.c.obj 261 | 262 | .PHONY : priority_queue.obj 263 | 264 | # target to build an object file 265 | priority_queue.c.obj: 266 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/priority_queue.c.obj 267 | .PHONY : priority_queue.c.obj 268 | 269 | priority_queue.i: priority_queue.c.i 270 | 271 | .PHONY : priority_queue.i 272 | 273 | # target to preprocess a source file 274 | priority_queue.c.i: 275 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/priority_queue.c.i 276 | .PHONY : priority_queue.c.i 277 | 278 | priority_queue.s: priority_queue.c.s 279 | 280 | .PHONY : priority_queue.s 281 | 282 | # target to generate assembly for a file 283 | priority_queue.c.s: 284 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/priority_queue.c.s 285 | .PHONY : priority_queue.c.s 286 | 287 | rio.obj: rio.c.obj 288 | 289 | .PHONY : rio.obj 290 | 291 | # target to build an object file 292 | rio.c.obj: 293 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/rio.c.obj 294 | .PHONY : rio.c.obj 295 | 296 | rio.i: rio.c.i 297 | 298 | .PHONY : rio.i 299 | 300 | # target to preprocess a source file 301 | rio.c.i: 302 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/rio.c.i 303 | .PHONY : rio.c.i 304 | 305 | rio.s: rio.c.s 306 | 307 | .PHONY : rio.s 308 | 309 | # target to generate assembly for a file 310 | rio.c.s: 311 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/rio.c.s 312 | .PHONY : rio.c.s 313 | 314 | threadpool.obj: threadpool.c.obj 315 | 316 | .PHONY : threadpool.obj 317 | 318 | # target to build an object file 319 | threadpool.c.obj: 320 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/threadpool.c.obj 321 | .PHONY : threadpool.c.obj 322 | 323 | threadpool.i: threadpool.c.i 324 | 325 | .PHONY : threadpool.i 326 | 327 | # target to preprocess a source file 328 | threadpool.c.i: 329 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/threadpool.c.i 330 | .PHONY : threadpool.c.i 331 | 332 | threadpool.s: threadpool.c.s 333 | 334 | .PHONY : threadpool.s 335 | 336 | # target to generate assembly for a file 337 | threadpool.c.s: 338 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/threadpool.c.s 339 | .PHONY : threadpool.c.s 340 | 341 | timer.obj: timer.c.obj 342 | 343 | .PHONY : timer.obj 344 | 345 | # target to build an object file 346 | timer.c.obj: 347 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/timer.c.obj 348 | .PHONY : timer.c.obj 349 | 350 | timer.i: timer.c.i 351 | 352 | .PHONY : timer.i 353 | 354 | # target to preprocess a source file 355 | timer.c.i: 356 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/timer.c.i 357 | .PHONY : timer.c.i 358 | 359 | timer.s: timer.c.s 360 | 361 | .PHONY : timer.s 362 | 363 | # target to generate assembly for a file 364 | timer.c.s: 365 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/timer.c.s 366 | .PHONY : timer.c.s 367 | 368 | util.obj: util.c.obj 369 | 370 | .PHONY : util.obj 371 | 372 | # target to build an object file 373 | util.c.obj: 374 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/util.c.obj 375 | .PHONY : util.c.obj 376 | 377 | util.i: util.c.i 378 | 379 | .PHONY : util.i 380 | 381 | # target to preprocess a source file 382 | util.c.i: 383 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/util.c.i 384 | .PHONY : util.c.i 385 | 386 | util.s: util.c.s 387 | 388 | .PHONY : util.s 389 | 390 | # target to generate assembly for a file 391 | util.c.s: 392 | $(MAKE) -f CMakeFiles\src_code.dir\build.make CMakeFiles/src_code.dir/util.c.s 393 | .PHONY : util.c.s 394 | 395 | # Help Target 396 | help: 397 | @echo The following are some of the valid targets for this Makefile: 398 | @echo ... all (the default if no target is provided) 399 | @echo ... clean 400 | @echo ... depend 401 | @echo ... src_code 402 | @echo ... edit_cache 403 | @echo ... rebuild_cache 404 | @echo ... epoll.obj 405 | @echo ... epoll.i 406 | @echo ... epoll.s 407 | @echo ... http.obj 408 | @echo ... http.i 409 | @echo ... http.s 410 | @echo ... http_parse.obj 411 | @echo ... http_parse.i 412 | @echo ... http_parse.s 413 | @echo ... http_request.obj 414 | @echo ... http_request.i 415 | @echo ... http_request.s 416 | @echo ... main.obj 417 | @echo ... main.i 418 | @echo ... main.s 419 | @echo ... priority_queue.obj 420 | @echo ... priority_queue.i 421 | @echo ... priority_queue.s 422 | @echo ... rio.obj 423 | @echo ... rio.i 424 | @echo ... rio.s 425 | @echo ... threadpool.obj 426 | @echo ... threadpool.i 427 | @echo ... threadpool.s 428 | @echo ... timer.obj 429 | @echo ... timer.i 430 | @echo ... timer.s 431 | @echo ... util.obj 432 | @echo ... util.i 433 | @echo ... util.s 434 | .PHONY : help 435 | 436 | 437 | 438 | #============================================================================= 439 | # Special targets to cleanup operation of make. 440 | 441 | # Special rule to run CMake to check the build system integrity. 442 | # No rule that depends on this can have commands that come from listfiles 443 | # because they might be regenerated. 444 | cmake_check_build_system: 445 | $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles\Makefile.cmake 0 446 | .PHONY : cmake_check_build_system 447 | 448 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/cmake_install.cmake: -------------------------------------------------------------------------------- 1 | # Install script for directory: C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code 2 | 3 | # Set the install prefix 4 | if(NOT DEFINED CMAKE_INSTALL_PREFIX) 5 | set(CMAKE_INSTALL_PREFIX "C:/Program Files (x86)/src_code") 6 | endif() 7 | string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") 8 | 9 | # Set the install configuration name. 10 | if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) 11 | if(BUILD_TYPE) 12 | string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" 13 | CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") 14 | else() 15 | set(CMAKE_INSTALL_CONFIG_NAME "Debug") 16 | endif() 17 | message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") 18 | endif() 19 | 20 | # Set the component getting installed. 21 | if(NOT CMAKE_INSTALL_COMPONENT) 22 | if(COMPONENT) 23 | message(STATUS "Install component: \"${COMPONENT}\"") 24 | set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") 25 | else() 26 | set(CMAKE_INSTALL_COMPONENT) 27 | endif() 28 | endif() 29 | 30 | if(CMAKE_INSTALL_COMPONENT) 31 | set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") 32 | else() 33 | set(CMAKE_INSTALL_MANIFEST "install_manifest.txt") 34 | endif() 35 | 36 | string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT 37 | "${CMAKE_INSTALL_MANIFEST_FILES}") 38 | file(WRITE "C:/Users/TeeKee/Desktop/WorkSpace/TKeed/src_code/cmake-build-debug/${CMAKE_INSTALL_MANIFEST}" 39 | "${CMAKE_INSTALL_MANIFEST_CONTENT}") 40 | -------------------------------------------------------------------------------- /src_code/cmake-build-debug/src_code.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 152 | 153 | -------------------------------------------------------------------------------- /src_code/epoll.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #include "epoll.h" 6 | 7 | struct epoll_event* events; 8 | 9 | int tk_epoll_create(int flags){ 10 | int epoll_fd = epoll_create1(flags); 11 | if(epoll_fd == -1) 12 | return -1; 13 | events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * MAXEVENTS); 14 | return epoll_fd; 15 | } 16 | 17 | // 注册新描述符 18 | int tk_epoll_add(int epoll_fd, int fd, tk_http_request_t* request, int events){ 19 | struct epoll_event event; 20 | event.data.ptr = (void*)request; 21 | event.events = events; 22 | int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event); 23 | if(ret == -1) 24 | return -1; 25 | } 26 | 27 | // 修改描述符状态 28 | int tk_epoll_mod(int epoll_fd, int fd, tk_http_request_t* request, int events){ 29 | struct epoll_event event; 30 | event.data.ptr = (void*)request; 31 | event.events = events; 32 | int ret = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event); 33 | if(ret == -1) 34 | return -1; 35 | } 36 | 37 | // 从epoll中删除描述符 38 | int tk_epoll_del(int epoll_fd, int fd, tk_http_request_t* request, int events){ 39 | struct epoll_event event; 40 | event.data.ptr = (void*)request; 41 | event.events = events; 42 | int ret = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event); 43 | if(ret == -1) 44 | return -1; 45 | } 46 | 47 | // 返回活跃事件数 48 | int tk_epoll_wait(int epoll_fd, struct epoll_event* events, int max_events, int timeout){ 49 | int ret_count = epoll_wait(epoll_fd, events, max_events, timeout); 50 | return ret_count; 51 | } 52 | 53 | // 分发处理函数 54 | void tk_handle_events(int epoll_fd, int listen_fd, struct epoll_event* events, 55 | int events_num, char* path, tk_threadpool_t* tp){ 56 | 57 | for(int i = 0; i < events_num; i++){ 58 | // 获取有事件产生的描述符 59 | tk_http_request_t* request = (tk_http_request_t*)(events[i].data.ptr); 60 | int fd = request->fd; 61 | 62 | // 有事件发生的描述符为监听描述符 63 | if(fd == listen_fd) { 64 | accept_connection(listen_fd, epoll_fd, path); 65 | } 66 | else{ 67 | // 有事件发生的描述符为连接描述符 68 | 69 | // 排除错误事件 70 | if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) 71 | || (!(events[i].events & EPOLLIN))){ 72 | close(fd); 73 | continue; 74 | } 75 | 76 | // 将请求任务加入到线程池中 77 | int rc = threadpool_add(tp, do_request, events[i].data.ptr); 78 | // do_request(events[i].data.ptr); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src_code/epoll.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef EPOLL_H 6 | #define EPOLL_H 7 | 8 | #include 9 | #include "http.h" 10 | #include "threadpool.h" 11 | 12 | #define MAXEVENTS 1024 13 | 14 | int tk_epoll_create(int flags); 15 | int tk_epoll_add(int epoll_fd, int fd, tk_http_request_t* request, int events); 16 | int tk_epoll_mod(int epoll_fd, int fd, tk_http_request_t* request, int events); 17 | int tk_epoll_del(int epoll_fd, int fd, tk_http_request_t* request, int events); 18 | int tk_epoll_wait(int epoll_fd, struct epoll_event *events, int max_events, int timeout); 19 | void tk_handle_events(int epoll_fd, int listen_fd, struct epoll_event* events, 20 | int events_num, char* path, tk_threadpool_t* tp); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src_code/error.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef ERROR_H 6 | #define ERROR_H 7 | 8 | #define TK_OK 0 9 | #define TK_ERROR -1 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src_code/http.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #include 6 | #include "http.h" 7 | 8 | static const char* get_file_type(const char *type); 9 | static void parse_uri(char *uri, int length, char *filename, char *query); 10 | static void do_error(int fd, char *cause, char *err_num, char *short_msg, char *long_msg); 11 | static void serve_static(int fd, char *filename, size_t filesize, tk_http_out_t *out); 12 | 13 | static char *ROOT = NULL; 14 | 15 | mime_type_t tkeed_mime[] = 16 | { 17 | {".html", "text/html"}, 18 | {".xml", "text/xml"}, 19 | {".xhtml", "application/xhtml+xml"}, 20 | {".txt", "text/plain"}, 21 | {".rtf", "application/rtf"}, 22 | {".pdf", "application/pdf"}, 23 | {".word", "application/msword"}, 24 | {".png", "image/png"}, 25 | {".gif", "image/gif"}, 26 | {".jpg", "image/jpeg"}, 27 | {".jpeg", "image/jpeg"}, 28 | {".au", "audio/basic"}, 29 | {".mpeg", "video/mpeg"}, 30 | {".mpg", "video/mpeg"}, 31 | {".avi", "video/x-msvideo"}, 32 | {".gz", "application/x-gzip"}, 33 | {".tar", "application/x-tar"}, 34 | {".css", "text/css"}, 35 | {NULL ,"text/plain"} 36 | }; 37 | 38 | static void parse_uri(char *uri_start, int uri_length, char *filename, char *query){ 39 | uri_start[uri_length] = '\0'; 40 | // 找到'?'位置界定非参部分 41 | char *delim_pos = strchr(uri_start, '?'); 42 | int filename_length = (delim_pos != NULL) ? ((int)(delim_pos - uri_start)) : uri_length; 43 | strcpy(filename, ROOT); 44 | // 将uri中属于'?'之前部分内容追加到filename 45 | strncat(filename, uri_start, filename_length); 46 | // 在请求中找到最后一个'/'位置界定文件位置 47 | char *last_comp = strrchr(filename, '/'); 48 | // 在文件名中找到最后一个'.'界定文件类型 49 | char *last_dot = strrchr(last_comp, '.'); 50 | // 请求文件时末尾加'/' 51 | if((last_dot == NULL) && (filename[strlen(filename) - 1] != '/')){ 52 | strcat(filename, "/"); 53 | } 54 | // 默认请求index.html 55 | if(filename[strlen(filename) - 1] == '/'){ 56 | strcat(filename, "index.html"); 57 | } 58 | return; 59 | } 60 | 61 | const char* get_file_type(const char *type){ 62 | // 将type和索引表中后缀比较,返回类型用于填充Content-Type字段 63 | for(int i = 0; tkeed_mime[i].type != NULL; ++i){ 64 | if(strcmp(type, tkeed_mime[i].type) == 0) 65 | return tkeed_mime[i].value; 66 | } 67 | // 未识别返回"text/plain" 68 | return "text/plain"; 69 | } 70 | 71 | // 响应错误信息 72 | void do_error(int fd, char *cause, char *err_num, char *short_msg, char *long_msg){ 73 | // 响应头缓冲(512字节)和数据缓冲(8192字节) 74 | char header[MAXLINE]; 75 | char body[MAXLINE]; 76 | 77 | // 用log_msg和cause字符串填充错误响应体 78 | sprintf(body, "TKeed Error<title>"); 79 | sprintf(body, "%s<body bgcolor=""ffffff"">\n", body); 80 | sprintf(body, "%s%s : %s\n", body, err_num, short_msg); 81 | sprintf(body, "%s<p>%s : %s\n</p>", body, long_msg, cause); 82 | sprintf(body, "%s<hr><em>TKeed web server</em>\n</body></html>", body); 83 | 84 | // 返回错误码,组织错误响应头 85 | sprintf(header, "HTTP/1.1 %s %s\r\n", err_num, short_msg); 86 | sprintf(header, "%sServer: TKeed\r\n", header); 87 | sprintf(header, "%sContent-type: text/html\r\n", header); 88 | sprintf(header, "%sConnection: close\r\n", header); 89 | sprintf(header, "%sContent-length: %d\r\n\r\n", header, (int)strlen(body)); 90 | 91 | // Add 404 Page 92 | 93 | // 发送错误信息 94 | rio_writen(fd, header, strlen(header)); 95 | rio_writen(fd, body, strlen(body)); 96 | return; 97 | } 98 | 99 | // 处理静态文件请求 100 | void serve_static(int fd, char *filename, size_t filesize, tk_http_out_t *out){ 101 | // 响应头缓冲(512字节)和数据缓冲(8192字节) 102 | char header[MAXLINE]; 103 | char buff[SHORTLINE]; 104 | struct tm tm; 105 | 106 | // 返回响应报文头,包含HTTP版本号状态码及状态码对应的短描述 107 | sprintf(header, "HTTP/1.1 %d %s\r\n", out->status, get_shortmsg_from_status_code(out->status)); 108 | 109 | // 返回响应头 110 | // Connection,Keep-Alive,Content-type,Content-length,Last-Modified 111 | if(out->keep_alive){ 112 | // 返回默认的持续连接模式及超时时间(默认500ms) 113 | sprintf(header, "%sConnection: keep-alive\r\n", header); 114 | sprintf(header, "%sKeep-Alive: timeout=%d\r\n", header, TIMEOUT_DEFAULT); 115 | } 116 | if(out->modified){ 117 | // 得到文件类型并填充Content-type字段 118 | const char* filetype = get_file_type(strrchr(filename, '.')); 119 | sprintf(header, "%sContent-type: %s\r\n", header, filetype); 120 | // 通过Content-length返回文件大小 121 | sprintf(header, "%sContent-length: %zu\r\n", header, filesize); 122 | // 得到最后修改时间并填充Last-Modified字段 123 | localtime_r(&(out->mtime), &tm); 124 | strftime(buff, SHORTLINE, "%a, %d %b %Y %H:%M:%S GMT", &tm); 125 | sprintf(header, "%sLast-Modified: %s\r\n", header, buff); 126 | } 127 | sprintf(header, "%sServer : TKeed\r\n", header); 128 | 129 | // 空行(must) 130 | sprintf(header, "%s\r\n", header); 131 | 132 | // 发送报文头部并校验完整性 133 | size_t send_len = (size_t)rio_writen(fd, header, strlen(header)); 134 | if(send_len != strlen(header)){ 135 | perror("Send header failed"); 136 | return; 137 | } 138 | 139 | // 打开并发送文件 140 | int src_fd = open(filename, O_RDONLY, 0); 141 | char *src_addr = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, src_fd, 0); 142 | close(src_fd); 143 | 144 | // 发送文件并校验完整性 145 | send_len = rio_writen(fd, src_addr, filesize); 146 | if(send_len != filesize){ 147 | perror("Send file failed"); 148 | return; 149 | } 150 | munmap(src_addr, filesize); 151 | } 152 | 153 | int error_proess(struct stat* sbufptr, char *filename, int fd){ 154 | // 处理文件找不到错误 155 | if(stat(filename, sbufptr) < 0){ 156 | do_error(fd, filename, "404", "Not Found", "TKeed can't find the file"); 157 | return 1; 158 | } 159 | 160 | // 处理权限错误 161 | if(!(S_ISREG((*sbufptr).st_mode)) || !(S_IRUSR & (*sbufptr).st_mode)){ 162 | do_error(fd, filename, "403", "Forbidden", "TKeed can't read the file"); 163 | return 1; 164 | } 165 | 166 | return 0; 167 | } 168 | 169 | void do_request(void* ptr){ 170 | tk_http_request_t* request = (tk_http_request_t*)ptr; 171 | int fd = request->fd; 172 | ROOT = request->root; 173 | char filename[SHORTLINE]; 174 | struct stat sbuf; 175 | int rc, n_read; 176 | char* plast = NULL; 177 | size_t remain_size; 178 | 179 | tk_del_timer(request); 180 | 181 | while(1){ 182 | // plast指向缓冲区buf当前可写入的第一个字节位置,这里取余是为了实现循环缓冲 183 | plast = &request->buff[request->last % MAX_BUF]; 184 | 185 | // remain_size表示缓冲区当前剩余可写入字节数 186 | remain_size = MIN(MAX_BUF - (request->last - request->pos) - 1, MAX_BUF - request->last % MAX_BUF); 187 | 188 | // 从连接描述符fd读取数据并复制到用户缓冲区plast指向的开始位置 189 | n_read = read(fd, plast, remain_size); 190 | 191 | // 已读到文件尾或无可读数据,断开连接 192 | if(n_read == 0) 193 | goto err; 194 | 195 | // 非EAGAIN错误,断开连接 196 | if(n_read < 0 && (errno != TK_AGAIN)) 197 | goto err; 198 | 199 | // Non-blocking下errno返回EAGAIN则重置定时器(进入此循环表示连接被激活),重新注册,在不断开TCP连接情况下重新等待下一次用户请求 200 | if((n_read < 0) && (errno == TK_AGAIN)) 201 | break; 202 | 203 | // 更新读到的总字节数 204 | request->last += n_read; 205 | 206 | // 解析请求报文行 207 | rc = tk_http_parse_request_line(request); 208 | if(rc == TK_AGAIN) 209 | continue; 210 | else if(rc != 0) 211 | goto err; 212 | 213 | // 解析请求报文体 214 | rc = tk_http_parse_request_body(request); 215 | if(rc == TK_AGAIN) 216 | continue; 217 | else if(rc != 0) 218 | goto err; 219 | 220 | // 分配并初始化返回数据结构 221 | tk_http_out_t* out = (tk_http_out_t *)malloc(sizeof(tk_http_out_t)); 222 | tk_init_out_t(out, fd); 223 | 224 | // 解析URI,获取文件名 225 | parse_uri(request->uri_start, request->uri_end - request->uri_start, filename, NULL); 226 | 227 | // 处理相应错误 228 | if(error_proess(&sbuf, filename, fd)) 229 | continue; 230 | 231 | tk_http_handle_header(request, out); 232 | 233 | // 获取请求文件类型 234 | out->mtime = sbuf.st_mtime; 235 | 236 | // 处理静态文件请求 237 | serve_static(fd, filename, sbuf.st_size, out); 238 | 239 | // 释放返回数据结构 240 | free(out); 241 | 242 | // 处理HTTP长连接,控制TCP是否断开连接 243 | if (!out->keep_alive) 244 | goto close; 245 | } 246 | // 一次请求响应结束后不直接断开TCP连接,而是重置状态 247 | // 修改已经注册描述符的事件类型 248 | // 重置定时器,每等待下一次请求均生效 249 | tk_epoll_mod(request->epoll_fd, request->fd, request, (EPOLLIN | EPOLLET | EPOLLONESHOT)); 250 | tk_add_timer(request, TIMEOUT_DEFAULT, tk_http_close_conn); 251 | 252 | // 每完成一次请求数据的响应都会return以移交出worker线程使用权,并在未断开TCP连接情况下通过epoll监听下一次请求 253 | return; 254 | 255 | err: 256 | close: 257 | // 发生错误或正常关闭 258 | // 关闭相应连接描述符,释放用户请求数据结构 259 | tk_http_close_conn(request); 260 | } -------------------------------------------------------------------------------- /src_code/http.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef HTTP_H 6 | #define HTTP_H 7 | 8 | #include <stdio.h> 9 | #include <stdlib.h> 10 | #include <stdint.h> 11 | #include <string.h> 12 | #include <sys/mman.h> 13 | #include <unistd.h> 14 | #include <sys/stat.h> 15 | #include <sys/types.h> 16 | #include <fcntl.h> 17 | #include "timer.h" 18 | #include "util.h" 19 | #include "rio.h" 20 | #include "epoll.h" 21 | #include "http_parse.h" 22 | #include "http_request.h" 23 | 24 | #define MAXLINE 8192 25 | #define SHORTLINE 512 26 | 27 | #define tk_str3_cmp(m, c0, c1, c2, c3) \ 28 | *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) 29 | #define tk_str3Ocmp(m, c0, c1, c2, c3) \ 30 | *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) 31 | #define tk_str4cmp(m, c0, c1, c2, c3) \ 32 | *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) 33 | 34 | // 用key-value表示mime_type_t 35 | typedef struct mime_type{ 36 | const char *type; 37 | const char *value; 38 | }mime_type_t; 39 | 40 | void do_request(void *ptr); 41 | 42 | #endif -------------------------------------------------------------------------------- /src_code/http_parse.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #include "http.h" 6 | 7 | int tk_http_parse_request_line(tk_http_request_t *request){ 8 | enum{ 9 | sw_start = 0, 10 | sw_method, 11 | sw_spaces_before_uri, 12 | sw_after_slash_in_uri, 13 | sw_http, 14 | sw_http_H, 15 | sw_http_HT, 16 | sw_http_HTT, 17 | sw_http_HTTP, 18 | sw_first_major_digit, 19 | sw_major_digit, 20 | sw_first_minor_digit, 21 | sw_minor_digit, 22 | sw_spaces_after_digit, 23 | sw_almost_done 24 | }state; 25 | state = request->state; 26 | 27 | u_char ch, *p, *m; 28 | size_t pi; 29 | for(pi = request->pos; pi < request->last; pi++){ 30 | p = (u_char *)&request->buff[pi % MAX_BUF]; 31 | ch = *p; 32 | 33 | switch(state){ 34 | case sw_start: 35 | request->request_start = p; 36 | if(ch == CR || ch == LF) 37 | break; 38 | if((ch < 'A' || ch > 'Z') && ch != '_') 39 | return TK_HTTP_PARSE_INVALID_METHOD; 40 | state = sw_method; 41 | break; 42 | 43 | case sw_method: 44 | if(ch == ' '){ 45 | request->method_end = p; 46 | m = request->request_start; 47 | switch(p - m){ 48 | case 3: 49 | if(tk_str3_cmp(m, 'G', 'E', 'T', ' ')){ 50 | request->method = TK_HTTP_GET; 51 | break; 52 | } 53 | break; 54 | case 4: 55 | if(tk_str3Ocmp(m, 'P', 'O', 'S', 'T')){ 56 | request->method = TK_HTTP_POST; 57 | break; 58 | } 59 | if(tk_str4cmp(m, 'H', 'E', 'A', 'D')){ 60 | request->method = TK_HTTP_HEAD; 61 | break; 62 | } 63 | break; 64 | default: 65 | request->method = TK_HTTP_UNKNOWN; 66 | break; 67 | } 68 | state = sw_spaces_before_uri; 69 | break; 70 | } 71 | 72 | if((ch < 'A' || ch > 'Z') && ch != '_') 73 | return TK_HTTP_PARSE_INVALID_METHOD; 74 | break; 75 | 76 | case sw_spaces_before_uri: 77 | if(ch == '/'){ 78 | request->uri_start = p + 1; 79 | state = sw_after_slash_in_uri; 80 | break; 81 | } 82 | switch(ch){ 83 | case ' ': 84 | break; 85 | default: 86 | return TK_HTTP_PARSE_INVALID_REQUEST; 87 | } 88 | break; 89 | 90 | case sw_after_slash_in_uri: 91 | switch(ch){ 92 | case ' ': 93 | request->uri_end = p; 94 | state = sw_http; 95 | break; 96 | default: 97 | break; 98 | } 99 | break; 100 | 101 | case sw_http: 102 | switch(ch){ 103 | case ' ': 104 | break; 105 | case 'H': 106 | state = sw_http_H; 107 | break; 108 | default: 109 | return TK_HTTP_PARSE_INVALID_REQUEST; 110 | } 111 | break; 112 | 113 | case sw_http_H: 114 | switch(ch){ 115 | case 'T': 116 | state = sw_http_HT; 117 | break; 118 | default: 119 | return TK_HTTP_PARSE_INVALID_REQUEST; 120 | } 121 | break; 122 | 123 | case sw_http_HT: 124 | switch(ch){ 125 | case 'T': 126 | state = sw_http_HTT; 127 | break; 128 | default: 129 | return TK_HTTP_PARSE_INVALID_REQUEST; 130 | } 131 | break; 132 | 133 | case sw_http_HTT: 134 | switch(ch){ 135 | case 'P': 136 | state = sw_http_HTTP; 137 | break; 138 | default: 139 | return TK_HTTP_PARSE_INVALID_REQUEST; 140 | } 141 | break; 142 | 143 | case sw_http_HTTP: 144 | switch(ch){ 145 | case '/': 146 | state = sw_first_major_digit; 147 | break; 148 | default: 149 | return TK_HTTP_PARSE_INVALID_REQUEST; 150 | } 151 | break; 152 | 153 | case sw_first_major_digit: 154 | if(ch < '1' || ch > '9') 155 | return TK_HTTP_PARSE_INVALID_REQUEST; 156 | request->http_major = ch - '0'; 157 | state = sw_major_digit; 158 | break; 159 | 160 | case sw_major_digit: 161 | if(ch == '.'){ 162 | state = sw_first_minor_digit; 163 | break; 164 | } 165 | if(ch < '0' || ch > '9') 166 | return TK_HTTP_PARSE_INVALID_REQUEST; 167 | request->http_major = request->http_major * 10 + ch - '0'; 168 | break; 169 | 170 | case sw_first_minor_digit: 171 | if(ch < '0' || ch > '9') 172 | return TK_HTTP_PARSE_INVALID_REQUEST; 173 | request->http_minor = ch - '0'; 174 | state = sw_minor_digit; 175 | break; 176 | 177 | case sw_minor_digit: 178 | if(ch == CR){ 179 | state = sw_almost_done; 180 | break; 181 | } 182 | if(ch == LF) 183 | goto done; 184 | if(ch == ' '){ 185 | state = sw_spaces_after_digit; 186 | break; 187 | } 188 | if(ch < '0' || ch > '9') 189 | return TK_HTTP_PARSE_INVALID_REQUEST; 190 | request->http_minor = request->http_minor * 10 + ch - '0'; 191 | break; 192 | 193 | case sw_spaces_after_digit: 194 | switch(ch){ 195 | case ' ': 196 | break; 197 | case CR: 198 | state = sw_almost_done; 199 | break; 200 | case LF: 201 | goto done; 202 | default: 203 | return TK_HTTP_PARSE_INVALID_REQUEST; 204 | } 205 | break; 206 | 207 | case sw_almost_done: 208 | request->request_end = p - 1; 209 | switch(ch){ 210 | case LF: 211 | goto done; 212 | default: 213 | return TK_HTTP_PARSE_INVALID_REQUEST; 214 | } 215 | } 216 | } 217 | request->pos = pi; 218 | request->state = state; 219 | return TK_AGAIN; 220 | 221 | done: 222 | request->pos = pi + 1; 223 | if (request->request_end == NULL) 224 | request->request_end = p; 225 | request->state = sw_start; 226 | return 0; 227 | } 228 | 229 | int tk_http_parse_request_body(tk_http_request_t *request){ 230 | // 状态列表 231 | enum{ 232 | sw_start = 0, 233 | sw_key, 234 | sw_spaces_before_colon, 235 | sw_spaces_after_colon, 236 | sw_value, 237 | sw_cr, 238 | sw_crlf, 239 | sw_crlfcr 240 | }state; 241 | state = request->state; 242 | 243 | size_t pi; 244 | unsigned char ch, *p; 245 | tk_http_header_t *hd; 246 | for (pi = request->pos; pi < request->last; pi++) { 247 | p = (unsigned char*)&request->buff[pi % MAX_BUF]; 248 | ch = *p; 249 | 250 | switch(state){ 251 | case sw_start: 252 | if(ch == CR || ch == LF) 253 | break; 254 | request->cur_header_key_start = p; 255 | state = sw_key; 256 | break; 257 | 258 | case sw_key: 259 | if(ch == ' '){ 260 | request->cur_header_key_end = p; 261 | state = sw_spaces_before_colon; 262 | break; 263 | } 264 | if(ch == ':'){ 265 | request->cur_header_key_end = p; 266 | state = sw_spaces_after_colon; 267 | break; 268 | } 269 | break; 270 | 271 | case sw_spaces_before_colon: 272 | if(ch == ' ') 273 | break; 274 | else if(ch == ':'){ 275 | state = sw_spaces_after_colon; 276 | break; 277 | } 278 | else 279 | return TK_HTTP_PARSE_INVALID_HEADER; 280 | 281 | case sw_spaces_after_colon: 282 | if (ch == ' ') 283 | break; 284 | state = sw_value; 285 | request->cur_header_value_start = p; 286 | break; 287 | 288 | case sw_value: 289 | if(ch == CR){ 290 | request->cur_header_value_end = p; 291 | state = sw_cr; 292 | } 293 | if(ch == LF){ 294 | request->cur_header_value_end = p; 295 | state = sw_crlf; 296 | } 297 | break; 298 | 299 | case sw_cr: 300 | if(ch == LF){ 301 | state = sw_crlf; 302 | hd = (tk_http_header_t *) malloc(sizeof(tk_http_header_t)); 303 | hd->key_start = request->cur_header_key_start; 304 | hd->key_end = request->cur_header_key_end; 305 | hd->value_start = request->cur_header_value_start; 306 | hd->value_end = request->cur_header_value_end; 307 | list_add(&(hd->list), &(request->list)); 308 | break; 309 | } 310 | else 311 | return TK_HTTP_PARSE_INVALID_HEADER; 312 | 313 | case sw_crlf: 314 | if(ch == CR) 315 | state = sw_crlfcr; 316 | else{ 317 | request->cur_header_key_start = p; 318 | state = sw_key; 319 | } 320 | break; 321 | 322 | case sw_crlfcr: 323 | switch(ch){ 324 | case LF: 325 | goto done; 326 | default: 327 | return TK_HTTP_PARSE_INVALID_HEADER; 328 | } 329 | } 330 | } 331 | request->pos = pi; 332 | request->state = state; 333 | return TK_AGAIN; 334 | 335 | done: 336 | request->pos = pi + 1; 337 | request->state = sw_start; 338 | return 0; 339 | } 340 | -------------------------------------------------------------------------------- /src_code/http_parse.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | 6 | #ifndef HTTP_PARSE_H 7 | #define HTTP_PARSE_H 8 | 9 | #define CR '\r' 10 | #define LF '\n' 11 | 12 | // http请求行解析 13 | int tk_http_parse_request_line(tk_http_request_t *request); 14 | // http请求体解析 15 | int tk_http_parse_request_body(tk_http_request_t *request); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src_code/http_request.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef _GNU_SOURCE 6 | #define _GNU_SOURCE 7 | #endif 8 | 9 | #include "http_request.h" 10 | 11 | static int tk_http_process_ignore(tk_http_request_t* request, tk_http_out_t* out, char* data, int len); 12 | static int tk_http_process_connection(tk_http_request_t* request, tk_http_out_t* out, char* data, int len); 13 | static int tk_http_process_if_modified_since(tk_http_request_t* request, tk_http_out_t* out, char* data, int len); 14 | 15 | tk_http_header_handle_t tk_http_headers_in[] = { 16 | {"Host", tk_http_process_ignore}, 17 | {"Connection", tk_http_process_connection}, 18 | {"If-Modified-Since", tk_http_process_if_modified_since}, 19 | {"", tk_http_process_ignore} 20 | }; 21 | 22 | static int tk_http_process_ignore(tk_http_request_t* request, tk_http_out_t* out, char* data, int len){ 23 | (void) request; 24 | (void) out; 25 | (void) data; 26 | (void) len; 27 | return 0; 28 | } 29 | 30 | 31 | // 处理连接方式 32 | static int tk_http_process_connection(tk_http_request_t* request, tk_http_out_t* out, char* data, int len){ 33 | (void) request; 34 | // 记录请求是否为keep-alive 35 | if (strncasecmp("keep-alive", data, len) == 0) { 36 | out->keep_alive = 1; 37 | } 38 | return 0; 39 | } 40 | 41 | // 判断文件是否修改 42 | static int tk_http_process_if_modified_since(tk_http_request_t* request, tk_http_out_t* out, char *data, int len){ 43 | (void) request; 44 | (void) len; 45 | struct tm tm; 46 | // 转换data格式为GMT格式 47 | if(strptime(data, "%a, %d %b %Y %H:%M:%S GMT", &tm) == (char*)NULL){ 48 | return 0; 49 | } 50 | // 将时间转换为自1970年1月1日以来持续时间的秒数 51 | time_t client_time = mktime(&tm); 52 | // 计算两个时刻之间的时间差 53 | double time_diff = difftime(out->mtime, client_time); 54 | // 微秒时间内未修改status显示未修改,modify字段置为1 55 | if(fabs(time_diff) < 1e-6){ 56 | out->modified = 0; 57 | out->status = TK_HTTP_NOT_MODIFIED; 58 | } 59 | return 0; 60 | } 61 | 62 | // 初始化请求数据结构 63 | int tk_init_request_t(tk_http_request_t* request, int fd, int epoll_fd, char* path){ 64 | // 初始化tk_http_request_t结构 65 | request->fd = fd; 66 | request->epoll_fd = epoll_fd; 67 | request->pos = 0; 68 | request->last = 0; 69 | request->state = 0; 70 | request->root = path; 71 | INIT_LIST_HEAD(&(request->list)); 72 | return 0; 73 | } 74 | 75 | // 初始化响应数据结构 76 | int tk_init_out_t(tk_http_out_t* out, int fd){ 77 | out->fd = fd; 78 | // 默认值1,保持连接不断开 79 | out->keep_alive = 1; 80 | out->modified = 1; 81 | // 默认为200(success),出错时被修改 82 | out->status = 200; 83 | return 0; 84 | } 85 | 86 | void tk_http_handle_header(tk_http_request_t* request, tk_http_out_t* out){ 87 | list_head* pos; 88 | tk_http_header_t* hd; 89 | tk_http_header_handle_t* header_in; 90 | int len; 91 | list_for_each(pos, &(request->list)){ 92 | hd = list_entry(pos, tk_http_header_t, list); 93 | for(header_in = tk_http_headers_in; strlen(header_in->name) > 0; header_in++){ 94 | if(strncmp(hd->key_start, header_in->name, hd->key_end - hd->key_start) == 0){ 95 | len = hd->value_end - hd->value_start; 96 | (*(header_in->handler))(request, out, hd->value_start, len); 97 | break; 98 | } 99 | } 100 | list_del(pos); 101 | free(hd); 102 | } 103 | } 104 | 105 | // 根据状态码返回shortmsg 106 | const char* get_shortmsg_from_status_code(int status_code){ 107 | if(status_code == TK_HTTP_OK){ 108 | return "OK"; 109 | } 110 | if(status_code == TK_HTTP_NOT_MODIFIED){ 111 | return "Not Modified"; 112 | } 113 | if(status_code == TK_HTTP_NOT_FOUND){ 114 | return "Not Found"; 115 | } 116 | return "Unknown"; 117 | } 118 | 119 | // 关闭描述符,释放请求数据结构 120 | int tk_http_close_conn(tk_http_request_t* request){ 121 | close(request->fd); 122 | free(request); 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /src_code/http_request.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef HTTP_REQUEST_H 6 | #define HTTP_REQUEST_H 7 | 8 | #include <stdio.h> 9 | #include <stdlib.h> 10 | #include <math.h> 11 | #include <time.h> 12 | #include <unistd.h> 13 | #include <string.h> 14 | #include <errno.h> 15 | #include "util.h" 16 | #include "list.h" 17 | 18 | #define TK_AGAIN EAGAIN 19 | 20 | #define TK_HTTP_PARSE_INVALID_METHOD 10 21 | #define TK_HTTP_PARSE_INVALID_REQUEST 11 22 | #define TK_HTTP_PARSE_INVALID_HEADER 12 23 | 24 | #define TK_HTTP_UNKNOWN 0x0001 25 | #define TK_HTTP_GET 0x0002 26 | #define TK_HTTP_HEAD 0x0004 27 | #define TK_HTTP_POST 0x0008 28 | 29 | #define TK_HTTP_OK 200 30 | #define TK_HTTP_NOT_MODIFIED 304 31 | #define TK_HTTP_NOT_FOUND 404 32 | #define MAX_BUF 8124 33 | 34 | typedef struct tk_http_request{ 35 | char* root; 36 | int fd; 37 | int epoll_fd; 38 | char buff[MAX_BUF]; 39 | size_t pos; 40 | size_t last; 41 | int state; 42 | 43 | void* request_start; 44 | void* method_end; 45 | int method; 46 | void* uri_start; 47 | void* uri_end; 48 | void* path_start; 49 | void* path_end; 50 | void* query_start; 51 | void* query_end; 52 | int http_major; 53 | int http_minor; 54 | void* request_end; 55 | 56 | struct list_head list; // 存储请求头,list.h中定义了此结构 57 | 58 | void* cur_header_key_start; 59 | void* cur_header_key_end; 60 | void* cur_header_value_start; 61 | void* cur_header_value_end; 62 | void* timer; 63 | }tk_http_request_t; 64 | 65 | typedef struct tk_http_out{ 66 | int fd; 67 | int keep_alive; 68 | time_t mtime; 69 | int modified; 70 | int status; 71 | }tk_http_out_t; 72 | 73 | typedef struct tk_http_header{ 74 | void* key_start; 75 | void* key_end; 76 | void* value_start; 77 | void* value_end; 78 | struct list_head list; 79 | }tk_http_header_t; 80 | 81 | typedef int (*tk_http_header_handler_pt)(tk_http_request_t* request, tk_http_out_t* out, char* data, int len); 82 | 83 | typedef struct tk_http_header_handle{ 84 | char* name; 85 | tk_http_header_handler_pt handler; // 函数指针 86 | }tk_http_header_handle_t; 87 | 88 | extern tk_http_header_handle_t tk_http_headers_in[]; 89 | 90 | void tk_http_handle_header(tk_http_request_t* request, tk_http_out_t* out); 91 | int tk_http_close_conn(tk_http_request_t* request); 92 | int tk_init_request_t(tk_http_request_t* request, int fd, int epoll_fd, char* path); 93 | int tk_init_out_t(tk_http_out_t* out, int fd); 94 | const char* get_shortmsg_from_status_code(int status_code); 95 | 96 | #endif -------------------------------------------------------------------------------- /src_code/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <title>Welcome to TKeed! 5 | 12 | 13 | 14 |

Welcome to TKeed!

15 |

If you see this page, the TKeed web server is successfully installed and 16 | working.

17 | 18 |

For online documentation and support please refer to 19 | TKeed WebServer.
20 | 21 |

Thank you for using TKeed.

22 | 23 | 24 | -------------------------------------------------------------------------------- /src_code/list.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef TK_LIST_H 6 | #define TK_LIST_H 7 | 8 | #ifndef NULL 9 | #define NULL 0 10 | #endif 11 | 12 | typedef struct list_head { 13 | struct list_head *prev, *next; 14 | }list_head; 15 | 16 | // 初始化链表 17 | #define INIT_LIST_HEAD(ptr) do {\ 18 | struct list_head *_ptr = (struct list_head *)ptr; \ 19 | (_ptr)->next = (_ptr); (_ptr->prev) = (_ptr); \ 20 | }while(0) 21 | 22 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 23 | 24 | #define container_of(ptr, type, member) ({ \ 25 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 26 | (type *)( (char *)__mptr - offsetof(type,member) ); \ 27 | }) 28 | 29 | #define list_entry(ptr, type, member) \ 30 | container_of(ptr, type, member) 31 | 32 | #define list_for_each(pos, head) \ 33 | for (pos = (head)->next; pos != (head); pos = pos->next) 34 | 35 | #define list_for_each_prev(pos, head) \ 36 | for (pos = (head)->prev; pos != (head); pos = pos->prev) 37 | 38 | // 插入新节点 39 | static inline void __list_add(struct list_head *_new, struct list_head *prev, struct list_head *next) { 40 | _new->next = next; 41 | next->prev = _new; 42 | prev->next = _new; 43 | _new->prev = prev; 44 | } 45 | 46 | // 头部新增 47 | static inline void list_add(struct list_head *_new, struct list_head *head) { 48 | __list_add(_new, head, head->next); 49 | } 50 | 51 | // 尾部新增 52 | static inline void list_add_tail(struct list_head *_new, struct list_head *head) { 53 | __list_add(_new, head->prev, head); 54 | } 55 | 56 | // 删除节点 57 | static inline void __list_del(struct list_head *prev, struct list_head *next) { 58 | prev->next = next; 59 | next->prev = prev; 60 | } 61 | 62 | // 删除entry节点 63 | static inline void list_del(struct list_head *entry) { 64 | __list_del(entry->prev, entry->next); 65 | } 66 | 67 | // 链表判空 68 | static inline int list_empty(struct list_head *head) { 69 | return (head->next == head) && (head->prev == head); 70 | } 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src_code/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | // 系统头文件 6 | #include 7 | #include "threadpool.h" 8 | #include "http.h" 9 | 10 | #define DEFAULT_CONFIG "tkeed.conf" 11 | 12 | extern struct epoll_event *events; 13 | char *conf_file = DEFAULT_CONFIG; 14 | tk_conf_t conf; 15 | 16 | int main(int argc, char *argv[]){ 17 | // 读取配置文件 18 | read_conf(conf_file, &conf); 19 | 20 | // 处理SIGPIPE 21 | handle_for_sigpipe(); 22 | 23 | // 初始化套接字开始监听 24 | int listen_fd = socket_bind_listen(conf.port); 25 | 26 | // 设置为socket非阻塞 27 | int rc = make_socket_non_blocking(listen_fd); 28 | 29 | // 创建epoll并注册监听描述符 30 | int epoll_fd = tk_epoll_create(0); 31 | tk_http_request_t* request = (tk_http_request_t*)malloc(sizeof(tk_http_request_t)); 32 | tk_init_request_t(request, listen_fd, epoll_fd, conf.root); 33 | tk_epoll_add(epoll_fd, listen_fd, request, (EPOLLIN | EPOLLET)); 34 | 35 | // 初始化线程池 36 | tk_threadpool_t *tp = threadpool_init(conf.thread_num); 37 | 38 | // 初始化计时器 39 | tk_timer_init(); 40 | 41 | while(1){ 42 | // 得到最近且未删除时间和当前时间差值(等待时间) 43 | int time = tk_find_timer(); 44 | 45 | // 调用epoll_wait函数,返回接收到事件的数量 46 | int events_num = tk_epoll_wait(epoll_fd, events, MAXEVENTS, -1); 47 | 48 | // 处理已经超时的请求 49 | tk_handle_expire_timers(); 50 | 51 | // 遍历events数组,根据监听种类及描述符类型分发操作 52 | tk_handle_events(epoll_fd, listen_fd, events, events_num, conf.root, tp); 53 | } 54 | 55 | // 回收线程资源 56 | // threadpool_destroy(tp, graceful_shutdown); 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src_code/makefile: -------------------------------------------------------------------------------- 1 | CC = gcc -std=gnu99 2 | 3 | TKeed:main.o util.o epoll.o http.o http_parse.o http_request.o priority_queue.o rio.o timer.o threadpool.o 4 | $(CC) *.o -o TKeed -lpthread 5 | 6 | main.o:main.c 7 | $(CC) -c main.c 8 | 9 | util.o:util.c util.h 10 | $(CC) -c util.c 11 | 12 | epoll.o:epoll.c epoll.h 13 | $(CC) -c epoll.c 14 | 15 | http.o:http.c http.h 16 | $(CC) -c http.c 17 | 18 | http_parse.o:http_parse.c http_parse.h 19 | $(CC) -c http_parse.c 20 | 21 | http_request.o:http_request.c http_request.h 22 | $(CC) -c http_request.c 23 | 24 | priority_queue.o:priority_queue.c priority_queue.h 25 | $(CC) -c priority_queue.c 26 | 27 | rio.o:rio.c rio.h 28 | $(CC) -c rio.c 29 | 30 | timer.o:timer.c timer.h 31 | $(CC) -c timer.c 32 | 33 | threadpool.o:threadpool.c threadpool.h 34 | $(CC) -c threadpool.c 35 | 36 | clean: 37 | rm *.o TKeed 38 | -------------------------------------------------------------------------------- /src_code/priority_queue.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #include 6 | #include 7 | #include "priority_queue.h" 8 | 9 | void exch(tk_pq_t *tk_pq, size_t i, size_t j){ 10 | void *tmp = tk_pq->pq[i]; 11 | tk_pq->pq[i] = tk_pq->pq[j]; 12 | tk_pq->pq[j] = tmp; 13 | } 14 | 15 | void swim(tk_pq_t *tk_pq, size_t k){ 16 | while (k > 1 && tk_pq->comp(tk_pq->pq[k], tk_pq->pq[k/2])){ 17 | exch(tk_pq, k, k/2); 18 | k /= 2; 19 | } 20 | } 21 | 22 | int sink(tk_pq_t *tk_pq, size_t k){ 23 | size_t j; 24 | size_t nalloc = tk_pq->nalloc; 25 | while((k << 1) <= nalloc){ 26 | j = k << 1; 27 | if((j < nalloc) && (tk_pq->comp(tk_pq->pq[j+1], tk_pq->pq[j]))) 28 | j++; 29 | 30 | if(!tk_pq->comp(tk_pq->pq[j], tk_pq->pq[k])) 31 | break; 32 | 33 | exch(tk_pq, j, k); 34 | k = j; 35 | } 36 | return k; 37 | } 38 | 39 | int tk_pq_sink(tk_pq_t *tk_pq, size_t i){ 40 | return sink(tk_pq, i); 41 | } 42 | 43 | int tk_pq_init(tk_pq_t *tk_pq, tk_pq_comparator_pt comp, size_t size){ 44 | // 为tk_pq_t节点的pq分配(void *)指针 45 | tk_pq->pq = (void **)malloc(sizeof(void *) * (size + 1)); 46 | if (!tk_pq->pq) 47 | return -1; 48 | 49 | tk_pq->nalloc = 0; 50 | tk_pq->size = size + 1; 51 | tk_pq->comp = comp; 52 | return 0; 53 | } 54 | 55 | int tk_pq_is_empty(tk_pq_t *tk_pq){ 56 | // 通过nalloc值款快速判断是否为空 57 | return (tk_pq->nalloc == 0) ? 1 : 0; 58 | } 59 | 60 | size_t tk_pq_size(tk_pq_t *tk_pq){ 61 | // 获取优先队列大小 62 | return tk_pq->nalloc; 63 | } 64 | 65 | void *tk_pq_min(tk_pq_t *tk_pq){ 66 | // 优先队列最小值直接返回第一个元素(指针) 67 | if (tk_pq_is_empty(tk_pq)) 68 | return (void *)(-1); 69 | 70 | return tk_pq->pq[1]; 71 | } 72 | 73 | 74 | int resize(tk_pq_t *tk_pq, size_t new_size){ 75 | if(new_size <= tk_pq->nalloc) 76 | return -1; 77 | 78 | void **new_ptr = (void **)malloc(sizeof(void *) * new_size); 79 | if(!new_ptr) 80 | return -1; 81 | // 将原本nalloc + 1个元素值拷贝到new_ptr指向的位置 82 | memcpy(new_ptr, tk_pq->pq, sizeof(void *) * (tk_pq->nalloc + 1)); 83 | // 释放旧元素 84 | free(tk_pq->pq); 85 | // 重新改写优先队列元素pq指针为new_ptr 86 | tk_pq->pq = new_ptr; 87 | tk_pq->size = new_size; 88 | return 0; 89 | } 90 | 91 | int tk_pq_delmin(tk_pq_t *tk_pq){ 92 | if(tk_pq_is_empty(tk_pq)) 93 | return 0; 94 | 95 | exch(tk_pq, 1, tk_pq->nalloc); 96 | --tk_pq->nalloc; 97 | sink(tk_pq, 1); 98 | if((tk_pq->nalloc > 0) && (tk_pq->nalloc <= (tk_pq->size - 1)/4)){ 99 | if(resize(tk_pq, tk_pq->size / 2) < 0) 100 | return -1; 101 | } 102 | return 0; 103 | } 104 | 105 | int tk_pq_insert(tk_pq_t *tk_pq, void *item){ 106 | if(tk_pq->nalloc + 1 == tk_pq->size){ 107 | if(resize(tk_pq, tk_pq->size * 2) < 0){ 108 | return -1; 109 | } 110 | } 111 | tk_pq->pq[++tk_pq->nalloc] = item; 112 | swim(tk_pq, tk_pq->nalloc); 113 | return 0; 114 | } 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src_code/priority_queue.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef PRIORITY_QUEUE_H 6 | #define PRIORITY_QUEUE_H 7 | 8 | #include 9 | 10 | #define TK_PQ_DEFAULT_SIZE 10 11 | 12 | typedef int (*tk_pq_comparator_pt)(void *pi, void *pj); 13 | 14 | typedef struct priority_queue{ 15 | void **pq; 16 | size_t nalloc; 17 | size_t size; 18 | tk_pq_comparator_pt comp; 19 | }tk_pq_t; 20 | 21 | int tk_pq_init(tk_pq_t *tk_pq, tk_pq_comparator_pt comp, size_t size); 22 | int tk_pq_is_empty(tk_pq_t *tk_pq); 23 | size_t tk_pq_size(tk_pq_t *tk_pq); 24 | void *tk_pq_min(tk_pq_t *tk_pq); 25 | int tk_pq_delmin(tk_pq_t *tk_pq); 26 | int tk_pq_insert(tk_pq_t *tk_pq, void *item); 27 | int tk_pq_sink(tk_pq_t *tk_pq, size_t i); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src_code/rio.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "rio.h" 10 | 11 | ssize_t rio_readn(int fd, void *usrbuf, size_t n) 12 | { 13 | size_t nleft = n; 14 | ssize_t nread; 15 | char *bufp = (char *)usrbuf; 16 | while(nleft > 0){ 17 | if((nread = read(fd, bufp, nleft)) < 0){ 18 | if(errno == EINTR) 19 | nread = 0; 20 | else 21 | return -1; 22 | } 23 | else if(nread == 0) 24 | break; 25 | nleft -= nread; 26 | bufp += nread; 27 | } 28 | return (n - nleft); 29 | } 30 | 31 | ssize_t rio_writen(int fd, void *usrbuf, size_t n) 32 | { 33 | size_t nleft = n; 34 | ssize_t nwritten; 35 | char *bufp = (char *)usrbuf; 36 | 37 | while(nleft > 0){ 38 | if((nwritten = write(fd, bufp, nleft)) <= 0){ 39 | if (errno == EINTR) 40 | nwritten = 0; 41 | else{ 42 | return -1; 43 | } 44 | } 45 | nleft -= nwritten; 46 | bufp += nwritten; 47 | } 48 | return n; 49 | } 50 | 51 | static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) 52 | { 53 | size_t cnt; 54 | while(rp->rio_cnt <= 0){ 55 | rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); 56 | if (rp->rio_cnt < 0){ 57 | if(errno == EAGAIN){ 58 | return -EAGAIN; 59 | } 60 | if(errno != EINTR){ 61 | return -1; 62 | } 63 | } 64 | else if(rp->rio_cnt == 0) 65 | return 0; 66 | else 67 | rp->rio_bufptr = rp->rio_buf; 68 | } 69 | cnt = n; 70 | if(rp->rio_cnt < (ssize_t)n) 71 | cnt = rp->rio_cnt; 72 | memcpy(usrbuf, rp->rio_bufptr, cnt); 73 | rp->rio_bufptr += cnt; 74 | rp->rio_cnt -= cnt; 75 | return cnt; 76 | } 77 | 78 | void rio_readinitb(rio_t *rp, int fd) 79 | { 80 | rp->rio_fd = fd; 81 | rp->rio_cnt = 0; 82 | rp->rio_bufptr = rp->rio_buf; 83 | } 84 | 85 | ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) 86 | { 87 | size_t nleft = n; 88 | ssize_t nread; 89 | char *bufp = (char *)usrbuf; 90 | while (nleft > 0){ 91 | if((nread = rio_read(rp, bufp, nleft)) < 0){ 92 | if(errno == EINTR) 93 | nread = 0; 94 | else 95 | return -1; 96 | } 97 | else if(nread == 0) 98 | break; 99 | nleft -= nread; 100 | bufp += nread; 101 | } 102 | return (n - nleft); 103 | } 104 | 105 | ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) 106 | { 107 | size_t n; 108 | ssize_t rc; 109 | char c, *bufp = (char *)usrbuf; 110 | for(n = 1; n < maxlen; n++){ 111 | if((rc = rio_read(rp, &c, 1)) == 1){ 112 | *bufp++ = c; 113 | if(c == '\n') 114 | break; 115 | } 116 | else if(rc == 0){ 117 | if (n == 1){ 118 | return 0; 119 | } 120 | else 121 | break; 122 | } 123 | else if(rc == -EAGAIN){ 124 | return rc; 125 | } 126 | else{ 127 | return -1; 128 | } 129 | } 130 | *bufp = 0; 131 | return n; 132 | } 133 | -------------------------------------------------------------------------------- /src_code/rio.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef RIO_H 6 | #define RIO_H 7 | 8 | #include 9 | #define RIO_BUFSIZE 8192 10 | 11 | typedef struct{ 12 | int rio_fd; /* descriptor for this internal buf */ 13 | ssize_t rio_cnt; /* unread bytes in internal buf */ 14 | char *rio_bufptr; /* next unread byte in internal buf */ 15 | char rio_buf[RIO_BUFSIZE]; /* internal buffer */ 16 | }rio_t; 17 | 18 | ssize_t rio_readn(int fd, void *usrbuf, size_t n); 19 | ssize_t rio_writen(int fd, void *usrbuf, size_t n); 20 | void rio_readinitb(rio_t *rp, int fd); 21 | ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n); 22 | ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src_code/threadpool.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/26. 3 | // 4 | 5 | 6 | #include "threadpool.h" 7 | 8 | static int threadpool_free(tk_threadpool_t *pool); 9 | static void* threadpool_worker(void *arg); 10 | 11 | // 释放线程池 12 | int threadpool_free(tk_threadpool_t *pool){ 13 | if(pool == NULL || pool->started > 0) 14 | return -1; 15 | 16 | // 释放线程数组 17 | if(pool->threads) 18 | free(pool->threads); 19 | 20 | 21 | // 逐节点销毁task链表 22 | tk_task_t *old; 23 | while(pool->head->next){ 24 | old = pool->head->next; 25 | pool->head->next = pool->head->next->next; 26 | free(old); 27 | } 28 | return 0; 29 | } 30 | 31 | void *threadpool_worker(void *arg){ 32 | if(arg == NULL) 33 | return NULL; 34 | 35 | 36 | tk_threadpool_t *pool = (tk_threadpool_t *)arg; 37 | tk_task_t *task; 38 | while(1){ 39 | // 对线程池上锁 40 | pthread_mutex_lock(&(pool->lock)); 41 | 42 | // 没有task且未停机则阻塞 43 | while((pool->queue_size == 0) && !(pool->shutdown)) 44 | pthread_cond_wait(&(pool->cond), &(pool->lock)); 45 | 46 | // 立即停机模式、平滑停机且没有未完成任务则退出 47 | if(pool->shutdown == immediate_shutdown) 48 | break; 49 | else if((pool->shutdown == graceful_shutdown) && (pool->queue_size == 0)) 50 | break; 51 | 52 | // 得到第一个task 53 | task = pool->head->next; 54 | // 没有task则开锁并进行下一次循环 55 | if(task == NULL){ 56 | pthread_mutex_unlock(&(pool->lock)); 57 | continue; 58 | } 59 | 60 | // 存在task则取走并开锁 61 | pool->head->next = task->next; 62 | pool->queue_size--; 63 | pthread_mutex_unlock(&(pool->lock)); 64 | 65 | // 设置task中func参数 66 | (*(task->func))(task->arg); 67 | free(task); 68 | } 69 | pool->started--; 70 | pthread_mutex_unlock(&(pool->lock)); 71 | pthread_exit(NULL); 72 | return NULL; 73 | } 74 | 75 | // 释放线程资源 76 | int threadpool_destory(tk_threadpool_t *pool, int graceful){ 77 | if(pool == NULL) 78 | return tk_tp_invalid; 79 | if(pthread_mutex_lock(&(pool->lock)) != 0) 80 | return tk_tp_lock_fail; 81 | 82 | int err = 0; 83 | do{ 84 | if(pool->shutdown){ 85 | err = tk_tp_already_shutdown; 86 | break; 87 | } 88 | 89 | pool->shutdown = (graceful) ? graceful_shutdown : immediate_shutdown; 90 | 91 | if(pthread_cond_broadcast(&(pool->cond)) != 0){ 92 | err = tk_tp_cond_broadcast; 93 | break; 94 | } 95 | 96 | if(pthread_mutex_unlock(&(pool->lock)) != 0){ 97 | err = tk_tp_lock_fail; 98 | break; 99 | } 100 | 101 | // 回收每个线程资源 102 | for(int i = 0; i < pool->thread_count; i++){ 103 | if(pthread_join(pool->threads[i], NULL) != 0){ 104 | err = tk_tp_thread_fail; 105 | } 106 | } 107 | }while(0); 108 | 109 | if(!err){ 110 | pthread_mutex_destroy(&(pool->lock)); 111 | pthread_cond_destroy(&(pool->cond)); 112 | threadpool_free(pool); 113 | } 114 | return err; 115 | } 116 | 117 | // 初始化线程池 118 | tk_threadpool_t *threadpool_init(int thread_num){ 119 | // 分配线程池 120 | tk_threadpool_t* pool; 121 | if((pool = (tk_threadpool_t *)malloc(sizeof(tk_threadpool_t))) == NULL) 122 | goto err; 123 | 124 | // threads指针指向线程数组(存放tid),数组大小即为线程数 125 | pool->thread_count = 0; 126 | pool->queue_size = 0; 127 | pool->shutdown = 0; 128 | pool->started = 0; 129 | pool->threads = (pthread_t*)malloc(sizeof(pthread_t) * thread_num); 130 | 131 | // 分配并初始化task头结点 132 | pool->head = (tk_task_t*)malloc(sizeof(tk_task_t)); 133 | if((pool->threads == NULL) || (pool->head == NULL)) 134 | goto err; 135 | pool->head->func = NULL; 136 | pool->head->arg = NULL; 137 | pool->head->next = NULL; 138 | 139 | // 初始化锁 140 | if(pthread_mutex_init(&(pool->lock), NULL) != 0) 141 | goto err; 142 | 143 | // 初始化条件变量 144 | if(pthread_cond_init(&(pool->cond), NULL) != 0) 145 | goto err; 146 | 147 | // 创建线程 148 | for(int i = 0; i < thread_num; ++i){ 149 | if(pthread_create(&(pool->threads[i]), NULL, threadpool_worker, (void*)pool) != 0){ 150 | threadpool_destory(pool, 0); 151 | return NULL; 152 | } 153 | pool->thread_count++; 154 | pool->started++; 155 | } 156 | return pool; 157 | 158 | err: 159 | if(pool) 160 | threadpool_free(pool); 161 | return NULL; 162 | } 163 | 164 | int threadpool_add(tk_threadpool_t* pool, void (*func)(void *), void *arg){ 165 | int rc, err = 0; 166 | if(pool == NULL || func == NULL) 167 | return -1; 168 | 169 | if(pthread_mutex_lock(&(pool->lock)) != 0) 170 | return -1; 171 | 172 | // 已设置关机 173 | if(pool->shutdown){ 174 | err = tk_tp_already_shutdown; 175 | goto out; 176 | } 177 | 178 | // 新建task并注册信息 179 | tk_task_t *task = (tk_task_t *)malloc(sizeof(tk_task_t)); 180 | if(task == NULL) 181 | goto out; 182 | task->func = func; 183 | task->arg = arg; 184 | 185 | // 新task节点在head处插入 186 | task->next = pool->head->next; 187 | pool->head->next = task; 188 | pool->queue_size++; 189 | 190 | rc = pthread_cond_signal(&(pool->cond)); 191 | 192 | out: 193 | if(pthread_mutex_unlock(&pool->lock) != 0) 194 | return -1; 195 | return err; 196 | } 197 | -------------------------------------------------------------------------------- /src_code/threadpool.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/26. 3 | // 4 | 5 | #ifndef THREADPOOL_H 6 | #define THREADPOOL_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | typedef struct tk_task{ 14 | void (*func)(void*); 15 | void* arg; 16 | struct tk_task* next; // 任务链表(下一节点指针) 17 | }tk_task_t; 18 | 19 | typedef struct threadpool{ 20 | pthread_mutex_t lock; // 互斥锁 21 | pthread_cond_t cond; // 条件变量 22 | pthread_t *threads; // 线程 23 | tk_task_t *head; // 任务链表 24 | int thread_count; // 线程数 25 | int queue_size; // 任务链表长 26 | int shutdown; // 关机模式 27 | int started; 28 | }tk_threadpool_t; 29 | 30 | typedef enum{ 31 | tk_tp_invalid = -1, 32 | tk_tp_lock_fail = -2, 33 | tk_tp_already_shutdown = -3, 34 | tk_tp_cond_broadcast = -4, 35 | tk_tp_thread_fail = -5, 36 | }tk_threadpool_error_t; 37 | 38 | typedef enum{ 39 | immediate_shutdown = 1, 40 | graceful_shutdown = 2 41 | }tk_threadpool_sd_t; 42 | 43 | tk_threadpool_t* threadpool_init(int thread_num); 44 | int threadpool_add(tk_threadpool_t* pool, void (*func)(void *), void* arg); 45 | int threadpool_destroy(tk_threadpool_t* pool, int gracegul); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src_code/timer.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | 6 | #include 7 | #include "timer.h" 8 | 9 | tk_pq_t tk_timer; 10 | size_t tk_current_msec; 11 | 12 | int timer_comp(void* ti, void* tj){ 13 | tk_timer_t* timeri = (tk_timer_t*)ti; 14 | tk_timer_t* timerj = (tk_timer_t*)tj; 15 | return (timeri->key < timerj->key) ? 1 : 0; 16 | } 17 | 18 | void tk_time_update(){ 19 | // 获取当前时间 20 | struct timeval tv; 21 | int rc = gettimeofday(&tv, NULL); 22 | tk_current_msec = ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); 23 | } 24 | 25 | int tk_timer_init(){ 26 | // 建立连接后立即初始化 27 | // 初始优先队列大小TK_PQ_DEFAULT_SIZE = 10 28 | int rc = tk_pq_init(&tk_timer, timer_comp, TK_PQ_DEFAULT_SIZE); 29 | 30 | // 更新当前时间 31 | tk_time_update(); 32 | return 0; 33 | } 34 | 35 | int tk_find_timer(){ 36 | int time; 37 | // 返回队列中最早时间和当前时间之差 38 | while(!tk_pq_is_empty(&tk_timer)){ 39 | // 更新当前时间 40 | tk_time_update(); 41 | // timer_node指向最小的时间 42 | tk_timer_t* timer_node = (tk_timer_t*)tk_pq_min(&tk_timer); 43 | // 如果已删则释放此节点(tk_del_timer只置位不删除) 44 | if(timer_node->deleted){ 45 | int rc = tk_pq_delmin(&tk_timer); 46 | free(timer_node); 47 | continue; 48 | } 49 | // 此时timer_node为时间最小节点,time为优先队列里最小时间减去当前时间 50 | time = (int)(timer_node->key - tk_current_msec); 51 | time = (time > 0) ? time : 0; 52 | break; 53 | } 54 | return time; 55 | } 56 | 57 | void tk_handle_expire_timers(){ 58 | while(!tk_pq_is_empty(&tk_timer)){ 59 | // 更新当前时间 60 | tk_time_update(); 61 | tk_timer_t* timer_node = (tk_timer_t*)tk_pq_min(&tk_timer); 62 | // 如果已删则释放此节点 63 | if(timer_node->deleted){ 64 | int rc = tk_pq_delmin(&tk_timer); 65 | free(timer_node); 66 | continue; 67 | } 68 | // 最早入队列节点超时时间大于当前时间(未超时) 69 | // 结束超时检查,顺带删了下标记为删除的节点 70 | if(timer_node->key > tk_current_msec){ 71 | return; 72 | } 73 | // 出现了没被删但是超时的情况,调用handler处理 74 | if(timer_node->handler){ 75 | timer_node->handler(timer_node->request); 76 | } 77 | int rc = tk_pq_delmin(&tk_timer); 78 | free(timer_node); 79 | } 80 | } 81 | 82 | void tk_add_timer(tk_http_request_t* request, size_t timeout, timer_handler_pt handler){ 83 | tk_time_update(); 84 | // 申请新的tk_timer_t节点,并加入到tk_http_request_t的timer下 85 | tk_timer_t* timer_node = (tk_timer_t*)malloc(sizeof(tk_timer_t)); 86 | request->timer = timer_node; 87 | // 加入时设置超时阈值,删除信息等 88 | timer_node->key = tk_current_msec + timeout; 89 | timer_node->deleted = 0; 90 | timer_node->handler = handler; 91 | // 注意需要在tk_timer_t节点中反向设置指向对应resquest的指针 92 | timer_node->request = request; 93 | // 将新节点插入优先队列 94 | int rc = tk_pq_insert(&tk_timer, timer_node); 95 | } 96 | 97 | void tk_del_timer(tk_http_request_t* request) { 98 | tk_time_update(); 99 | tk_timer_t* timer_node = request->timer; 100 | // 惰性删除 101 | // 标记为已删,在find_timer和handle_expire_timers检查队列时会删除 102 | timer_node->deleted = 1; 103 | } -------------------------------------------------------------------------------- /src_code/timer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef TK_TIMER_H 6 | #define TK_TIMER_H 7 | 8 | #include "priority_queue.h" 9 | #include "http_request.h" 10 | 11 | #define TIMEOUT_DEFAULT 500 /* ms */ 12 | 13 | // 函数指针,负责超时处理,tk_add_timer时指定处理函数 14 | typedef int (*timer_handler_pt)(tk_http_request_t* request); 15 | 16 | typedef struct tk_timer{ 17 | size_t key; // 标记超时时间 18 | int deleted; // 标记是否被删除 19 | timer_handler_pt handler; // 超时处理,add时指定 20 | tk_http_request_t* request; // 指向对应的request请求 21 | } tk_timer_t; 22 | 23 | // tk_pq_t定义在"priority_queue.h"中,优先队列中节点 24 | extern tk_pq_t tk_timer; 25 | extern size_t tk_current_msec; 26 | 27 | int tk_timer_init(); 28 | int tk_find_timer(); 29 | void tk_handle_expire_timers(); 30 | void tk_add_timer(tk_http_request_t* request, size_t timeout, timer_handler_pt handler); 31 | void tk_del_timer(tk_http_request_t* request); 32 | int timer_comp(void *ti, void *tj); 33 | 34 | #endif -------------------------------------------------------------------------------- /src_code/tkeed.conf: -------------------------------------------------------------------------------- 1 | root=./# 2 | port=3000 3 | thread_num=4 4 | -------------------------------------------------------------------------------- /src_code/util.c: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "util.h" 14 | #include "http_request.h" 15 | #include "epoll.h" 16 | 17 | int read_conf(char* filename, tk_conf_t* conf){ 18 | // 以只读方式打开文件 19 | FILE* fp = fopen(filename, "r"); 20 | if(!fp) 21 | return TK_CONF_ERROR; 22 | 23 | char buff[BUFLEN]; 24 | int buff_len = BUFLEN; 25 | char* curr_pos = buff; 26 | char* delim_pos = NULL; 27 | int i = 0; 28 | int pos = 0; 29 | int line_len = 0; 30 | while(fgets(curr_pos, buff_len - pos, fp)){ 31 | // 定位每行第一个界定符位置 32 | delim_pos = strstr(curr_pos, DELIM); 33 | if(!delim_pos) 34 | return TK_CONF_ERROR; 35 | if(curr_pos[strlen(curr_pos) - 1] == '\n'){ 36 | curr_pos[strlen(curr_pos) - 1] = '\0'; 37 | } 38 | 39 | // 得到root信息 40 | if(strncmp("root", curr_pos, 4) == 0){ 41 | delim_pos = delim_pos + 1; 42 | while(*delim_pos != '#'){ 43 | conf->root[i++] = *delim_pos; 44 | ++delim_pos; 45 | } 46 | } 47 | 48 | // 得到port值 49 | if(strncmp("port", curr_pos, 4) == 0) 50 | conf->port = atoi(delim_pos + 1); 51 | 52 | // 得到thread数量 53 | if(strncmp("thread_num", curr_pos, 9) == 0) 54 | conf->thread_num = atoi(delim_pos + 1); 55 | 56 | // line_len得到当前行行长 57 | line_len = strlen(curr_pos); 58 | 59 | // 当前位置跳转至下一行首部 60 | curr_pos += line_len; 61 | } 62 | fclose(fp); 63 | return TK_CONF_OK; 64 | } 65 | 66 | void handle_for_sigpipe(){ 67 | struct sigaction sa; 68 | memset(&sa, '\0', sizeof(sa)); 69 | sa.sa_handler = SIG_IGN; 70 | sa.sa_flags = 0; 71 | if(sigaction(SIGPIPE, &sa, NULL)) 72 | return; 73 | } 74 | 75 | int socket_bind_listen(int port){ 76 | // 检查port值,取正确区间范围 77 | port = ((port <= 1024) || (port >= 65535)) ? 6666 : port; 78 | 79 | // 创建socket(IPv4 + TCP),返回监听描述符 80 | int listen_fd = 0; 81 | if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 82 | return -1; 83 | 84 | // 消除bind时"Address already in use"错误 85 | int optval = 1; 86 | if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (const void*)&optval, sizeof(int)) == -1){ 87 | return -1; 88 | } 89 | 90 | // 设置服务器IP和Port,和监听描述副绑定 91 | struct sockaddr_in server_addr; 92 | bzero((char*)&server_addr, sizeof(server_addr)); 93 | server_addr.sin_family = AF_INET; 94 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 95 | server_addr.sin_port = htons((unsigned short)port); 96 | if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) 97 | return -1; 98 | 99 | // 开始监听,最大等待队列长为LISTENQ 100 | if(listen(listen_fd, LISTENQ) == -1) 101 | return -1; 102 | 103 | // 无效监听描述符 104 | if(listen_fd == -1){ 105 | close(listen_fd); 106 | return -1; 107 | } 108 | 109 | return listen_fd; 110 | } 111 | 112 | int make_socket_non_blocking(int fd){ 113 | int flag = fcntl(fd, F_GETFL, 0); 114 | if(flag == -1) 115 | return -1; 116 | 117 | flag |= O_NONBLOCK; 118 | if(fcntl(fd, F_SETFL, flag) == -1) 119 | return -1; 120 | } 121 | 122 | void accept_connection(int listen_fd, int epoll_fd, char* path){ 123 | struct sockaddr_in client_addr; 124 | memset(&client_addr, 0, sizeof(struct sockaddr_in)); 125 | socklen_t client_addr_len = 0; 126 | int accept_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len); 127 | if(accept_fd == -1) 128 | perror("accept"); 129 | 130 | // 设为非阻塞模式 131 | int rc = make_socket_non_blocking(accept_fd); 132 | 133 | // 申请tk_http_request_t类型节点并初始化 134 | tk_http_request_t* request = (tk_http_request_t*)malloc(sizeof(tk_http_request_t)); 135 | tk_init_request_t(request, accept_fd, epoll_fd, path); 136 | 137 | // 文件描述符可以读,边缘触发(Edge Triggered)模式,保证一个socket连接在任一时刻只被一个线程处理 138 | tk_epoll_add(epoll_fd, accept_fd, request, (EPOLLIN | EPOLLET | EPOLLONESHOT)); 139 | 140 | // 新增时间信息 141 | tk_add_timer(request, TIMEOUT_DEFAULT, tk_http_close_conn); 142 | } 143 | 144 | -------------------------------------------------------------------------------- /src_code/util.h: -------------------------------------------------------------------------------- 1 | // 2 | // Latest edit by TeeKee on 2017/7/23. 3 | // 4 | 5 | #ifndef UTIL_H 6 | #define UTIL_H 7 | 8 | #define PATHLEN 128 9 | #define LISTENQ 1024 10 | #define BUFLEN 8192 11 | #define DELIM "=" 12 | 13 | #define TK_CONF_OK 0 14 | #define TK_CONF_ERROR -1 15 | 16 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 17 | 18 | typedef struct tk_conf{ 19 | char root[PATHLEN]; 20 | int port; 21 | int thread_num; 22 | }tk_conf_t; 23 | 24 | int read_conf(char* filename, tk_conf_t* conf); 25 | void handle_for_sigpipe(); 26 | int socket_bind_listen(int port); 27 | int make_socket_non_blocking(int fd); 28 | void accept_connection(int listen_fd, int epoll_fd, char* path); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /test_unit/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright, TeeKee 3 | * Date, 2017.7.20 4 | */ 5 | 6 | #ifndef TK_LIST_H 7 | #define TK_LIST_H 8 | 9 | #ifndef NULL 10 | #define NULL 0 11 | #endif 12 | 13 | typedef struct list_head { 14 | struct list_head *prev, *next; 15 | }list_head; 16 | 17 | // 初始化链表 18 | #define INIT_LIST_HEAD(ptr) do {\ 19 | struct list_head *_ptr = (struct list_head *)ptr; \ 20 | (_ptr)->next = (_ptr); (_ptr->prev) = (_ptr); \ 21 | }while(0) 22 | 23 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 24 | 25 | #define container_of(ptr, type, member) ({ \ 26 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 27 | (type *)( (char *)__mptr - offsetof(type,member) ); \ 28 | }) 29 | 30 | #define list_entry(ptr, type, member) \ 31 | container_of(ptr, type, member) 32 | 33 | #define list_for_each(pos, head) \ 34 | for (pos = (head)->next; pos != (head); pos = pos->next) 35 | 36 | #define list_for_each_prev(pos, head) \ 37 | for (pos = (head)->prev; pos != (head); pos = pos->prev) 38 | 39 | // 插入新节点 40 | static inline void __list_add(struct list_head *_new, struct list_head *prev, struct list_head *next) { 41 | _new->next = next; 42 | next->prev = _new; 43 | prev->next = _new; 44 | _new->prev = prev; 45 | } 46 | 47 | // 头部新增 48 | static inline void list_add(struct list_head *_new, struct list_head *head) { 49 | __list_add(_new, head, head->next); 50 | } 51 | 52 | // 尾部新增 53 | static inline void list_add_tail(struct list_head *_new, struct list_head *head) { 54 | __list_add(_new, head->prev, head); 55 | } 56 | 57 | // 删除节点 58 | static inline void __list_del(struct list_head *prev, struct list_head *next) { 59 | prev->next = next; 60 | next->prev = prev; 61 | } 62 | 63 | // 删除entry节点 64 | static inline void list_del(struct list_head *entry) { 65 | __list_del(entry->prev, entry->next); 66 | } 67 | 68 | // 链表判空 69 | static inline int list_empty(struct list_head *head) { 70 | return (head->next == head) && (head->prev == head); 71 | } 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /test_unit/list_test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/test_unit/list_test -------------------------------------------------------------------------------- /test_unit/list_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "list.h" 4 | 5 | typedef struct{ 6 | void *arg; 7 | list_head list; 8 | }st_header; 9 | 10 | typedef struct{ 11 | void *arg1; 12 | void *arg2; 13 | void *arg3; 14 | list_head list; 15 | }st_struct; 16 | 17 | void add_node(void *arg, list_head *head){ 18 | st_struct *s = (st_struct *)malloc(sizeof(st_struct)); 19 | s->arg1 = arg; 20 | s->arg2 = arg; 21 | s->arg3 = arg; 22 | list_add(&(s->list), head); 23 | } 24 | 25 | void del_node(list_head *entry){ 26 | list_del(entry); 27 | st_struct *s = list_entry(entry, st_struct, list); 28 | free(s); 29 | } 30 | 31 | void display(list_head *head){ 32 | list_head *pos; 33 | st_struct *et; 34 | size_t i = 99; 35 | 36 | list_for_each(pos, head) { 37 | et = list_entry(pos, st_struct, list); 38 | } 39 | } 40 | 41 | int main() { 42 | st_header st_h; 43 | // init list 44 | INIT_LIST_HEAD(&st_h.list); 45 | 46 | size_t i; 47 | // add test 48 | for (i = 0; i < 100; i++) 49 | add_node((void *)i, &(st_h.list)); 50 | 51 | list_head *pos; 52 | st_struct *et; 53 | i = 99; 54 | list_for_each(pos, &st_h.list) { 55 | et = list_entry(pos, st_struct, list); 56 | i--; 57 | } 58 | 59 | // delete test 60 | for (i = 0; i < 32; i++) { 61 | del_node(st_h.list.next); 62 | } 63 | 64 | i = 67; 65 | list_for_each(pos, &st_h.list) { 66 | et = list_entry(pos, st_struct, list); 67 | printf("The num = %ld\n", (size_t)et->arg1); 68 | i--; 69 | } 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /test_unit/priority_queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright, TeeKee 3 | * Date, 2017.7.20 4 | */ 5 | 6 | #include "priority_queue.h" 7 | 8 | 9 | 10 | void exch(tk_pq_t *tk_pq, size_t i, size_t j){ 11 | void *tmp = tk_pq->pq[i]; 12 | tk_pq->pq[i] = tk_pq->pq[j]; 13 | tk_pq->pq[j] = tmp; 14 | } 15 | 16 | void swim(tk_pq_t *tk_pq, size_t k) { 17 | while (k > 1 && tk_pq->comp(tk_pq->pq[k], tk_pq->pq[k/2])) { 18 | exch(tk_pq, k, k/2); 19 | k /= 2; 20 | } 21 | } 22 | 23 | int sink(tk_pq_t *tk_pq, size_t k) { 24 | size_t j; 25 | size_t nalloc = tk_pq->nalloc; 26 | while ((k << 1) <= nalloc) { 27 | j = k << 1; 28 | if((j < nalloc) && (tk_pq->comp(tk_pq->pq[j+1], tk_pq->pq[j]))){ 29 | j++; 30 | } 31 | if(!tk_pq->comp(tk_pq->pq[j], tk_pq->pq[k])){ 32 | break; 33 | } 34 | exch(tk_pq, j, k); 35 | k = j; 36 | } 37 | return k; 38 | } 39 | 40 | int tk_pq_sink(tk_pq_t *tk_pq, size_t i) { 41 | return sink(tk_pq, i); 42 | } 43 | 44 | int tk_pq_init(tk_pq_t *tk_pq, tk_pq_comparator_pt comp, size_t size) { 45 | // 为tk_pq_t节点的pq分配size个(void *)指针 46 | tk_pq->pq = (void **)malloc(sizeof(void *) * (size + 1)); 47 | if (!tk_pq->pq) { 48 | return -1; 49 | } 50 | tk_pq->nalloc = 0; 51 | tk_pq->size = size + 1; 52 | tk_pq->comp = comp; 53 | return 0; 54 | } 55 | 56 | int tk_pq_is_empty(tk_pq_t *tk_pq){ 57 | // 通过nalloc值款快速判断是否为空 58 | return (tk_pq->nalloc == 0) ? 1 : 0; 59 | } 60 | 61 | size_t tk_pq_size(tk_pq_t *tk_pq){ 62 | // 获取优先队列大小 63 | return tk_pq->nalloc; 64 | } 65 | 66 | void *tk_pq_min(tk_pq_t *tk_pq) { 67 | // 优先队列最小值直接返回第一个元素(指针) 68 | if (tk_pq_is_empty(tk_pq)) { 69 | return (void *)(-1); 70 | } 71 | return tk_pq->pq[1]; 72 | } 73 | 74 | 75 | int resize(tk_pq_t *tk_pq, size_t new_size) { 76 | if (new_size <= tk_pq->nalloc) { 77 | return -1; 78 | } 79 | void **new_ptr = (void **)malloc(sizeof(void *) * new_size); 80 | if (!new_ptr) { 81 | return -1; 82 | } 83 | // 将原本nalloc + 1个元素值拷贝到new_ptr指向的位置 84 | memcpy(new_ptr, tk_pq->pq, sizeof(void *) * (tk_pq->nalloc + 1)); 85 | // 释放旧元素 86 | free(tk_pq->pq); 87 | // 重新改写优先队列元素pq指针为new_ptr 88 | tk_pq->pq = new_ptr; 89 | tk_pq->size = new_size; 90 | return 0; 91 | } 92 | 93 | int tk_pq_delmin(tk_pq_t *tk_pq) { 94 | if (tk_pq_is_empty(tk_pq)) { 95 | return 0; 96 | } 97 | 98 | exch(tk_pq, 1, tk_pq->nalloc); 99 | --tk_pq->nalloc; 100 | sink(tk_pq, 1); 101 | if ((tk_pq->nalloc > 0) && (tk_pq->nalloc <= (tk_pq->size - 1)/4)){ 102 | if (resize(tk_pq, tk_pq->size / 2) < 0){ 103 | return -1; 104 | } 105 | } 106 | return 0; 107 | } 108 | 109 | int tk_pq_insert(tk_pq_t *tk_pq, void *item){ 110 | if (tk_pq->nalloc + 1 == tk_pq->size) { 111 | if(resize(tk_pq, tk_pq->size * 2) < 0){ 112 | return -1; 113 | } 114 | } 115 | tk_pq->pq[++tk_pq->nalloc] = item; 116 | swim(tk_pq, tk_pq->nalloc); 117 | return 0; 118 | } 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /test_unit/priority_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright, TeeKee 3 | * Date, 2017.7.20 4 | */ 5 | 6 | #ifndef PRIORITY_QUEUE_H 7 | #define PRIORITY_QUEUE_H 8 | 9 | #include 10 | #include 11 | 12 | #define TK_PQ_DEFAULT_SIZE 10 13 | 14 | typedef int (*tk_pq_comparator_pt)(void *pi, void *pj); 15 | 16 | typedef struct priority_queue{ 17 | void **pq; 18 | size_t nalloc; 19 | size_t size; 20 | tk_pq_comparator_pt comp; 21 | }tk_pq_t; 22 | 23 | int tk_pq_init(tk_pq_t *tk_pq, tk_pq_comparator_pt comp, size_t size); 24 | int tk_pq_is_empty(tk_pq_t *tk_pq); 25 | size_t tk_pq_size(tk_pq_t *tk_pq); 26 | void *tk_pq_min(tk_pq_t *tk_pq); 27 | int tk_pq_delmin(tk_pq_t *tk_pq); 28 | int tk_pq_insert(tk_pq_t *tk_pq, void *item); 29 | int tk_pq_sink(tk_pq_t *tk_pq, size_t i); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /test_unit/priority_queue_test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/test_unit/priority_queue_test -------------------------------------------------------------------------------- /test_unit/priority_queue_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "priority_queue.h" 3 | 4 | static int comp(void *i, void *j) { 5 | size_t si = (size_t)i; 6 | size_t sj = (size_t)j; 7 | return (si < sj)? 1: 0; 8 | } 9 | 10 | int TAG = 1; 11 | 12 | size_t testdata[] = {112, 1634, 2151, 1345, 362, 1557, 1622, 461, 427, 1587, 1836, 2902, 1059, 501, 1391, 1223, 1590, 1931, 2004, 155, 2629, 2796, 2324, 323, 2931, 460, 1270, 1683, 89, 837, 100, 1399, 2110, 156, 24, 1814, 206, 751, 1837, 2438, 2670, 398, 526, 568, 197, 2917, 2430, 245, 1390, 2571, 659, 1246, 1763, 490, 64, 1768, 1710, 1243, 2187, 949, 2604, 2216, 2241, 1073, 2013, 1799, 1860, 1823, 345, 2514, 2008, 1520, 2088, 2841, 231, 351, 2047, 2887, 1389, 1876, 2246, 772, 420, 919, 2068, 2652, 1258, 1962, 2934, 649, 691, 839, 456, 1646, 1268, 2355, 608, 2062, 1808, 858, 421, 2883, 771, 1317, 521, 1125, 2655, 1209, 1991, 2221, 2264, 2227, 1794, 2161, 746, 1496, 2135, 228, 433, 2574, 2725, 1969, 1084, 551, 2454, 1472, 355, 1720, 2864, 1342, 279, 359, 2222, 1769, 2634, 618, 495, 2444, 953, 2265, 2930, 1065, 668, 486, 2166, 2798, 157, 2982, 1204, 2284, 1982, 1659, 2251, 1211, 2316, 2517, 1567, 487, 1098, 106, 853, 1016, 48, 2683, 1033, 2044, 354, 1013, 2964, 1241, 1310, 1666, 2180, 2178, 2846, 1835, 1942, 357, 334, 1018, 2083, 2666, 1930, 1103, 836, 2804, 453, 110, 2128, 1967, 2692, 1902, 2353, 917, 1849, 2060, 2471, 1006, 46, 2253, 384, 2214, 867, 237, 2349, 2562, 2932, 1862, 78, 258, 1053, 1436, 941, 72, 2094, 872, 1396, 121, 711, 1017, 1756, 1611, 2189, 2534, 973, 1387, 141, 2317, 2025, 1180, 1159, 2220, 845, 1238, 11, 1041, 1581, 242, 2953, 1653, 1924, 758, 2587, 787, 1996, 2540, 122, 2530, 2001, 2777, 1549, 1187, 1827, 2755, 1375, 1515, 2790, 1037, 2038, 898, 2197, 2870, 2101, 1420, 2579, 1119, 1485, 2678, 449, 1092, 376, 2714, 2803, 2215, 318, 330, 1941, 1791, 866, 1035, 1192, 1993, 2095, 1868, 225, 918, 2366, 1347, 549, 591, 1562, 2737, 2719, 1577, 2164, 955, 2968, 1432, 732, 368}; 13 | 14 | size_t resultdata[] = {11, 24, 46, 48, 64, 72, 78, 89, 100, 106, 110, 112, 121, 122, 141, 155, 156, 157, 197, 206, 225, 228, 231, 237, 242, 245, 258, 279, 318, 323, 330, 334, 345, 351, 354, 355, 357, 359, 362, 368, 376, 384, 398, 420, 421, 427, 433, 449, 453, 456, 460, 461, 486, 487, 490, 495, 501, 521, 526, 549, 551, 568, 591, 608, 618, 649, 659, 668, 691, 711, 732, 746, 751, 758, 771, 772, 787, 836, 837, 839, 845, 853, 858, 866, 867, 872, 898, 917, 918, 919, 941, 949, 953, 955, 973, 1006, 1013, 1016, 1017, 1018, 1033, 1035, 1037, 1041, 1053, 1059, 1065, 1073, 1084, 1092, 1098, 1103, 1119, 1125, 1159, 1180, 1187, 1192, 1204, 1209, 1211, 1223, 1238, 1241, 1243, 1246, 1258, 1268, 1270, 1310, 1317, 1342, 1345, 1347, 1375, 1387, 1389, 1390, 1391, 1396, 1399, 1420, 1432, 1436, 1472, 1485, 1496, 1515, 1520, 1549, 1557, 1562, 1567, 1577, 1581, 1587, 1590, 1611, 1622, 1634, 1646, 1653, 1659, 1666, 1683, 1710, 1720, 1756, 1763, 1768, 1769, 1791, 1794, 1799, 1808, 1814, 1823, 1827, 1835, 1836, 1837, 1849, 1860, 1862, 1868, 1876, 1902, 1924, 1930, 1931, 1941, 1942, 1962, 1967, 1969, 1982, 1991, 1993, 1996, 2001, 2004, 2008, 2013, 2025, 2038, 2044, 2047, 2060, 2062, 2068, 2083, 2088, 2094, 2095, 2101, 2110, 2128, 2135, 2151, 2161, 2164, 2166, 2178, 2180, 2187, 2189, 2197, 2214, 2215, 2216, 2220, 2221, 2222, 2227, 2241, 2246, 2251, 2253, 2264, 2265, 2284, 2316, 2317, 2324, 2349, 2353, 2355, 2366, 2430, 2438, 2444, 2454, 2471, 2514, 2517, 2530, 2534, 2540, 2562, 2571, 2574, 2579, 2587, 2604, 2629, 2634, 2652, 2655, 2666, 2670, 2678, 2683, 2692, 2714, 2719, 2725, 2737, 2755, 2777, 2790, 2796, 2798, 2803, 2804, 2841, 2846, 2864, 2870, 2883, 2887, 2902, 2917, 2930, 2931, 2932, 2934, 2953, 2964, 2968, 2982}; 15 | 16 | 17 | int main() { 18 | tk_pq_t pq; 19 | int rc; 20 | // check init 21 | rc = tk_pq_init(&pq, comp, TK_PQ_DEFAULT_SIZE); 22 | if(rc == 0) 23 | printf("Init success !\n"); 24 | else{ 25 | TAG = 0; 26 | printf("Init failed !\n"); 27 | } 28 | 29 | // check empty 30 | rc = tk_pq_is_empty(&pq); 31 | if(rc == 1) 32 | printf("Empty success !\n"); 33 | else{ 34 | TAG = 0; 35 | printf("Empty failed !\n"); 36 | } 37 | 38 | size_t sz; 39 | sz = tk_pq_size(&pq); 40 | printf("Size = %lu\n", sz); 41 | 42 | // rc = tk_pq_delmin(&pq); 43 | 44 | int n = sizeof(testdata)/sizeof(size_t); 45 | for (int i = 0; i < n; i++) { 46 | rc = tk_pq_insert(&pq, (void *)testdata[i]); 47 | if(rc == 0) 48 | printf("Insert success !\n"); 49 | else{ 50 | TAG = 0; 51 | printf("Insert failed !\n"); 52 | } 53 | } 54 | 55 | int i = 0; 56 | void *min; 57 | while (!tk_pq_is_empty(&pq)) { 58 | min = tk_pq_min(&pq); 59 | printf("Now, min = %d\n", i); 60 | i++; 61 | rc = tk_pq_delmin(&pq); 62 | if(rc == 0) 63 | printf("Delete success !\n"); 64 | else{ 65 | TAG = 0; 66 | printf("Delete failed !\n"); 67 | } 68 | } 69 | if(TAG) 70 | printf("Pass priority_queue_test !\n"); 71 | else 72 | printf("Something was wrong !\n"); 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /test_unit/webbench/socket.c: -------------------------------------------------------------------------------- 1 | /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $ 2 | * 3 | * This module has been modified by Radim Kolar for OS/2 emx 4 | */ 5 | 6 | /*********************************************************************** 7 | module: socket.c 8 | program: popclient 9 | SCCS ID: @(#)socket.c 1.5 4/1/94 10 | programmer: Virginia Tech Computing Center 11 | compiler: DEC RISC C compiler (Ultrix 4.1) 12 | environment: DEC Ultrix 4.3 13 | description: UNIX sockets code. 14 | ***********************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | int Socket(const char *host, int clientPort) 30 | { 31 | int sock; 32 | unsigned long inaddr; 33 | struct sockaddr_in ad; 34 | struct hostent *hp; 35 | 36 | memset(&ad, 0, sizeof(ad)); 37 | ad.sin_family = AF_INET; 38 | 39 | inaddr = inet_addr(host); 40 | if (inaddr != INADDR_NONE) 41 | memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); 42 | else 43 | { 44 | hp = gethostbyname(host); 45 | if (hp == NULL) 46 | return -1; 47 | memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); 48 | } 49 | ad.sin_port = htons(clientPort); 50 | 51 | sock = socket(AF_INET, SOCK_STREAM, 0); 52 | if (sock < 0) 53 | return sock; 54 | if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0) 55 | return -1; 56 | return sock; 57 | } 58 | 59 | -------------------------------------------------------------------------------- /test_unit/webbench/webbench.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Radim Kolar 1997-2004 3 | * This is free software, see GNU Public License version 2 for 4 | * details. 5 | * 6 | * Simple forking WWW Server benchmark: 7 | * 8 | * Usage: 9 | * webbench --help 10 | * 11 | * Return codes: 12 | * 0 - sucess 13 | * 1 - benchmark failed (server is not on-line) 14 | * 2 - bad param 15 | * 3 - internal error, fork failed 16 | * 17 | */ 18 | #include "socket.c" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /* values */ 28 | volatile int timerexpired=0; 29 | int speed=0; 30 | int failed=0; 31 | int bytes=0; 32 | /* globals */ 33 | int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */ 34 | /* Allow: GET, HEAD, OPTIONS, TRACE */ 35 | #define METHOD_GET 0 36 | #define METHOD_HEAD 1 37 | #define METHOD_OPTIONS 2 38 | #define METHOD_TRACE 3 39 | #define PROGRAM_VERSION "1.5" 40 | int method=METHOD_GET; 41 | int clients=1; 42 | int force=0; 43 | int force_reload=0; 44 | int proxyport=80; 45 | char *proxyhost=NULL; 46 | int benchtime=30; 47 | /* internal */ 48 | int mypipe[2]; 49 | char host[MAXHOSTNAMELEN]; 50 | #define REQUEST_SIZE 2048 51 | char request[REQUEST_SIZE]; 52 | 53 | static const struct option long_options[]= 54 | { 55 | {"force",no_argument,&force,1}, 56 | {"reload",no_argument,&force_reload,1}, 57 | {"time",required_argument,NULL,'t'}, 58 | {"help",no_argument,NULL,'?'}, 59 | {"http09",no_argument,NULL,'9'}, 60 | {"http10",no_argument,NULL,'1'}, 61 | {"http11",no_argument,NULL,'2'}, 62 | {"get",no_argument,&method,METHOD_GET}, 63 | {"head",no_argument,&method,METHOD_HEAD}, 64 | {"options",no_argument,&method,METHOD_OPTIONS}, 65 | {"trace",no_argument,&method,METHOD_TRACE}, 66 | {"version",no_argument,NULL,'V'}, 67 | {"proxy",required_argument,NULL,'p'}, 68 | {"clients",required_argument,NULL,'c'}, 69 | {NULL,0,NULL,0} 70 | }; 71 | 72 | /* prototypes */ 73 | static void benchcore(const char* host,const int port, const char *request); 74 | static int bench(void); 75 | static void build_request(const char *url); 76 | 77 | static void alarm_handler(int signal) 78 | { 79 | timerexpired=1; 80 | } 81 | 82 | static void usage(void) 83 | { 84 | fprintf(stderr, 85 | "webbench [option]... URL\n" 86 | " -f|--force Don't wait for reply from server.\n" 87 | " -r|--reload Send reload request - Pragma: no-cache.\n" 88 | " -t|--time Run benchmark for seconds. Default 30.\n" 89 | " -p|--proxy Use proxy server for request.\n" 90 | " -c|--clients Run HTTP clients at once. Default one.\n" 91 | " -9|--http09 Use HTTP/0.9 style requests.\n" 92 | " -1|--http10 Use HTTP/1.0 protocol.\n" 93 | " -2|--http11 Use HTTP/1.1 protocol.\n" 94 | " --get Use GET request method.\n" 95 | " --head Use HEAD request method.\n" 96 | " --options Use OPTIONS request method.\n" 97 | " --trace Use TRACE request method.\n" 98 | " -?|-h|--help This information.\n" 99 | " -V|--version Display program version.\n" 100 | ); 101 | }; 102 | int main(int argc, char *argv[]) 103 | { 104 | int opt=0; 105 | int options_index=0; 106 | char *tmp=NULL; 107 | 108 | if(argc==1) 109 | { 110 | usage(); 111 | return 2; 112 | } 113 | 114 | while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF ) 115 | { 116 | switch(opt) 117 | { 118 | case 0 : break; 119 | case 'f': force=1;break; 120 | case 'r': force_reload=1;break; 121 | case '9': http10=0;break; 122 | case '1': http10=1;break; 123 | case '2': http10=2;break; 124 | case 'V': printf(PROGRAM_VERSION"\n");exit(0); 125 | case 't': benchtime=atoi(optarg);break; 126 | case 'p': 127 | /* proxy server parsing server:port */ 128 | tmp=strrchr(optarg,':'); 129 | proxyhost=optarg; 130 | if(tmp==NULL) 131 | { 132 | break; 133 | } 134 | if(tmp==optarg) 135 | { 136 | fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg); 137 | return 2; 138 | } 139 | if(tmp==optarg+strlen(optarg)-1) 140 | { 141 | fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg); 142 | return 2; 143 | } 144 | *tmp='\0'; 145 | proxyport=atoi(tmp+1);break; 146 | case ':': 147 | case 'h': 148 | case '?': usage();return 2;break; 149 | case 'c': clients=atoi(optarg);break; 150 | } 151 | } 152 | 153 | if(optind==argc) { 154 | fprintf(stderr,"webbench: Missing URL!\n"); 155 | usage(); 156 | return 2; 157 | } 158 | 159 | if(clients==0) clients=1; 160 | if(benchtime==0) benchtime=60; 161 | /* Copyright */ 162 | fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n" 163 | "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n" 164 | ); 165 | build_request(argv[optind]); 166 | /* print bench info */ 167 | printf("\nBenchmarking: "); 168 | switch(method) 169 | { 170 | case METHOD_GET: 171 | default: 172 | printf("GET");break; 173 | case METHOD_OPTIONS: 174 | printf("OPTIONS");break; 175 | case METHOD_HEAD: 176 | printf("HEAD");break; 177 | case METHOD_TRACE: 178 | printf("TRACE");break; 179 | } 180 | printf(" %s",argv[optind]); 181 | switch(http10) 182 | { 183 | case 0: printf(" (using HTTP/0.9)");break; 184 | case 2: printf(" (using HTTP/1.1)");break; 185 | } 186 | printf("\n"); 187 | if(clients==1) printf("1 client"); 188 | else 189 | printf("%d clients",clients); 190 | 191 | printf(", running %d sec", benchtime); 192 | if(force) printf(", early socket close"); 193 | if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport); 194 | if(force_reload) printf(", forcing reload"); 195 | printf(".\n"); 196 | return bench(); 197 | } 198 | 199 | void build_request(const char *url) 200 | { 201 | char tmp[10]; 202 | int i; 203 | 204 | bzero(host,MAXHOSTNAMELEN); 205 | bzero(request,REQUEST_SIZE); 206 | 207 | if(force_reload && proxyhost!=NULL && http10<1) http10=1; 208 | if(method==METHOD_HEAD && http10<1) http10=1; 209 | if(method==METHOD_OPTIONS && http10<2) http10=2; 210 | if(method==METHOD_TRACE && http10<2) http10=2; 211 | 212 | switch(method) 213 | { 214 | default: 215 | case METHOD_GET: strcpy(request,"GET");break; 216 | case METHOD_HEAD: strcpy(request,"HEAD");break; 217 | case METHOD_OPTIONS: strcpy(request,"OPTIONS");break; 218 | case METHOD_TRACE: strcpy(request,"TRACE");break; 219 | } 220 | 221 | strcat(request," "); 222 | 223 | if(NULL==strstr(url,"://")) 224 | { 225 | fprintf(stderr, "\n%s: is not a valid URL.\n",url); 226 | exit(2); 227 | } 228 | if(strlen(url)>1500) 229 | { 230 | fprintf(stderr,"URL is too long.\n"); 231 | exit(2); 232 | } 233 | if(proxyhost==NULL) 234 | if (0!=strncasecmp("http://",url,7)) 235 | { fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n"); 236 | exit(2); 237 | } 238 | /* protocol/host delimiter */ 239 | i=strstr(url,"://")-url+3; 240 | /* printf("%d\n",i); */ 241 | 242 | if(strchr(url+i,'/')==NULL) { 243 | fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n"); 244 | exit(2); 245 | } 246 | if(proxyhost==NULL) 247 | { 248 | /* get port from hostname */ 249 | if(index(url+i,':')!=NULL && 250 | index(url+i,':')0) 275 | strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n"); 276 | if(proxyhost==NULL && http10>0) 277 | { 278 | strcat(request,"Host: "); 279 | strcat(request,host); 280 | strcat(request,"\r\n"); 281 | } 282 | if(force_reload && proxyhost!=NULL) 283 | { 284 | strcat(request,"Pragma: no-cache\r\n"); 285 | } 286 | if(http10>1) 287 | strcat(request,"Connection: close\r\n"); 288 | /* add empty line at end */ 289 | if(http10>0) strcat(request,"\r\n"); 290 | // printf("Req=%s\n",request); 291 | } 292 | 293 | /* vraci system rc error kod */ 294 | static int bench(void) 295 | { 296 | int i,j,k; 297 | pid_t pid=0; 298 | FILE *f; 299 | 300 | /* check avaibility of target server */ 301 | i=Socket(proxyhost==NULL?host:proxyhost,proxyport); 302 | if(i<0) { 303 | fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n"); 304 | return 1; 305 | } 306 | close(i); 307 | /* create pipe */ 308 | if(pipe(mypipe)) 309 | { 310 | perror("pipe failed."); 311 | return 3; 312 | } 313 | 314 | /* not needed, since we have alarm() in childrens */ 315 | /* wait 4 next system clock tick */ 316 | /* 317 | cas=time(NULL); 318 | while(time(NULL)==cas) 319 | sched_yield(); 320 | */ 321 | 322 | /* fork childs */ 323 | for(i=0;i0) 418 | { 419 | /* fprintf(stderr,"Correcting failed by signal\n"); */ 420 | failed--; 421 | } 422 | return; 423 | } 424 | s=Socket(host,port); 425 | if(s<0) { failed++;continue;} 426 | if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;} 427 | if(http10==0) 428 | if(shutdown(s,1)) { failed++;close(s);continue;} 429 | if(force==0) 430 | { 431 | /* read all available data from socket */ 432 | while(1) 433 | { 434 | if(timerexpired) break; 435 | i=read(s,buf,1500); 436 | /* fprintf(stderr,"%d\n",i); */ 437 | if(i<0) 438 | { 439 | failed++; 440 | close(s); 441 | goto nexttry; 442 | } 443 | else 444 | if(i==0) break; 445 | else 446 | bytes+=i; 447 | } 448 | } 449 | if(close(s)) {failed++;continue;} 450 | speed++; 451 | } 452 | } 453 | -------------------------------------------------------------------------------- /usage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome to TKeed! 5 | 12 | 13 | 14 |

Welcome to TKeed!

15 |

If you see this page, the TKeed web server is successfully installed and 16 | working.

17 | 18 |

For online documentation and support please refer to 19 | TKeed WebServer.
20 | 21 |

Thank you for using TKeed.

22 | 23 | 24 | -------------------------------------------------------------------------------- /usage/obj_file/epoll.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/epoll.o -------------------------------------------------------------------------------- /usage/obj_file/http.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/http.o -------------------------------------------------------------------------------- /usage/obj_file/http_parse.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/http_parse.o -------------------------------------------------------------------------------- /usage/obj_file/http_request.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/http_request.o -------------------------------------------------------------------------------- /usage/obj_file/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/main.o -------------------------------------------------------------------------------- /usage/obj_file/priority_queue.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/priority_queue.o -------------------------------------------------------------------------------- /usage/obj_file/rio.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/rio.o -------------------------------------------------------------------------------- /usage/obj_file/server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/server -------------------------------------------------------------------------------- /usage/obj_file/threadpool.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/threadpool.o -------------------------------------------------------------------------------- /usage/obj_file/timer.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/timer.o -------------------------------------------------------------------------------- /usage/obj_file/util.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linw7/TKeed/8636bf753b61a98a015e9dc4d327059c28a28bb2/usage/obj_file/util.o -------------------------------------------------------------------------------- /主要函数.md: -------------------------------------------------------------------------------- 1 | # 主要函数及调用树 2 | 3 | ## 主要函数 4 | 5 | 1. util.c 6 | 7 | - 读配置:int read_conf(char* filename, tk_conf_t* conf); 8 | 9 | - 绑定监听:int socket_bind_listen(int port); 10 | 11 | - 处理连接:void accept_connection(int listen_fd, int epoll_fd, char* path); 12 | 13 | 2. epoll.c 14 | 15 | - 创建epoll:int tk_epoll_create(int flags); 16 | 17 | - 添加到epoll:int tk_epoll_add(int epoll_fd, int fd, tk_http_request_t* request, int events); 18 | 19 | - 从epoll删除:int tk_epoll_del(int epoll_fd, int fd, tk_http_request_t* request, int events); 20 | 21 | - 修改事件状态:int tk_epoll_mod(int epoll_fd, int fd, tk_http_request_t* request, int events); 22 | 23 | - 等待事件:int tk_epoll_wait(int epoll_fd, struct epoll_event* events, int max_events, int timeout); 24 | 25 | - 分发对应事件:void tk_handle_events(int epoll_fd, int listen_fd, struct epoll_event* events, int events_num, char* path); 26 | 27 | - http.c 28 | 29 | - 处理请求总入口:void do_request(void* ptr); 30 | 31 | - 解析URI:void parse_uri(char* uri, int length, char* filename, char *query); 32 | 33 | - 获取文件类型:const char* get_file_type(const char* type); 34 | 35 | - 错误信息处理:void do_error(int fd, char* cause, char* err_num, char* short_msg, char* long_msg); 36 | 37 | - 响应静态文件:void serve_static(int fd, char* filename, size_t filesize, tk_http_out_t* out); 38 | 39 | - http_parse.c 40 | 41 | - 解析请求行:int tk_http_parse_request_line(tk_http_request_t* request); 42 | 43 | - 解析请求体:int tk_http_parse_request_body(tk_http_request_t* request); 44 | 45 | - http_request.c 46 | 47 | - 初始化请求头结构:int tk_init_request_t(tk_http_request_t* request, int fd, int epoll_fd, char* path); 48 | 49 | - 删除请求头结构:int tk_free_out_t(tk_http_out_t* out); 50 | 51 | - 初始化响应结构:int tk_init_out_t(tk_http_out_t* out, int fd); 52 | 53 | - 删除响应头结构:int tk_free_out_t(tk_http_out_t* out); 54 | 55 | - 获取状态码对应提示:const char* get_shortmsg_from_status_code(int status_code); 56 | 57 | - 关闭连接:int tk_http_close_conn(tk_http_request_t* request); 58 | 59 | - timer.c 60 | 61 | - 刷新当前时间:void tk_time_update(); 62 | 63 | - 初始化时间:int tk_timer_init(); 64 | 65 | - 新增时间戳:void tk_add_timer(tk_http_request_t* request, size_t timeout, timer_handler_pt handler); 66 | 67 | - 删除时间戳:void tk_del_timer(tk_http_request_t* request); 68 | 69 | - 处理超时:void tk_handle_expire_timers(); 70 | 71 | - threadpool.c 72 | 73 | - 初始化线程池:tk_threadpool_t* threadpool_init(int thread_num); 74 | 75 | - 添加任务:threadpool_add(tk_threadpool_t* pool, void (* func)(void*), void* arg); 76 | 77 | - 释放线程池及任务:threadpool_free(tk_threadpool_t* pool); 78 | 79 | - 回收线程资源:int threadpool_destory(tk_threadpool_t* pool, int graceful); 80 | 81 | - 工作线程:void* threadpool_worker(void* arg); 82 | 83 | ## 调用树 84 | 85 | ![调用树](./datum/ClusterCall_full.jpg) 86 | 87 | --- 88 | -------------------------------------------------------------------------------- /启示录.md: -------------------------------------------------------------------------------- 1 | # TKeed启示录 2 | 3 | 1. [C10K问题](https://www.oschina.net/translate/c10k)本质? 4 | 5 | - C10K问题本质是操作系统问题,创建线程多了,数据频繁拷贝(I/O,内核数据拷贝到用户进程空间、阻塞),进程/线程上下文切换消耗大,从而导致操作系统崩溃。 6 | 7 | 2. 为什么使用Reactor模型而不是Proactor模型? 8 | 9 | - Reactor为同步非阻塞模型,Proactor为异步非阻塞模型。核心区别在于**I/O具体是由产生I/O的主线程完成还是交给操作系统来完成**。 10 | 11 | - 异步I/O需要操作系统支持,因为当前Linux下AIO(异步I/O)还不够成熟,所以TKeed使用Reactor模型。 12 | 13 | - Reactor模型网络库:libevent, libev, libuv。 14 | 15 | - Proactor模型网络库:Boost.Asio,IOCP。 16 | 17 | 18 | 3. 为什么使用长连接,怎么实现的? 19 | 20 | - 长连接指在一个TCP连接上可以发送多个HTTP请求和响应,可以避免重复建立TCP连接导致效率低下。 21 | 22 | - 实现长连接主要需要以下两步: 23 | 24 | - 首先要将默认的blocking I/O设为non-blocking I/O,此时read有数据则读取并返回读取的字节数,没数据则返回-1并设置errno为EAGAIN,表示下次有数据来时再读取,这么做的目的是防止长连接下数据未读取完被一直阻塞住(长连接下服务端read并不知道下一次请求会在什么时候到来)。 25 | 26 | - 设置epoll为ET模式(边缘触发模式),只有当状态发生变化时才会被epoll_wait返回,其他时候不会重复返回。所以长连接情况下不会因为数据未读完而每次都返回,这么做的目的是为了提高系统效率,避免每次重复检查大量处于就绪中但并没有数据的描述符。 27 | 28 | 4. epoll底层实现原理及核心参数? 29 | 30 | - 执行epoll_create时创建红黑树和就绪链表,执行epoll_ctl时,如果增加的句柄已经在红黑树中则立即返回,不存在则添加到描述符组成的红黑树中并向内核注册相应的回调函数。当事件到来时向就绪链表插入描述符及回调信息。 31 | 32 | - epoll有两种工作模式,分别是ET和LT,默认为LT,在LT下只要事件read/write等未完全处理完,每次epoll_wait时都会返回,ET模式则只在状态发生变化时返回。另外epoll_wait的第四个参数为超时时间,这里设为-1时,若无事件发生则会一直阻塞住,但若设置好timeout,每隔timeout(ms)就会被唤醒一次并返回0表示超时。 33 | 34 | 5. 定时器是做什么的,怎么实现的? 35 | 36 | - 初始化优先队列结构(小根堆),堆头为expire值最小的节点。 37 | 38 | - 新任务添加时设置超时时间(expire = add_time + timeout)。 39 | 40 | - 每次大循环先通过find_time函数得到最早超时请求的剩余时间(timer = expire - find_time)。 41 | 42 | - 将timer填入epoll_wait的timeout位置,timer时间后自动唤醒并通过handle_expire_timers处理超时连接。 43 | 44 | 6. 如何实现线程池的? 45 | 46 | - 使用互斥锁保证对线程池的互斥访问,使用条件变量实现同步。 47 | 48 | - 初始化线程池,创建worker线程。 49 | 50 | - 各worker最外层为while循环,获得互斥锁的线程可以进入线程池,若无task队列为空则 pthread_cond_wait自动解锁互斥量,置该线程为等待状态并等待条件触发。若存在task则取出队列第一个任务,**之后立即开锁,之后再并执行具体操作**。这里若先执行后开锁则在task完成前整个线程池处于锁定状态,其他线程不能取任务,相当于串行操作! 51 | 52 | - 建立连接后,当客户端请求到达服务器端,创建task任务并添加到线程池task队列尾,当添加完task之后调用pthread_cond_signal唤醒因没有具体task而处于等待状态的worker线程。 53 | 54 | 7. 如何实现线程池同步互斥? 55 | 56 | 处理线程同步互斥问题可以考虑互斥锁、条件变量、读写锁和信号量。本线程池为1:N模型,主线程负责监听并负责添加任务(建立连接 + 创建参数)到线程池中,之后worker线程负责取任务并执行,可供选择的同步策略可以是"互斥锁 + 条件变量"或信号量来完成。 57 | 58 | - 互斥锁 + 条件变量(线程同步) 59 | 60 | ```C++ 61 | int pthread_mutex_lock(pthread_mutex_t *mptr); 62 | int pthread_mutex_unlock(pthread_mutex_t *mptr); 63 | int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr); 64 | int pthread_cond_signal(pthread_cond_t *cptr); 65 | ``` 66 | 67 | - 信号量(进程、线程同步) 68 | 69 | ```C++ 70 | int sem_init(sem_t *sem, int shared, unsigned int value); 71 | int sem_wait(sem_t *sem); 72 | int sem_post(sem_t *sem); 73 | int sem_destory(sem_t *sem); 74 | ``` 75 | 其实信号量初值设为1(二元信号量)时,可以实现互斥锁功能,信号量初值为N时可以实现条件变量功能。不过信号量主要上锁和解锁可以在不同线程,同步操作容易写错,另外,信号量必须先处理同步信号量再用互斥信号量包住临界区,这里写错会发生死锁情况。所以本线程池使用互斥锁 + 条件变量来实现。 76 | 77 | 8. 和生产者消费者问题区别? 78 | 79 | 本质上依旧是生产者消费者问题,生产者消费者模型通常是对有界缓冲区进行同步操作,但在WebServer中,如果连接缓冲的大小固定的话,有可能导致新来的连接无法投入缓冲池中导致生产者线程(监听线程)被阻塞。所以目前Task任务通过链式队列实现,**但目前也在思考如果因为任务未来得及处理,但连接持续被投入线程池会不会造成溢出问题。** 80 | 81 | 9. 设置handle_for_sigpipe的目的是什么? 82 | 83 | - 在使用Webbench时,测试完服务器总是会挂掉,阅读Webbench源码后发现当到达设定的时间之后Webbench采取的措施是直接关闭socket而不会等待最后数据的到来。这样就导致服务器在向已关闭的socket写数据,系统发送SIGPIPE信号终止了服务器。handle_for_sigpipe函数的目的就是把SIGPIPE信号的handler设置为SIG_IGN(忽略)而非终止服务器运行。 84 | 85 | 10. 怎样才算是完整的请求处理过程? 86 | 87 | 注:EAGAIN发生于非阻塞I/O中,当做read操作却没有数据时,会设errno为EAGAIN,表示当前没有数据,稍候再试。 88 | 89 | - 错误断开连接情况: 90 | 91 | - 发生非EAGAIN错误。 92 | 93 | - 解析请求失败。 94 | 95 | - 正常断开连接情况: 96 | 97 | - 执行结束且为非持久连接。 98 | 99 | - 重新返回循环: 100 | 101 | - 数据未读完。 102 | 103 | - 更新定时器、epoll注册信息: 104 | 105 | - 发生EAGAIN问题。 106 | 107 | 11. 如何实现平滑关闭和立即关闭的? 108 | 109 | - 当就绪任务队列有任务时: 110 | 111 | - 若为立即关闭,则直接打开互斥锁并退出线程,可用线程数(started减1)。 112 | 113 | - 若为平滑关闭,则还需要判断当前就绪队列中task是否为空,不为空则执行完就绪队列中任务再关闭。 114 | 115 | 12. TKeed高CPU占用问题? 116 | 117 | - 问题描述: 118 | 119 | - 无任务时CPU占用率一直接近满负荷,压测时CPU占用率反而略微下降(至85%)。 120 | 121 | - 初步分析: 122 | 123 | - 通常CPU利用率高于90%一定是死循环引起的问题。 124 | 125 | - 故障排查: 126 | 127 | - 主循环体问题: 128 | 129 | - epoll_wait的timeout设为-1,且用strace跟踪系统调用发现已阻塞,但CPU占用率依旧很高。 130 | 131 | - 注释掉主执行体,CPU占用率依旧是90%以上,问题应该是在线程池上。 132 | 133 | - 线程池问题: 134 | 135 | - 排查线程池初始化函数,在worker线程最外层循环中有如下语句: 136 | 137 | ```C++ 138 | while ((pool->queue_size == 0) && (pool->shutdown)){ 139 | pthread_cond_wait(&(pool->cond), &(pool->lock)); 140 | } 141 | ``` 142 | - 本意是为了检查条件变量:任务队列是否为空和是否已关机。但这里忘了shutdown初始值为0,导致始终不会调用pthread_cond_wait来阻塞worker线程,导致CPU占用率高。实际上当无任务时使用"top -H -p pid"命令可见四个worker线程CPU使用率都在25%左右,就应该可以知道问题出在了线程池上(未及时阻塞,处于轮询状态)。 143 | 144 | - 修改后满负荷下各线程CPU占用率约10%(压测数据) 145 | 146 | ![压测数据](./datum/压测负载.png) 147 | 148 | 149 | 13. 抓包遇到的问题? 150 | 151 | - 问题: 152 | 153 | - 在本地使用tcpdump抓包时,无论是加上port还是src都不能抓到包。 154 | 155 | - 解决方法: 156 | 157 | - 使用tcpdump -i lo可以抓取本地环回数据。 158 | 159 | 14. TKeed基本信息? 160 | 161 | - 端口号:3000 162 | 163 | - 线程数(worker线程):4 164 | 165 | - 超时阀值:500(ms) 166 | 167 | - 监听最大等待队列:1024 168 | 169 | ## 推荐阅读 170 | 171 | [高性能网络编程 1](http://www.52im.net/thread-560-1-1.html) 172 | 173 | [高性能网络编程 2](http://www.cocoachina.com/bbs/read.php?tid-1705273.html) 174 | 175 | [如何处理C10K问题](https://www.oschina.net/translate/c10k) -------------------------------------------------------------------------------- /并发模型.md: -------------------------------------------------------------------------------- 1 | # 并发模型 2 | 3 | 并发模型主要有多进程模型、多线程模型和事件驱动模型(select, poll, epoll)。多进程模型首先进程比较占资源,切换起来也比较麻烦,况且Linux下最大进程数也有限制,所以不考虑。多线程模型同样有最大线程数限制(主要是单独进程虚地址空间有限),大并发下也不适合。 4 | 5 | 因为进程创建有一定的开销,所以为了减少创建进程、线程的开销,在并发服务器中也常设置进程池和线程池,这样在有新连接到来时就不需要重新创建造成不必要的开销。除此之外,使用epoll,可以将任务拆分成了独立事件,各个事件可以独立被监视和执行。 6 | 7 | - [epoll (kernel 2.6+)](https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/) 8 | 9 | - non-blocking I/O 10 | 11 | - threadpool 12 | 13 | Linux内核2.6之后开始支持epoll,也是本服务器的核心。epoll模型中内核相当于监控代理,监控的粒度为每一个事件,我们把每个完整的处理过程分拆成了多个独立的事件并在epoll中注册,之后监控是否有事件发生的任务就交给内核来做,一旦监测到事件就分发到相应处理模块。就HTTP服务器而言,可以分为以下几步。在创建好了epoll之后: 14 | 15 | - 首先需要注册到"监听事件",之后不需要一直等待下去,直接返回。 16 | 17 | - 一旦内核监听到请求就会自动通知可以去建立连接并创建连接描述符,该连接描述符被注册到"读事件",之后立即返回。 18 | 19 | - 用户发送的数据到达服务器,内核感知到读事件,建立任务并放入线程池中。之后唤醒等待任务的worker线程来执行响应操作。 20 | 21 | 当然,TKeed并不仅仅只用到了epoll和异步非阻塞I/O模型。监听事件最先被注册,在此之后不再阻塞监听,当内核监听到事件发生之后,立即建立连接并注册读事件,读事件并不会阻塞等待用户数据,一直等到内核通知该事件到来才去获取请求,这部分使用的是epoll + 异步非阻塞I/O模型。当请求到达之后,处理请求的操作被放到线程池中,等待多个线程并发响应处理,即使某一线程读取本地文件时被阻塞也会有其他线程可以被调度执行。 22 | 23 | 之所以选择epoll模型是因为事件驱动适合I/O密集型操作,而HTTP服务器最核心的任务就是响应请求的数据,涉及大量I/O请求。另外当并发量上来之后,传统的多进程、多线程模型虽然并发量很大,但大多处于阻塞状态,即使多为就绪态,系统调度开销也非常大,因此这里使用事件驱动模型无疑更适合。 24 | 25 | 综上,TKeed核心架构就是同步事件循环 + 非阻塞I/O + 线程池,即Reactor模型。是否同步指的是产生I/O的线程是否会一直等到I/O完成(I/O是否为当前线程完成),若会一直等待I/O完成则是同步I/O,如果将I/O任务"委派"给其他线程完成并通过回调方式通知调用者(即产生I/O线程可以和执行I/O线程并发),此时就是异步。这里是否是阻塞式I/O指的是read或write等系统调用在数据未准备好前是一直阻塞住还是立即返回并设置errno为EAGAIN(+轮询检查)。 26 | 27 | ![Reactor](./datum/Reactor.png) 28 | 29 | 很对人对于epoll有所误解,觉得epoll是异步的,但epoll实际上只是通过多路事件分离器分离事件,起到的仅仅是告知某个文件描述符可以读写了,但真正执行I/O的线程依旧是需要等待I/O完成的,此时阻塞I/O会直接阻塞住线程,非阻塞I/O则需要该线程通过轮询方式判断I/O是否就绪(检查EAGAIN)。也就是说epoll并不具有区别同步、异步的属性,区别还是得看产生I/O的线程是如何做的。 30 | 31 | 与TKeed相对应的模型为Proactor模型,即异步非阻塞I/O模型。从名字也可以看出,二者区别主要在于是同步I/O还是异步I/O,我们可以对比二者执行步骤(以读事件为例)。 32 | 33 | - Reactor 34 | 35 | - 应用程序注册读就需事件和相关联的事件处理器。 36 | 37 | - 事件分离器等待事件的发生。 38 | 39 | - 当发生读就需事件的时候,事件分离器调用第一步注册的事件处理器。 40 | 41 | - 事件处理器首先执行实际的读取操作,然后根据读取到的内容进行进一步的处理。 42 | 43 | - Proactor 44 | 45 | - 应用程序初始化一个异步读取操作,然后注册相应的事件处理器,此时事件处理器不关注读取就绪事件,而是关注读取完成事件,这是区别于Reactor的关键。 46 | 47 | - 事件分离器等待读取操作完成事件。 48 | 49 | - 在事件分离器等待读取操作完成的时候,操作系统调用内核线程完成读取操作,并将读取的内容放入用户传递过来的缓存区中。这也是区别于Reactor的一点,在Proactor中,应用程序需要传递缓存区。 50 | 51 | - 事件分离器捕获到读取完成事件后,激活应用程序注册的事件处理器,事件处理器直接从缓存区读取数据,而不需要进行实际的读取操作。 52 | 53 | 从上面可以看出,Reactor和Proactor模式的主要区别就是真正的读取和写入操作是有谁来完成的,Reactor中需要应用程序(TKeed中为worker线程)自己读取或者写入数据,而Proactor模式中,应用程序不需要进行实际的读写过程,操作系统会读取缓存区或者写入缓存区到真正的IO设备。 54 | 55 | --- -------------------------------------------------------------------------------- /架构分析.md: -------------------------------------------------------------------------------- 1 | # TKeed整体设计 2 | 3 | 如果该部分阅读有障碍可以先看下面的各部分结构定义及联系。 4 | 5 | **初始化** 6 | 7 | - 读取配置。 8 | 9 | - 绑定地址与监听(LISTENQ = 1024)。 10 | 11 | - 创建epoll并添加监听描述符。 12 | 13 | - 初始化线程池: 14 | 15 | - 初始化线程池各参数。 16 | 17 | - 创建线程。 18 | 19 | - 调用threadpool_worker函数(每个worker线程循环执行以下几步): 20 | 21 | - 每个worker线程进入线程池时都先对线程池互斥锁。 22 | 23 | - 通过条件变量判断是否有待处理任务,只要没有任务则阻塞并打开互斥锁。 24 | 25 | - 如有任务(通过threadpool_add添加),则取出队列中第一个节点。 26 | 27 | - 对线程池开锁,之后其他线程可访问线程池。 28 | 29 | - 执行每个任务对应的操作并删除该任务。 30 | 31 | 注:这里先开锁后执行对应任务,因为在此情况下已经完成对该worker线程任务的分配,不可能将其他任务再分配给此线程,其他线程的并发操作不会对其有任何影响。另外,如果将开锁过程置于任务执行之后,则线程池会被加锁至前一个任务执行结束,整个线程池处于被锁定状态。任务会退化成顺序执行,所以这里先开锁非常重要。 32 | 33 | - 初始化定时器: 34 | 35 | - 初始化定时器优先队列。 36 | 37 | - 更新当前时间。 38 | 39 | **任务处理** 40 | 41 | - 请求连接(事件:有连接请求): 42 | 43 | - 接受连接并返回连接描述符。 44 | 45 | - 向epoll中注册新描述符。 46 | 47 | - 新增时间戳信息,新时间戳被加入到优先队列: 48 | 49 | - 最早超时节点在优先队列头。 50 | 51 | - 响应任务(事件:请求到服务器): 52 | 53 | - 将新建的任务加入到线程池中,处理函数为do_request,参数为请求结构(tk_request_t)。 54 | 55 | - 完成tk_task_t节点初始化,任务数量queue_size加1。 56 | 57 | - 将新任务挂在线程池task队列。 58 | 59 | - 调用pthread_cond_signal激活一个等待该条件的线程(只激活一个,避免惊群效应)。在初始化时,线程池中多个线程因为任务队列为空,所以调用pthread_cond_wait被休眠。一旦有新任务被加入线程池,条件变量条件(任务队列非空)即可被满足,此时会唤醒工作线程来处理该任务。 60 | 61 | ## 具体处理用户请求过程 62 | 63 | 某一具体任务被添加进task队列中就会激活一个worker线程去处理请求,入口函数为do_request。 64 | 65 | - 删除该请求定时器: 66 | 67 | 因为该任务已经响应,且未超时,不再需要定时器去处理该连接的超时情况,接下来在定时器队列中删除该请求的定时器。这也就是为什么tk_request_t和tk_timer_t结构中互有指向彼此指针的原因。 68 | 69 | - 读取用户请求: 70 | 71 | - 数据读取发生错误(errno非EAGAIN)则关闭连接,释放相应数据结构。 72 | 73 | - 若为errno为EAGAIN: 74 | 75 | - 跳出循环(外层使用循环主要是为了触发EAGAIN条件)。 76 | 77 | - 将该请求重设定时器并与于epoll中重新注册。 78 | 79 | - 结束do_request函数,释放worker线程控制权(TCP未断开)。 80 | 81 | - 解析请求并填充tk_request_t各变量。 82 | 83 | - 获取用户请求文件名,判断默认目录下该文件权限等基本信息: 84 | 85 | - 如果有错误信息返回错误响应体。 86 | 87 | - 返回响应体: 88 | 89 | - 若为长连接,则不关闭TCP连接并重新回到循环中。若为短连接则断开连接。 90 | 91 | 92 | ## 结构定义及联系 93 | 94 | ### 线程池结构定义 95 | 96 | **关于线程池首先需要了解TKeed中线程池结构体的定义:** 97 | ```C++ 98 | typedef struct threadpool{ 99 | pthread_mutex_t lock; // 互斥锁 100 | pthread_cond_t cond; // 条件变量 101 | pthread_t *threads; // 线程 102 | tk_task_t *head; // 任务链表 103 | int thread_count; // 线程数 104 | int queue_size; // 任务队列长 105 | int shutdown; // 关机方式 106 | int started; 107 | }tk_threadpool_t; 108 | ``` 109 | 110 | 除了线程的锁机制,线程池还有指向任务队列头节点的head指针。 111 | 112 | - 互斥锁lock 113 | 114 | 互斥锁lock在每次访问临界区时都需要先检查互斥锁,第一个执行pthread_mutex_lock()的线程会得到互斥锁,其他线程会一直等待直到第一个线程执行pthread_mutex_unlock()才会释放。为了保证每个任务的原子性(同一个task不会被多个线程获取并执行),线程池中线程数虽然大于1,但某一具体时刻只能有一个线程在取任务。(对应生产者 - 消费者模型中只能有一个消费者线程取任务) 115 | 116 | - 条件变量cond 117 | 118 | 多线程情况下,如果某个线程已经进入临界区,其他线程会一直检查是否已经开锁,但这是在浪费时间和系统资源,于是就设置条件变量cond来解决这种忙等的问题。某个线程一旦监测到有线程已经得到互斥锁就是进程进入休眠状态,一旦满足条件就会唤醒休眠的线程,之后被唤醒的线程再去检查互斥锁。 119 | 120 | - 线程数组threads 121 | 122 | 初始化时根据配置文件中设定的worker线程数分配线程缓冲池。 123 | 124 | - 任务列表头head 125 | 126 | 所有任务以链表形式组织,head指针指向任务队列的首节点。具体每个任务节点的变量会在下面分析到tk_task_t结构时补充。 127 | 128 | - 线程数thread_count 129 | 130 | 初始化时从用户配置中获得,用于标识线程池中worker线程数量。 131 | 132 | - 任务队列大小queue_size 133 | 134 | 用于标识当前未处理的任务数,设置其目的是为了快速判断是否任务队列已经为空。(也可以判断通过"head->next == NULL"来判断,但不够直观) 135 | 136 | - 关机方式shutdown 137 | 138 | 有立即关机(immediate_shutdown)和平滑关机(graceful_shutdown)两种模式。 139 | 140 | ## 任务结构定义 141 | 142 | **上述线程池中,其他均为"原子变量",只有head变量是tk_task_t是复合型的,tk_task_t的定义如下:** 143 | 144 | ```C++ 145 | typedef struct tk_task{ 146 | void (*func)(void *); // 处理函数的函数指针 147 | void *arg; // 函数变量 148 | struct tk_task *next; // 任务链表(下一节点指针) 149 | }tk_task_t; 150 | ``` 151 | 152 | 这里使用无类型函数指针和无类型变量指针也是为了程序扩展性。 153 | 154 | - 处理函数指针func 155 | 156 | 每个任务创建时设置函数指针func,func为该任务的执行函数。 157 | 158 | - 函数参数指针arg 159 | 160 | 函数参数指针指向任务处理函数func的变量。 161 | 162 | - 链表下一节点指针next 163 | 164 | 所有新增的任务以链表形式组织,next指向下一个任务节点。 165 | 166 | ## 请求节点定义 167 | 168 | **所有HTTP请求解析的参数都以tk_request_t结构定义,上述arg指针会被强制转为tk_request_t类型的指针** 169 | 170 | ```C++ 171 | typedef struct tk_http_request{ 172 | char* root; // 配置目录 173 | int fd; // 描述符(监听、连接) 174 | int epoll_fd; // epoll描述符 175 | char buff[MAX_BUF]; // 用户缓冲 176 | int method; // 请求方法 177 | int state; // 请求头解析状态 178 | void *request_start; 179 | void *method_end; 180 | void *uri_start; 181 | void *uri_end; 182 | void *path_start; 183 | void *path_end; 184 | void *timer; // 指向时间戳结构 185 | ..... 186 | ..... 187 | }tk_http_request_t; 188 | ``` 189 | 本结构用作任务中用户请求处理函数的参数,之后timer节点为非原子结构。 190 | 191 | - 默认文件目录指针root 192 | 193 | root指向默认文件目录。该目录在读取配置后被设置。 194 | 195 | - 连接描述符fd 196 | 197 | 服务器接受请求创建连接后返回的客户端连接描述符,该描述符在向客户机发回响应文件时候会被用到。 198 | 199 | - epoll描述符epoll_fd 200 | 201 | epoll描述符。 202 | 203 | - 用户缓冲buff 204 | 205 | 用户请求的到达客户端后,需要从内核缓冲区读出,读到用户缓冲区buff中。请求行等均会被先读入buff中,之后再进行解析操作。 206 | 207 | - 用户请求方法method 208 | 209 | 解析buff中用户请求行时填入method,如GET、POST等。 210 | 211 | - 解析状态state 212 | 213 | 使用状态机解析用户请求行,state用于记录每个解析状态。 214 | 215 | - 解析指针 216 | 217 | 解析用户请求行时,设置各指针指向buff中某一部分收尾部位,比如uri_start指向uri首地址,uri_end指向uri最后一个字节。设置指针方式是为了操作方便。 218 | 219 | - 时间结构timer 220 | 221 | 用于各个请求时间相关数据,具体描述下面会说。 222 | 223 | ## 时间结构定义 224 | 225 | **该结构记录各请求时间戳信息。** 226 | 227 | ```C++ 228 | 229 | typedef struct tk_timer{ 230 | size_t key; // 标记超时时间 231 | int deleted; // 标记是否被删除 232 | timer_handler_pt handler; // 超时处理 233 | tk_http_request_t *request; // 指向对应的request请求 234 | } tk_timer_t; 235 | ``` 236 | - 超时时间key 237 | 238 | 添加请求时先更新当前时间,key的值为当前时间加上超时时间(默认timeout为500ms)。 239 | 240 | - 标记是否被删除deleted 241 | 242 | 标记该请求是否需要被关闭。每次删除时并不是直接删除该请求,而是先置deleted为1,之后在检查超时时会统一处理,实现惰性删除。 243 | 244 | - 超时回调函数handler 245 | 246 | 发生超时需要处理时,处理方法为调用回调函数handler。这里这么处理也是为了扩展性考虑。 247 | 248 | - 请求节点指针request 249 | 250 | 指向tk_thread_t节点的指针,每个timer和请求节点一一对应。 251 | 252 | --- 253 | -------------------------------------------------------------------------------- /核心结构体.md: -------------------------------------------------------------------------------- 1 | # 核心结构体 2 | 3 | 1. 配置信息结构(unil.h) 4 | ```C++ 5 | typedef struct tk_conf{ 6 | char root[PATHLEN]; // 文件根目录 7 | int port; // 端口号 8 | int thread_num; // 线程数(线程池大小) 9 | }tk_conf_t; 10 | ``` 11 | 12 | 2. 请求信息结构(http_request.h) 13 | ```C++ 14 | typedef struct tk_http_request{ 15 | char* root; // 配置目录 16 | int fd; // 描述符(监听、连接) 17 | int epoll_fd; // epoll描述符 18 | char buff[MAX_BUF]; // 用户缓冲 19 | int method; // 请求方法 20 | int state; // 请求头解析状态 21 | // 以下主要为标记解析请求时索引信息 22 | // 部分未使用,用于扩展功能 23 | size_t pos; 24 | size_t last; 25 | void *request_start; 26 | void *method_end; 27 | void *uri_start; 28 | void *uri_end; 29 | void *path_start; 30 | void *path_end; 31 | void *query_start; 32 | void *query_end; 33 | int http_major; 34 | int http_minor; 35 | void *request_end; 36 | struct list_head list; 37 | void *cur_header_key_start; 38 | void *cur_header_key_end; 39 | void *cur_header_value_start; 40 | void *cur_header_value_end; 41 | void *timer; // 指向时间戳结构 42 | }tk_http_request_t; 43 | ``` 44 | 45 | 3. 响应头结构(http_requesh.h) 46 | ```C++ 47 | typedef struct tk_http_out{ 48 | int fd; // 连接描述符 49 | int keep_alive; // HTTP连接状态 50 | time_t mtime; // 文件类型 51 | int modified; // 是否修改 52 | int status; // 返回码 53 | }tk_http_out_t; 54 | ``` 55 | 56 | 4. 优先队列结构(priority_queue.h) 57 | ```C++ 58 | typedef struct priority_queue{ 59 | void **pq; // 优先队列节点指针 60 | size_t nalloc; // 优先队列实际元素个数 61 | size_t size; // 优先队列大小 62 | tk_pq_comparator_pt comp; // 堆模式 63 | }tk_pq_t; 64 | ``` 65 | 66 | 5. 时间结构(timer.h) 67 | ```C++ 68 | typedef struct tk_timer{ 69 | size_t key; // 标记超时时间 70 | int deleted; // 标记是否被删除 71 | timer_handler_pt handler; // 超时处理 72 | tk_http_request_t *request; // 指向对应的request请求 73 | } tk_timer_t; 74 | ``` 75 | 76 | 6. I/O包结构(rio.h) 77 | ```C++ 78 | typedef struct{ 79 | int rio_fd; // 描述符 80 | ssize_t rio_cnt; // buf中未读字节数 81 | char *rio_bufptr; // 下一个未读字符指针 82 | char rio_buf[RIO_BUFSIZE]; // 缓冲 83 | }rio_t; 84 | ``` 85 | 86 | 7. 线程池结构(threadpool.h) 87 | ```C++ 88 | typedef struct threadpool{ 89 | pthread_mutex_t lock; // 互斥锁 90 | pthread_cond_t cond; // 条件变量 91 | pthread_t *threads; // 线程 92 | tk_task_t *head; // 任务链表 93 | int thread_count; // 线程数 94 | int queue_size; // 任务链表长 95 | int shutdown; // 关机方式 96 | int started; 97 | }tk_threadpool_t; 98 | ``` 99 | 100 | 8. 任务结构(threadpool.h) 101 | ```C++ 102 | typedef struct tk_task{ 103 | void (*func)(void *); // 104 | void *arg; // 105 | struct tk_task *next; // 任务链表(下一节点指针) 106 | }tk_task_t; 107 | ``` 108 | 109 | --- -------------------------------------------------------------------------------- /测试及改进.md: -------------------------------------------------------------------------------- 1 | # 测试及改进 2 | 3 | ## 测试结果 4 | 5 | - 测试工具为Webbench,测试时间为60s,并发数及工作线程数为测试内容。 6 | 7 | - 测试环境为本地,配置4核心i5处理器。 8 | 9 | **1000并发量** 10 | 11 | 在1K并发量下,并未因线程数增多性能有所提升,相反吞吐量还略微下降并且还有191个failed产生,通常线程数为CPU核心数。在测试中1分钟内4线程下请求671102个页面(515 bytes/page),全部成功。另外,在满负荷情况下TKeed总CPU使用率约40%(10% * 4threads或5% * 8threads)。请求1KB标准页面时,吞吐量稍有下降。 12 | 13 | - 4工作线程 14 | 15 | - 性能结果 16 | 17 | ![4worker](./datum/压测结果.png) 18 | 19 | - 系统负载 20 | 21 | ![4works](./datum/压测负载.png) 22 | 23 | - 8工作线程(结果) 24 | 25 | - 性能结果 26 | 27 | ![8worker](./datum/压测结果(8worker).png) 28 | 29 | - 系统负载 30 | 31 | ![8works](./datum/压测负载(8worker).png) 32 | 33 | - 标准1KB页面测试 34 | 35 | - 性能结果 36 | 37 | ![1KB_Page](./datum/标准页面性能测试.png) 38 | 39 | --- -------------------------------------------------------------------------------- /背景知识.md: -------------------------------------------------------------------------------- 1 | # 背景知识 2 | 3 | ## 网络基础 4 | 5 | **应用层** 6 | 7 | HTTP协议工作在应用层,端口号是80。HTTP协议被用于网络中两台计算机间的通信,相比于TCP/IP这些底层协议,HTTP协议更像是高层标记型语言,浏览器根据从服务器得到的HTTP响应体中分别得到报文头,响应头和信息体(HTML正文等),之后将HTML文件解析并呈现在浏览器上。同样,我们在浏览器地址栏输入网址之后,浏览器相当于用户代理帮助我们组织好报文头,请求头和信息体(可选),之后通过网络发送到服务器,服务器根据请求的内容准备数据。所以如果想要完全弄明白HTTP协议,你需要写一个浏览器 + 一个Web服务器,一侧来生成请求信息,一侧生成响应信息。 8 | 9 | 从网络分层模型来看,HTTP工作在应用层,其在传输层由TCP协议为其提供服务。所以可以猜到,HTTP请求前,客户机和服务器之间一定已经通过三次握手建立起连接,其中套接字中服务器一侧的端口号为HTTP周知端口80。在请求和传输数据时也是有讲究的,通常一个页面上不只有文本数据,有时会内嵌很多图片,这时候有两种选择可以考虑。一种是对每一个文件都建立一个TCP连接,传送完数据后立马断开,通过多次这样的操作获取引用的所有数据,但是这样一个页面的打开需要建立多次连接,效率会低很多。另一种是对于有多个资源的页面,传送完一个数据后不立即断开连接,在同一次连接下多次传输数据直至传完,但这种情况有可能会长时间占用服务器资源,降低吞吐率。上述两种模式分别是HTTP 1.0和HTTP 1.1版本的默认方式,具体是什么含义会在后面详细解释。 10 | 11 | - HTTP工作流程 12 | 13 | 一次完整的HTTP请求事务包含以下四个环节: 14 | 15 | - 建立起客户机和服务器连接。 16 | 17 | - 建立连接后,客户机发送一个请求给服务器。 18 | 19 | - 服务器收到请求给予响应信息。 20 | 21 | - 客户端浏览器将返回的内容解析并呈现,断开连接。 22 | 23 | - HTTP协议结构 24 | 25 | **请求报文** 26 | 27 | 对于HTTP请求报文我们可以通过以下两种方式比较直观的看到:一是在浏览器调试模式下(F12)看请求响应信息,二是通过wireshark或者tcpdump抓包实现。通过前者看到的数据更加清晰直观,通过后者抓到的数据更真实。但无论是用哪种方式查看,得到的请求报文主题体信息都是相同的,对于请求报文,主要包含以下四个部分,每一行数据必须通过"\r\n"分割,这里可以理解为行末标识符。 28 | 29 | - 报文头(只有一行) 30 | 31 | 结构:method uri version 32 | 33 | - method 34 | 35 | HTTP的请求方法,一共有9中,但GET和POST占了99%以上的使用频次。GET表示向特定资源发起请求,当然也能提交部分数据,不过提交的数据以明文方式出现在URL中。POST通常用于向指定资源提交数据进行处理,提交的数据被包含在请求体中,相对而言比较安全些。 36 | 37 | - uri 38 | 39 | 用来指代请求的文件,≠URL。 40 | 41 | - version 42 | 43 | HTTP协议的版本,该字段有HTTP/1.0和HTTP/1.1两种。 44 | 45 | - 请求头(多行) 46 | 47 | 在HTTP/1.1中,请求头除了Host都是可选的。包含的头五花八门,这里只介绍部分。 48 | 49 | - Host:指定请求资源的主机和端口号。端口号默认80。 50 | 51 | - Connection:值为keep-alive和close。keep-alive使客户端到服务器的连接持续有效,不需要每次重连,此功能为HTTP/1.1预设功能。 52 | 53 | - Accept:浏览器可接收的MIME类型。假设为text/html表示接收服务器回发的数据类型为text/html,如果服务器无法返回这种类型,返回406错误。 54 | 55 | - Cache-control:缓存控制,Public内容可以被任何缓存所缓存,Private内容只能被缓存到私有缓存,non-cache指所有内容都不会被缓存。 56 | 57 | - Cookie:将存储在本地的Cookie值发送给服务器,实现无状态的HTTP协议的会话跟踪。 58 | 59 | - Content-Length:请求消息正文长度。 60 | 61 | 另有User-Agent、Accept-Encoding、Accept-Language、Accept-Charset、Content-Type等请求头这里不一一罗列。由此可见,请求报文是告知服务器请求的内容,而请求头是为了提供服务器一些关于客户机浏览器的基本信息,包括编码、是否缓存等。 62 | 63 | - 空行(一行) 64 | 65 | - 可选消息体(多行) 66 | 67 | **响应报文** 68 | 69 | 响应报文是服务器对请求资源的响应,通过上面提到的方式同样可以看到,同样地,数据也是以"\r\n"来分割。 70 | 71 | - 报文头(一行) 72 | 73 | 结构:version status_code status_message 74 | 75 | - version 76 | 77 | 描述所遵循的HTTP版本。 78 | 79 | - status_code 80 | 81 | 状态码,指明对请求处理的状态,常见的如下。 82 | 83 | - 200:成功。 84 | 85 | - 301:内容已经移动。 86 | 87 | - 400:请求不能被服务器理解。 88 | 89 | - 403:无权访问该文件。 90 | 91 | - 404:不能找到请求文件。 92 | 93 | - 500:服务器内部错误。 94 | 95 | - 501:服务器不支持请求的方法。 96 | 97 | - 505:服务器不支持请求的版本。 98 | 99 | - status_message 100 | 101 | 显示和状态码等价英文描述。 102 | 103 | - 响应头(多行) 104 | 105 | 这里只罗列部分。 106 | 107 | - Date:表示信息发送的时间。 108 | 109 | - Server:Web服务器用来处理请求的软件信息。 110 | 111 | - Content-Encoding:Web服务器表明了自己用什么压缩方法压缩对象。 112 | 113 | - Content-Length:服务器告知浏览器自己响应的对象长度。 114 | 115 | - Content-Type:告知浏览器响应对象类型。 116 | 117 | - 空行(一行) 118 | 119 | - 信息体(多行) 120 | 121 | 实际有效数据,通常是HTML格式的文件,该文件被浏览器获取到之后解析呈现在浏览器中。 122 | 123 | **CGI与环境变量** 124 | 125 | - CGI程序 126 | 127 | 服务器为客户端提供动态服务首先需要解决的是得到用户提供的参数再根据参数信息返回。为了和客户端进行交互,服务器需要先创建子进程,之后子进程执行相应的程序去为客户服务。CGI正是帮助我们解决参数获取、输出结果的。 128 | 129 | 动态内容获取其实请求报文的头部和请求静态数据时完全相同,但请求的资源从静态的HTML文件变成了后台程序。服务器收到请求后fork()一个子进程,子进程执行请求的程序,这样的程序称为CGI程序(Python、Perl、C++等均可)。通常在服务器中我们会预留一个单独的目录(cgi-bin)用来存放所有的CGI程序,请求报文头部中请求资源的前缀都是/cgi-bin,之后加上所请求调用的CGI程序即可。 130 | 131 | 所以上述流程就是:客户端请求程序 -> 服务器fork()子进程 -> 执行被请求程序。接下来需要解决的问题就是如何获取客户端发送过来的参数和输出信息怎么传递回客户端。 132 | 133 | - 环境变量 134 | 135 | 对CGI程序来说,CGI环境变量在创建时被初始化,结束时被销毁。当CGI程序被HTTP服务器调用时,因为是被服务器fork()出来的子进程,所以其继承了其父进程的环境变量,这些环境变量包含了很多基本信息,请求头中和响应头中列出的内容(比如用户Cookie、客户机主机名、客户机IP地址、浏览器信息等),CGI程序所需要的参数也在其中。 136 | 137 | - GET方法下参数获取 138 | 139 | 服务器把接收到的参数数据编码到环境变量QUERY_STRING中,在请求时只需要直接把参数写到URL最后即可,比如"http:127.0.0.1:80/cgi-bin/test?a=1&b=2&c=3",表示请求cgi-bin目录下test程序,'?'之后部分为参数,多个参数用'&'分割开。服务器接收到请求后环境变量QUERY_STRING的值即为a=1&b=2&c=3。 140 | 141 | 在CGI程序中获取环境变量值的方法是:getenv(),比如我们需要得到上述QUERY_STRING的值,只需要下面这行语句就可以了。 142 | 143 | char *value = getenv("QUERY_STRING"); 144 | 145 | 之后对获得的字符串处理一下提取出每个参数信息即可。 146 | 147 | - POST方法下参数获取 148 | 149 | POST方法下,CGI可以直接从服务器标准输入获取数据,不过要先从CONTENT_LENGTH这个环境变量中得到POST参数长度,再获取对应长度内容。 150 | 151 | **会话机制** 152 | 153 | HTTP作为无状态协议,必然需要在某种方式保持连接状态。这里简要介绍一下Cookie和Session。 154 | 155 | - Cookie 156 | 157 | Cookie是客户端保持状态的方法。 158 | 159 | Cookie简单的理解就是存储由服务器发至客户端并由客户端保存的一段字符串。为了保持会话,服务器可以在响应客户端请求时将Cookie字符串放在Set-Cookie下,客户机收到Cookie之后保存这段字符串,之后再请求时候带上Cookie就可以被识别。 160 | 161 | 除了上面提到的这些,Cookie在客户端的保存形式可以有两种,一种是会话Cookie一种是持久Cookie,会话Cookie就是将服务器返回的Cookie字符串保持在内存中,关闭浏览器之后自动销毁,持久Cookie则是存储在客户端磁盘上,其有效时间在服务器响应头中被指定,在有效期内,客户端再次请求服务器时都可以直接从本地取出。需要说明的是,存储在磁盘中的Cookie是可以被多个浏览器代理所共享的。 162 | 163 | - Session 164 | 165 | Session是服务器保持状态的方法。 166 | 167 | 首先需要明确的是,Session保存在服务器上,可以保存在数据库、文件或内存中,每个用户有独立的Session用户在客户端上记录用户的操作。我们可以理解为每个用户有一个独一无二的Session ID作为Session文件的Hash键,通过这个值可以锁定具体的Session结构的数据,这个Session结构中存储了用户操作行为。 168 | 169 | 当服务器需要识别客户端时就需要结合Cookie了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用Cookie来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在Cookie里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。如果客户端的浏览器禁用了Cookie,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如sid=xxxxx这样的参数,服务端据此来识别用户,这样就可以帮用户完成诸如用户名等信息自动填入的操作了。 170 | 171 | **传输层** 172 | 173 | 传输层主要需要了解TCP建立连接过程和客户机-服务器状态变化。深入了解传输层的话,抓包(Wireshark或Tcpdump)无疑是最好的。[详见笔记](https://github.com/linw7/Skill-Tree/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md)。 174 | 175 | **网络层** 176 | 177 | 网络层部分对于服务器而言过于底层,这里不再介绍,[详见笔记](https://github.com/linw7/Skill-Tree/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md)。 178 | 179 | ## 客户端 - 服务器模型 180 | 181 | 182 | **客户端** 183 | 184 | - 创建socket -> int socket(int domain, int type, int protocol); 185 | 186 | - 连接指定计算机 -> int connect(int sockfd, struct sockaddr* addr, socklen_t addrlen); 187 | 188 | - sockfd客户端的sock描述字。 189 | 190 | - addr:服务器的地址。 191 | 192 | - addrlen:socket地址长度。 193 | 194 | - 向socket写入信息 -> ssize_t write(int fd, const void *buf, size_t count); 195 | 196 | - fd、buf、count:同read中意义。 197 | 198 | - 大于0表示写了部分或全部数据,小于0表示出错。 199 | 200 | - 关闭oscket -> int close(int fd); 201 | 202 | - fd:同服务器端fd。 203 | 204 | **服务器端** 205 | 206 | - 创建socket -> int socket(int domain, int type, int protocol); 207 | 208 | - domain:协议域,决定了socket的地址类型,IPv4为AF_INET。 209 | 210 | - type:指定socket类型,SOCK_STREAM为TCP连接。 211 | 212 | - protocol:指定协议。IPPROTO_TCP表示TCP协议,为0时自动选择type默认协议。 213 | 214 | - 绑定socket和端口号 -> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 215 | 216 | - sockfd:socket返回的套接字描述符,类似于文件描述符fd。 217 | 218 | - addr:有个sockaddr类型数据的指针,指向的是被绑定结构变量。 219 | 220 | ```C++ 221 | // IPv4的sockaddr地址结构 222 | struct sockaddr_in { 223 | sa_family_t sin_family; // 协议类型,AF_INET 224 | in_port_t sin_port; // 端口号 225 | struct in_addr sin_addr; // IP地址 226 | }; 227 | struct in_addr { 228 | uint32_t s_addr; 229 | } 230 | ``` 231 | 232 | - addrlen:地址长度。 233 | 234 | - 监听端口号 -> int listen(int sockfd, int backlog); 235 | 236 | - sockfd:要监听的sock描述字。 237 | 238 | - backlog:socket可以排队的最大连接数。 239 | 240 | - 接收用户请求 -> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 241 | 242 | - sockfd:服务器socket描述字。 243 | 244 | - addr:指向地址结构指针。 245 | 246 | - addrlen:协议地址长度。 247 | 248 | - 注:一旦accept某个客户机请求成功将返回一个全新的描述符用于标识具体客户的TCP连接。 249 | 250 | - 从socket中读取字符 -> ssize_t read(int fd, void *buf, size_t count); 251 | 252 | - fd:连接描述字。 253 | 254 | - buf:缓冲区buf。 255 | 256 | - count:缓冲区长度。 257 | 258 | - 注:大于0表示读取的字节数,返回0表示文件读取结束,小于0表示发生错误。 259 | 260 | - 关闭socket -> int close(int fd); 261 | 262 | - fd:accept返回的连接描述字,每个连接有一个,生命周期为连接周期。 263 | 264 | - 注:sockfd是监听描述字,一个服务器只有一个,用于监听是否有连接;fd是连接描述字,用于每个连接的操作。 265 | 266 | ## 实现方案 267 | 268 | 基本的多线程多进程方案和优劣参考[操作系统专题](https://github.com/linw7/Skill-Tree/blob/master/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F.md)。因为各个进程拥有独立的虚拟地址空间,所以讨论进程时主要讨论IPC机制(有名管道、无名管道、消息队列、共享内存、信号量、套接字)。另外讨论一下线程存储器模型。一组并发线程运行在进程的上下文中,每个线程拥有自己独立的tid、栈、栈指针、程序计数器、条件码和通用目的寄存器。从属于某一进程的各个线程共享进程的虚拟地址空间,包括代码段、数据段、**堆**、打开文件及共享库等。所以某一进程内的全部线程是不能访问其他线程的数据的,但他们却共享着进程的数据,在使用多线程模型时要格外注意对共享变量(全局变量、静态全局/局部变量等)需要互斥访问,且需要了解操作顺序的不同是否会影响最终结果。 269 | 270 | 下面主要讲一下网络I/O模型。在描述网络I/O模型的诸多书籍中,很多都只说笼统的概念,我们将问题具体化,暂时只考虑服务器端的网络I/O情形。我们假定目前的情形是服务器已经在监听用户请求,建立连接后服务器调用read()函数等待读取用户发送过来的数据流,之后将接收到的数据打印出来。 271 | 272 | 所以服务器端简单是这样的流程:建立连接 -> 监听请求 -> 等待用户数据 -> 打印数据。我们总结网络通信中的等待: 273 | 274 | - 建立连接时等待对方的ACK包。 275 | 276 | - 等待客户端请求。 277 | 278 | - 输入等待:服务器用户数据到达内核缓冲区(read函数等待)。 279 | 280 | - 输出等待:用户端等待缓冲区有足够空间可以输入(write函数等待)。 281 | 282 | 另外为了能够解释清楚网络I/O模型,还需要了解一些基础。对服务器而言,打印出用户输入的字符串(printf函数)和从网络中获取数据(read函数)需要单独来看。服务器首先accept用户连接请求后首先调用read函数等待数据,这里的read函数是系统调用,运行于内核态,使用的也是内核地址空间,并且从网络中取得的数据需要先写入到内核缓冲区。当read系统调用获取到数据后将这些数据再复制到用户地址空间的用户缓冲区中,之后返回到用户态执行printf函数打印字符串。我们需要明确两点: 283 | 284 | - read执行在内核态且数据流先读入内核缓冲区;printf运行于用户态,打印的数据会先从内核缓冲区复制到进程的用户缓冲区,之后打印出来。 285 | 286 | - printf函数一定是在read函数已经准备好数据之后才能执行,但read函数作为I/O操作通常需要等待而触发阻塞。调用read函数的是服务器进程,一旦被read调用阻塞,整个服务器在获取到用户数据前都不能接受任何其他用户的请求(单进程/线程)。 287 | 288 | 有了上面的基础,我们就可以介绍下面四种网路I/O模型。 289 | 290 | **阻塞式** 291 | 292 | - 阻塞表示一旦调用I/O函数必须等整个I/O完成才返回。正如上面提到的那种情形,当服务器调用了read函数之后,如果不是立即接收到数据,服务器进程会被阻塞,之后一直在等待用户数据到达,用户数据到达后首先会写进内核缓冲区,之后内核缓冲区数据复制到用户进程(服务器进程)缓冲区。完成了上述所有的工作后,才会把执行权限返回给用户(从内核态 -> 用户态)。 293 | 294 | - 很显然,阻塞式I/O的效率实在太低,如果用户输入数据迟迟不到的话,整个服务器就会一直被阻塞(单进程/线程)。为了不影响服务器接收其他进程的连接,我们可以考虑多进程模型,这样当服务器建立连接后为连接的用户创建新线程,新线程即使是使用阻塞式I/O也仅仅是这一个线程被阻塞,不会影响服务器等待接收新的连接。 295 | 296 | - 多线程模型下,主线程等待用户请求,用户有请求到达时创建新线程。新线程负责具体的工作,即使是因为调用了read函数被阻塞也不会影响服务器。我们还可以进一步优化创建连接池和线程池以减小频繁调用I/O接口的开销。但新问题随之产生,每个新线程或者进程(加入使用对进程模型)都会占用大量系统资源,除此之外过多的线程和进程在调度方面开销也会大很对,所以这种模型并不适合大并发量。 297 | 298 | **非阻塞I/O** 299 | 300 | - 阻塞和非阻塞最大的区别在于调用I/O系统调用后,是等整个I/O过程完成再把操作权限返回给用户还是会立即返回。 301 | 302 | - 可以使用以下语句将句柄fd设置为非阻塞I/O:fcntl(fd, F_SETFL, O_NONBLOCK); 303 | 304 | - 非阻塞I/O在调用后会立即返回,用户进程对返回的返回值判断以区分是否完成了I/O。如果返回大于0表示完成了数据读取,返回值即读取的字节数;返回0表示连接已经正常断开;返回-1表示错误,接下来用户进程会不停地询问kernel是否准备完毕。 305 | 306 | - 非阻塞I/O虽然不再会完全阻塞用户进程,但实际上由于用户进程需要不停地询问kernel是否准备完数据,所以整体效率依旧非常低,不适合做并发。 307 | 308 | **I/O多路复用(事件驱动模型)** 309 | 310 | 前面已经论述了多进程、多进程模型会因为开销巨大和调度困难而导致并不能承受高并发量。但不适用这种模型的话,无论是阻塞还是非阻塞方式都会导致整个服务器停滞。 311 | 312 | 所以对于大并发量,我们需要一种代理模型可以帮助我们集中去管理所有的socket连接,一旦某个socket数据到达了就执行其对应的用户进程,I/O多路复用就是这么一种模型。Linux下I/O多路复用的系统调用有select,poll和epoll,但从本质上来讲他们都是同步I/O范畴。 313 | 314 | 1. select 315 | 316 | - 相关接口: 317 | 318 | int select (int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); 319 | 320 | FD_ZERO(int fd, fd_set* fds) //清空集合 321 | 322 | FD_SET(int fd, fd_set* fds) //将给定的描述符加入集合 323 | 324 | FD_ISSET(int fd, fd_set* fds) //判断指定描述符是否在集合中 325 | 326 | FD_CLR(int fd, fd_set* fds) //将给定的描述符从文件中删除 327 | 328 | - 参数: 329 | 330 | maxfd:当前最大文件描述符的值+1(≠ MAX_CONN)。 331 | 332 | readfds:指向读文件队列集合(fd_set)的指针。 333 | 334 | writefds:同上,指向读集合的指针。 335 | 336 | writefds:同上,指向错误集合的指针。 337 | 338 | timeout:指向timeval结构指针,用于设置超时。 339 | 340 | - 其他: 341 | 342 | 判断和操作对象为set_fd集合,集合大小为单个进程可打开的最大文件数1024或2048(可重新编译内核修改但不建议)。 343 | 344 | 2. poll 345 | 346 | - 相关接口: 347 | 348 | int poll(struct pollfd *fds, unsigned int nfds, int timeout); 349 | 350 | - 结构体定义: 351 | ```C++ 352 | struct pollfd{ 353 | int fd; // 文件描述符 354 | short events; // 等到的事件 355 | short revents; // 实际发生的事件 356 | } 357 | ``` 358 | 359 | - 参数: 360 | 361 | fds:指向pollfd结构体数组的指针。 362 | 363 | nfds:pollfd数组当前已被使用的最大下标。 364 | 365 | timeout:等待毫秒数。 366 | 367 | - 其他: 368 | 369 | 判断和操作对象是元素为pollfd类型的数组,数组大小自己设定,即为最大连接数。 370 | 371 | 3. epoll 372 | 373 | - 相关接口: 374 | 375 | int epoll_create(int size); // 创建epoll句柄 376 | 377 | int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); // 事件注册函数 378 | 379 | int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); 380 | 381 | - 结构体定义: 382 | ```C++ 383 | struct epoll_event{ 384 | __uint32_t events; 385 | epoll_data_t data; 386 | }; 387 | typedef union epoll_data{ 388 | void *ptr; 389 | int fd; 390 | __uint32_t u32; 391 | __uint64_t u64; 392 | }epoll_data_t; 393 | ``` 394 | 395 | - 参数: 396 | 397 | size:用来告诉内核要监听的数目。 398 | 399 | epfd:epoll函数的返回值。 400 | 401 | op:表示动作(EPOLL_CTL_ADD/EPOLL_CTL_FD/EPOLL_CTL_DEL)。 402 | 403 | fd:需要监听的fd。 404 | 405 | events:指向epoll_event的指针,该结构记录监听的事件。 406 | 407 | maxevents:告诉内核events的大小。 408 | 409 | timeout:超时时间(ms为单位,0表示立即返回,-1将不确定)。 410 | 411 | 4. select、poll和epoll区别 412 | 413 | - 操作方式及效率: 414 | 415 | select是遍历,需要遍历fd_set每一个比特位(= MAX_CONN),O(n);poll是遍历,但只遍历到pollfd数组当前已使用的最大下标(≠ MAX_CONN),O(n);epoll是回调,O(1)。 416 | 417 | - 最大连接数: 418 | 419 | select为1024/2048(一个进程打开的文件数是有限制的);poll无上限;epoll无上限。 420 | 421 | - fd拷贝: 422 | 423 | select每次都需要把fd集合从用户态拷贝到内核态;poll每次都需要把fd集合从用户态拷贝到内核态;epoll调用epoll_ctl时拷贝进内核并放到事件表中,但用户进程和内核通过mmap映射共享同一块存储,避免了fd从内核赋值到用户空间。 424 | 425 | - 其他: 426 | 427 | select每次内核仅仅是通知有消息到了需要处理,具体是哪一个需要遍历所有的描述符才能找到。epoll不仅通知有I/O到来还可通过callback函数具体定位到活跃的socket,实现伪AIO。 428 | 429 | **异步I/O模型** 430 | 431 | - 上面三种I/O方式均属于同步I/O。 432 | 433 | - 从阻塞式I/O到非阻塞I/O,我们已经做到了调用I/O请求后立即返回,但不停轮询的操作效率又很低,如果能够既像非阻塞I/O能够立即返回又能不一直轮询的话会更符合我们的预期。 434 | 435 | - 之所以用户进程会不停轮询就是因为在数据准备完毕后内核不会回调用户进程,只能通过用户进程一次又一次轮询来查询I/O结果。如果内核能够在完成I/O后通过消息告知用户进程来处理已经得到的数据自然是最好的,异步I/O就是这么回事。 436 | 437 | - 异步I/O就是当用户进程发起I/O请求后立即返回,直到内核发送一个信号,告知进程I/O已完成,在整个过程中,都没有进程被阻塞。看上去异步I/O和非阻塞I/O的区别在于:判断数据是否准备完毕的任务从用户进程本身被委托给内核来完成。这里所谓的异步只是操作系统提供的一直机制罢了。 438 | -------------------------------------------------------------------------------- /项目目的.md: -------------------------------------------------------------------------------- 1 | # 项目目的 2 | 3 | TKeed既是个人网络编程项目,也是一次串联知识结构的过程,在整个开发过程不仅仅是为了做出一个"能用"的Web服务器,也在扩展性、稳定性方面做了很多思考和总结,也给出了测试数据和系统架构,方便别人学习交流。 4 | 5 | - 软件开发流程 6 | 7 | 遵循完整开发流程,确定需求 -> 选定服务器模型 -> 定义数据结构 -> 开发辅助工具 -> 单元测试 -> 核心部分开发 -> 集成测试 -> 性能测试。开发环境也统一到Linux环境下,通过git进行版本控制,尽可能模拟真实工作环境。 8 | 9 | - 基础知识 10 | 11 | 开发HTTP服务器从宏观上来说会对网络协议TCP及其各个状态理解更深,会对HTTP协议主要字段的功能理解更深,会对操作系统中多线程、多进程并发概念和局限性理解更深刻,会对网络I/O模型认识更深。 12 | 13 | - 数据结构 14 | 15 | 通过对场景需求和将来扩展性的研究,需要设计合理的、高效的数据结构,比如在本项目中最核心的tk_request_t、tk_timer_t和tk_pq_t结构考虑到了扩展性和操作高效性(比如tk_time_t中的deleted字段、tk_pq_t中的size字段)。同时根据需求实现了list和priority_queue库并提供统一接口,可以帮助更好地掌握数据结构和设计接口。 16 | 17 | - 编程语言 18 | 19 | 项目中涉及C语言中方方面面,比如预定义、typedef、全局变量、静态全局变量、函数指针、位运算、强制类型转换、结构体操作等,很多在调试时候遇到过问题,但也能学到很多。另外,也顺带学些了很多编译和调试的小技巧。 20 | 21 | - 开发工具 22 | 23 | 最后,在开发过程中使用到的都是最基本、最常用的开发工具,开发、调试、版本控制都有所涉及,可以更好地利用辅助工具完成开发任务。 24 | 25 | --- --------------------------------------------------------------------------------