[
  {
    "path": "Client/Codes/README.md",
    "content": "一个针对服务器的压力测试程序，可自定义请求客户的数目\n"
  },
  {
    "path": "Client/Codes/client.pro",
    "content": "QT += core\nQT -= gui\n\nTARGET = client\nCONFIG += console\nQMAKE_CXXFLAGS += -std=c++11\nCONFIG -= app_bundle\n\nTEMPLATE = app\n\nSOURCES += main.cpp\n\n"
  },
  {
    "path": "Client/Codes/main.cpp",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/epoll.h>\n#include <fcntl.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <string.h>\n\nstatic const char* request = \"GET http://localhost/html/index.html HTTP/1.1\\r\\nConnection: keep-alive\\r\\n\\r\\nxxxxxxxxxxxx\";\n\nint setnonblocking( int fd )\n{\n    int old_option = fcntl( fd, F_GETFL );\n    int new_option = old_option | O_NONBLOCK;\n    fcntl( fd, F_SETFL, new_option );\n    return old_option;\n}\n\nvoid addfd( int epoll_fd, int fd )\n{\n    epoll_event event;\n    event.data.fd = fd;\n    event.events = EPOLLOUT | EPOLLET | EPOLLERR;\n    epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event );\n    setnonblocking( fd );\n}\n\nbool write_nbytes( int sockfd, const char* buffer, int len )\n{\n    int bytes_write = 0;\n    printf( \"write out %d bytes to socket %d\\n\", len, sockfd );\n    while( 1 )\n    {\n        bytes_write = send( sockfd, buffer, len, 0 );\n        if ( bytes_write == -1 )\n        {\n            return false;\n        }\n        else if ( bytes_write == 0 )\n        {\n            return false;\n        }\n\n        len -= bytes_write;\n        buffer = buffer + bytes_write;\n        if ( len <= 0 )\n        {\n            return true;\n        }\n    }\n}\n\nbool read_once( int sockfd, char* buffer, int len )\n{\n    int bytes_read = 0;\n    memset( buffer, '\\0', len );\n    bytes_read = recv( sockfd, buffer, len, 0 );\n    if ( bytes_read == -1 )\n    {\n        return false;\n    }\n    else if ( bytes_read == 0 )\n    {\n        return false;\n    }\n    printf( \"read in %d bytes from socket %d with content: %s\\n\", bytes_read, sockfd, buffer );\n\n    return true;\n}\n\nvoid start_conn( int epoll_fd, int num, const char* ip, int port )\n{\n    int ret = 0;\n    struct sockaddr_in address;\n    bzero( &address, sizeof( address ) );\n    address.sin_family = AF_INET;\n    inet_pton( AF_INET, ip, &address.sin_addr );\n    address.sin_port = htons( port );\n\n    for ( int i = 0; i < num; ++i )\n    {\n        sleep( 0.01 );\n        int sockfd = socket( PF_INET, SOCK_STREAM, 0 );\n        printf( \"create 1 sock\\n\" );\n        if( sockfd < 0 )\n        {\n            continue;\n        }\n\n        if (  connect( sockfd, ( struct sockaddr* )&address, sizeof( address ) ) == 0  )\n        {\n            printf( \"build connection %d\\n\", i );\n            addfd( epoll_fd, sockfd );\n        }\n    }\n}\n\nvoid close_conn( int epoll_fd, int sockfd )\n{\n    epoll_ctl( epoll_fd, EPOLL_CTL_DEL, sockfd, 0 );\n    close( sockfd );\n}\n\nint main( int argc, char* argv[] )\n{\n    if(argc<4)\n    {\n        fprintf(stderr,\"usage: %s server_ip server_port client_num \",argv[0]);\n        exit(0);\n    }\n    int epoll_fd = epoll_create( 100 );\n    start_conn( epoll_fd, atoi( argv[ 3 ] ), argv[1], atoi( argv[2] ) );\n    epoll_event events[ 10000 ];\n    char buffer[ 2048 ];\n    while ( 1 )\n    {\n        int fds = epoll_wait( epoll_fd, events, 10000, 2000 );\n        for ( int i = 0; i < fds; i++ )\n        {\n            int sockfd = events[i].data.fd;\n            if ( events[i].events & EPOLLIN )\n            {\n                if ( ! read_once( sockfd, buffer, 2048 ) )\n                {\n                    close_conn( epoll_fd, sockfd );\n                }\n                struct epoll_event event;\n                event.events = EPOLLOUT | EPOLLET | EPOLLERR;\n                event.data.fd = sockfd;\n                epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event );\n            }\n            else if( events[i].events & EPOLLOUT )\n            {\n                if ( ! write_nbytes( sockfd, request, strlen( request ) ) )\n                {\n                    close_conn( epoll_fd, sockfd );\n                }\n                struct epoll_event event;\n                event.events = EPOLLIN | EPOLLET | EPOLLERR;\n                event.data.fd = sockfd;\n                epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event );\n            }\n            else if( events[i].events & EPOLLERR )\n            {\n                close_conn( epoll_fd, sockfd );\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "Client/Debug/Makefile",
    "content": "#############################################################################\n# Makefile for building: client\n# Generated by qmake (2.01a) (Qt 4.8.7) on: ?? 2? 27 15:14:57 2020\n# Project:  ../Codes/client.pro\n# Template: app\n# Command: /usr/lib/x86_64-linux-gnu/qt4/bin/qmake -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/client.pro\n#############################################################################\n\n####### Compiler, tools and options\n\nCC            = gcc\nCXX           = g++\nDEFINES       = -DQT_CORE_LIB -DQT_SHARED\nCFLAGS        = -m64 -pipe -g -Wall -W -D_REENTRANT $(DEFINES)\nCXXFLAGS      = -m64 -pipe -std=c++11 -g -Wall -W -D_REENTRANT $(DEFINES)\nINCPATH       = -I/usr/share/qt4/mkspecs/linux-g++-64 -I../Codes -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. -I../Codes -I.\nLINK          = g++\nLFLAGS        = -m64\nLIBS          = $(SUBLIBS)  -L/usr/lib/x86_64-linux-gnu -lQtCore -lpthread \nAR            = ar cqs\nRANLIB        = \nQMAKE         = /usr/lib/x86_64-linux-gnu/qt4/bin/qmake\nTAR           = tar -cf\nCOMPRESS      = gzip -9f\nCOPY          = cp -f\nSED           = sed\nCOPY_FILE     = $(COPY)\nCOPY_DIR      = $(COPY) -r\nSTRIP         = strip\nINSTALL_FILE  = install -m 644 -p\nINSTALL_DIR   = $(COPY_DIR)\nINSTALL_PROGRAM = install -m 755 -p\nDEL_FILE      = rm -f\nSYMLINK       = ln -f -s\nDEL_DIR       = rmdir\nMOVE          = mv -f\nCHK_DIR_EXISTS= test -d\nMKDIR         = mkdir -p\n\n####### Output directory\n\nOBJECTS_DIR   = ./\n\n####### Files\n\nSOURCES       = ../Codes/main.cpp \nOBJECTS       = main.o\nDIST          = /usr/share/qt4/mkspecs/common/unix.conf \\\n\t\t/usr/share/qt4/mkspecs/common/linux.conf \\\n\t\t/usr/share/qt4/mkspecs/common/gcc-base.conf \\\n\t\t/usr/share/qt4/mkspecs/common/gcc-base-unix.conf \\\n\t\t/usr/share/qt4/mkspecs/common/g++-base.conf \\\n\t\t/usr/share/qt4/mkspecs/common/g++-unix.conf \\\n\t\t/usr/share/qt4/mkspecs/qconfig.pri \\\n\t\t/usr/share/qt4/mkspecs/features/qt_functions.prf \\\n\t\t/usr/share/qt4/mkspecs/features/qt_config.prf \\\n\t\t/usr/share/qt4/mkspecs/features/exclusive_builds.prf \\\n\t\t/usr/share/qt4/mkspecs/features/default_pre.prf \\\n\t\t/usr/share/qt4/mkspecs/features/debug.prf \\\n\t\t/usr/share/qt4/mkspecs/features/default_post.prf \\\n\t\t/usr/share/qt4/mkspecs/features/shared.prf \\\n\t\t/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \\\n\t\t/usr/share/qt4/mkspecs/features/warn_on.prf \\\n\t\t/usr/share/qt4/mkspecs/features/qt.prf \\\n\t\t/usr/share/qt4/mkspecs/features/unix/thread.prf \\\n\t\t/usr/share/qt4/mkspecs/features/moc.prf \\\n\t\t/usr/share/qt4/mkspecs/features/resources.prf \\\n\t\t/usr/share/qt4/mkspecs/features/uic.prf \\\n\t\t/usr/share/qt4/mkspecs/features/yacc.prf \\\n\t\t/usr/share/qt4/mkspecs/features/lex.prf \\\n\t\t/usr/share/qt4/mkspecs/features/include_source_dir.prf \\\n\t\t../Codes/client.pro\nQMAKE_TARGET  = client\nDESTDIR       = \nTARGET        = client\n\nfirst: all\n####### Implicit rules\n\n.SUFFIXES: .o .c .cpp .cc .cxx .C\n\n.cpp.o:\n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n.cc.o:\n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n.cxx.o:\n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n.C.o:\n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n.c.o:\n\t$(CC) -c $(CFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n####### Build rules\n\nall: Makefile $(TARGET)\n\n$(TARGET):  $(OBJECTS)  \n\t$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)\n\t{ test -n \"$(DESTDIR)\" && DESTDIR=\"$(DESTDIR)\" || DESTDIR=.; } && test $$(gdb --version | sed -e 's,[^0-9][^0-9]*\\([0-9]\\)\\.\\([0-9]\\).*,\\1\\2,;q') -gt 72 && gdb --nx --batch --quiet -ex 'set confirm off' -ex \"save gdb-index $$DESTDIR\" -ex quit '$(TARGET)' && test -f $(TARGET).gdb-index && objcopy --add-section '.gdb_index=$(TARGET).gdb-index' --set-section-flags '.gdb_index=readonly' '$(TARGET)' '$(TARGET)' && rm -f $(TARGET).gdb-index || true\n\nMakefile: ../Codes/client.pro  /usr/share/qt4/mkspecs/linux-g++-64/qmake.conf /usr/share/qt4/mkspecs/common/unix.conf \\\n\t\t/usr/share/qt4/mkspecs/common/linux.conf \\\n\t\t/usr/share/qt4/mkspecs/common/gcc-base.conf \\\n\t\t/usr/share/qt4/mkspecs/common/gcc-base-unix.conf \\\n\t\t/usr/share/qt4/mkspecs/common/g++-base.conf \\\n\t\t/usr/share/qt4/mkspecs/common/g++-unix.conf \\\n\t\t/usr/share/qt4/mkspecs/qconfig.pri \\\n\t\t/usr/share/qt4/mkspecs/features/qt_functions.prf \\\n\t\t/usr/share/qt4/mkspecs/features/qt_config.prf \\\n\t\t/usr/share/qt4/mkspecs/features/exclusive_builds.prf \\\n\t\t/usr/share/qt4/mkspecs/features/default_pre.prf \\\n\t\t/usr/share/qt4/mkspecs/features/debug.prf \\\n\t\t/usr/share/qt4/mkspecs/features/default_post.prf \\\n\t\t/usr/share/qt4/mkspecs/features/shared.prf \\\n\t\t/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \\\n\t\t/usr/share/qt4/mkspecs/features/warn_on.prf \\\n\t\t/usr/share/qt4/mkspecs/features/qt.prf \\\n\t\t/usr/share/qt4/mkspecs/features/unix/thread.prf \\\n\t\t/usr/share/qt4/mkspecs/features/moc.prf \\\n\t\t/usr/share/qt4/mkspecs/features/resources.prf \\\n\t\t/usr/share/qt4/mkspecs/features/uic.prf \\\n\t\t/usr/share/qt4/mkspecs/features/yacc.prf \\\n\t\t/usr/share/qt4/mkspecs/features/lex.prf \\\n\t\t/usr/share/qt4/mkspecs/features/include_source_dir.prf \\\n\t\t/usr/lib/x86_64-linux-gnu/libQtCore.prl\n\t$(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/client.pro\n/usr/share/qt4/mkspecs/common/unix.conf:\n/usr/share/qt4/mkspecs/common/linux.conf:\n/usr/share/qt4/mkspecs/common/gcc-base.conf:\n/usr/share/qt4/mkspecs/common/gcc-base-unix.conf:\n/usr/share/qt4/mkspecs/common/g++-base.conf:\n/usr/share/qt4/mkspecs/common/g++-unix.conf:\n/usr/share/qt4/mkspecs/qconfig.pri:\n/usr/share/qt4/mkspecs/features/qt_functions.prf:\n/usr/share/qt4/mkspecs/features/qt_config.prf:\n/usr/share/qt4/mkspecs/features/exclusive_builds.prf:\n/usr/share/qt4/mkspecs/features/default_pre.prf:\n/usr/share/qt4/mkspecs/features/debug.prf:\n/usr/share/qt4/mkspecs/features/default_post.prf:\n/usr/share/qt4/mkspecs/features/shared.prf:\n/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf:\n/usr/share/qt4/mkspecs/features/warn_on.prf:\n/usr/share/qt4/mkspecs/features/qt.prf:\n/usr/share/qt4/mkspecs/features/unix/thread.prf:\n/usr/share/qt4/mkspecs/features/moc.prf:\n/usr/share/qt4/mkspecs/features/resources.prf:\n/usr/share/qt4/mkspecs/features/uic.prf:\n/usr/share/qt4/mkspecs/features/yacc.prf:\n/usr/share/qt4/mkspecs/features/lex.prf:\n/usr/share/qt4/mkspecs/features/include_source_dir.prf:\n/usr/lib/x86_64-linux-gnu/libQtCore.prl:\nqmake:  FORCE\n\t@$(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/client.pro\n\ndist: \n\t@$(CHK_DIR_EXISTS) .tmp/client1.0.0 || $(MKDIR) .tmp/client1.0.0 \n\t$(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/client1.0.0/ && $(COPY_FILE) --parents ../Codes/main.cpp .tmp/client1.0.0/ && (cd `dirname .tmp/client1.0.0` && $(TAR) client1.0.0.tar client1.0.0 && $(COMPRESS) client1.0.0.tar) && $(MOVE) `dirname .tmp/client1.0.0`/client1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/client1.0.0\n\n\nclean:compiler_clean \n\t-$(DEL_FILE) $(OBJECTS)\n\t-$(DEL_FILE) *~ core *.core\n\n\n####### Sub-libraries\n\ndistclean: clean\n\t-$(DEL_FILE) $(TARGET) \n\t-$(DEL_FILE) Makefile\n\n\ncheck: first\n\nmocclean: compiler_moc_header_clean compiler_moc_source_clean\n\nmocables: compiler_moc_header_make_all compiler_moc_source_make_all\n\ncompiler_moc_header_make_all:\ncompiler_moc_header_clean:\ncompiler_rcc_make_all:\ncompiler_rcc_clean:\ncompiler_image_collection_make_all: qmake_image_collection.cpp\ncompiler_image_collection_clean:\n\t-$(DEL_FILE) qmake_image_collection.cpp\ncompiler_moc_source_make_all:\ncompiler_moc_source_clean:\ncompiler_uic_make_all:\ncompiler_uic_clean:\ncompiler_yacc_decl_make_all:\ncompiler_yacc_decl_clean:\ncompiler_yacc_impl_make_all:\ncompiler_yacc_impl_clean:\ncompiler_lex_make_all:\ncompiler_lex_clean:\ncompiler_clean: \n\n####### Compile\n\nmain.o: ../Codes/main.cpp \n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o ../Codes/main.cpp\n\n####### Install\n\ninstall:   FORCE\n\nuninstall:   FORCE\n\nFORCE:\n\n"
  },
  {
    "path": "README.md",
    "content": "* **前言**\n\t\n\t本项目是基于C++线程池的轻量级Web并发服务器,事件处理模式采用Reactor模式，主线程只负责监听文件描述符是否有事件发生，读写数据、接收新的连接、以及处理客户请求均在工作线程中实现；使用半同步/半异步模式，每个线程（主线程和工作线程）都通过一个epoll维护自己的事件循环，它们各自独立地监听不同事件。使用C++ 11的atomic原子变量来同步线程访问从而避免同步错误；使用STL的优先队列作为定时器容器来回收非活动长连接。本项目的具体设计思路可以参照本人的知乎文章[C++网络编程入门：轻量级Web并发服务器开发](https://zhuanlan.zhihu.com/p/109905285)，纯属抛砖引玉，欢迎大佬们批评指正。\n\n* **运行环境**\n\n\t(1).系统：Ubuntu16.04;\n\t\n\t(2).语言：C++ 11及以上版本;\n\n* **支持功能**\n\t\n\t(1).支持Get请求;\n\t(2).支持长连接/短连接;\n\t(3).支持ipv4/ipv6;(4).支持tcp;(5).支持请求静态内容(6).支持并发请求\n\t\n* **开始运行**\n\n\t(1).下载程序;\n\n\t`git clone git@github.com:Buerzhu/TinyWeb.git`\n\t\n\t(2).打开新终端，指定ip地址和端口运行服务器程序：\n\n\t`cd ~/TinyWeb/Server/Debug`\n\n\t`./server 127.0.0.1 12345`\n\t\n\t(3).打开新终端，指定ip地址、端口、客户连接数运行压力测试程序：\n\t\n\t`cd ~/TinyWeb/Client/Debug`\n\t\n\t`./client 127.0.0.1 12345 1000`\n"
  },
  {
    "path": "Server/Codes/README.md",
    "content": "* **web_thread.h**\n\t\n\t(1). 定义了webthread类;\n\t\n\t(2). 该类是主线程与子线程通信的媒介，本程序对于每个子线程建立对应的wedthread全局对象,主线程通过该对象来与子线程通信，子线程通过该对象接收来自主线程的消息，并运行该对象的work()函数来处理主线程消息和用户的http请求。\n\n* **http_conn.h**\n\n\t(1).定义了http_conn类和util_timer类;\n\t\n\t(2).http_conn类是用于处理http请求和作出http应答的一个类，本程序对于每个新连接的用户都分配一个http_conn对象用于处理http请求;\n\t\n\t(3).util_timer类是定时器类，本程序对于每个新连接的用户都分配一个定时器对象，并设置该定时器的超时时间，如果长连接用户在超时时间内都处于非活动状态则关闭用户连接，如果用户在超时时间到达之前重新活动则延长超时时间。\n\t\n\n* **web_function.h**\n\t\n\t(1).该文件用于定义全局变量和常量，包括线程数、当前总用户数、读写缓冲区大小等，如果要支持更多的并发用户请求，应该修改该文件中定义的这些常量;\n\t\n\t(2).该文件还提供了一些通用的基本功能函数，例如显示当前时区时间的函数，显示用户ip地址的函数等;\n\t\n\t\n* **main.cpp**\n\t\n\t(1). 程序运行入口\n\t\n\t(2). 主线程只负责监听文件描述符是否有事件发生而不处理面向用户的业务逻辑(Reactor模式);\n\t\n* **server.pro**\n\n\t(1). 该文件是qtcreator关于qmake的工程文件，qt通过该文件来生成Makefile文件。\n\n\t\n\t\t\n\t\n"
  },
  {
    "path": "Server/Codes/http_conn.h",
    "content": "#ifndef HTTPCONNECTION_H\n#define HTTPCONNECTION_H\n\n#include <unistd.h>\n#include <signal.h>\n#include <sys/types.h>\n#include <sys/epoll.h>\n#include <fcntl.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <assert.h>\n#include <sys/stat.h>\n#include <string.h>\n#include <pthread.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <stdarg.h>\n#include <errno.h>\n#include<time.h>\n#include<queue>\n#include<stack>\n#include\"web_function.h\"\n\n\nusing namespace std;\n\nconst char* ok_200_title = \"OK\";\nconst char* error_400_title = \"Bad Request\";\nconst char* error_400_form = \"Your request has bad syntax or is inherently impossible to satisfy.\\n\";\nconst char* error_403_title = \"Forbidden\";\nconst char* error_403_form = \"You do not have permission to get file from this server.\\n\";\nconst char* error_404_title = \"Not Found\";\nconst char* error_404_form = \"The requested file was not found on this server.\\n\";\nconst char* error_500_title = \"Internal Error\";\nconst char* error_500_form = \"There was an unusual problem serving the requested file.\\n\";\nchar* doc_root=new char[256];\n\nclass util_timer;\n\nclass http_conn\n{\npublic:\n    util_timer* timer;\n    enum METHOD { GET = 0, POST, HEAD, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH };//http请求方法，本程序只支持get\n    enum CHECK_STATE { CHECK_STATE_REQUESTLINE = 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT };//解析客户请求时当前所处的状态\n    enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, NO_RESOURCE, FORBIDDEN_REQUEST, FILE_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION };//服务器处理http请求的可能结果\n    enum LINE_STATUS { LINE_OK = 0, LINE_BAD, LINE_OPEN };//每一行的读取状态\n\npublic:\n    http_conn(){}\n    ~http_conn(){}\n\npublic:\n    void init(int epollfd, int sockfd, const sockaddr_in& addr ,util_timer* _timer,int index,stack<int>* _st);//初始化新接受的连接\n    void close_conn( bool real_close = true );//关闭连接\n    void process();//处理客户请求\n    bool read();//非阻塞读操作\n    bool write();//非阻塞写操作\n\nprivate:\n    void init();//初始化连接\n\n    HTTP_CODE process_read();//解析http请求\n    bool process_write( HTTP_CODE ret );//填充http应答\n\n    //下面这一组函数被process_read()函数调用以分析http请求\n    HTTP_CODE parse_request_line( char* text );\n    HTTP_CODE parse_headers( char* text );\n    HTTP_CODE parse_content( char* text );\n    HTTP_CODE do_request();\n    char* get_line() { return m_read_buf + m_start_line; }\n    LINE_STATUS parse_line();\n\n    //下面这一组函数被process_write()函数调用以填充http应答\n    void unmap();\n    bool add_response( const char* format, ... );\n    bool add_content( const char* content );\n    bool add_status_line( int status, const char* title );\n    bool add_headers( int content_length );\n    bool add_content_length( int content_length );\n    bool add_linger();\n    bool add_blank_line();\n\npublic:\n    int m_epollfd;\n\nprivate:\n    int m_sockfd;\n    sockaddr_in m_address;\n\n    char m_read_buf[ READ_BUFFER_SIZE ];\n    int m_read_idx;\n    int m_checked_idx;\n    int m_start_line;\n    char m_write_buf[ WRITE_BUFFER_SIZE ];\n    int m_write_idx;\n\n    CHECK_STATE m_check_state;\n    METHOD m_method;\n\n    char m_real_file[ FILENAME_LEN ];\n    char* m_url;\n    char* m_version;\n    char* m_host;\n    int m_content_length;\n    bool m_linger;\n\n    char* m_file_address;\n    struct stat m_file_stat;\n    struct iovec m_iv[2];\n    int m_iv_count;\n\n    int user_index;\n    stack<int>* index_free;\n};\n\nclass util_timer\n{\npublic:\n    util_timer(time_t t):expire(t),free(true) {}\n    time_t expire;//超时时间\n    bool free;//是否被某个用户占用\n    int sockfd;//关联的连接描述符\n    void timed_event(){client->close_conn();}//超时操作，关闭关联的连接\n    http_conn* client;//关联的用户地址\n};\n\n\nvoid http_conn::close_conn( bool real_close )\n{\n    if( real_close && ( m_sockfd != -1 ) )\n    {\n        removefd( m_epollfd, m_sockfd );\n        timer->free=true;\n        index_free->push(user_index);\n        m_sockfd = -1;\n\n        show_sys_time();\n        printf(\"close connection: \");\n        show_addr(m_address);\n        user_count--;//C++11\n        printf(\"user_online: %d\\n\",(int)user_count);//C++11\n    }\n}\n\nvoid http_conn::init( int epollfd,int sockfd, const sockaddr_in& addr,util_timer* _timer,int index,stack<int>* st)\n{\n    m_epollfd=epollfd;\n    m_sockfd = sockfd;\n    m_address = addr;\n    timer=_timer;\n    timer->free=false;\n    timer->sockfd=sockfd;\n    user_index=index;\n    index_free=st;\n    int error = 0;\n    socklen_t len = sizeof( error );\n    getsockopt( m_sockfd, SOL_SOCKET, SO_ERROR, &error, &len );\n//    int reuse = 1;\n//    setsockopt( m_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof( reuse ) );\n    addfd( m_epollfd, sockfd, true );\n\n    init();\n\n\n    show_sys_time();\n    printf(\"new connection: \");\n    show_addr(addr);\n    user_count++;//C++11\n    printf(\"user_count: %d\\n\",(int)user_count);\n    if(user_count.load()>max_user_online.load())\n    {\n        max_user_online=user_count.load();\n    }\n}\n\nvoid http_conn::init()\n{\n    m_check_state = CHECK_STATE_REQUESTLINE;\n    m_linger = false;\n\n    m_method = GET;\n    m_url = 0;\n    m_version = 0;\n    m_content_length = 0;\n    m_host = 0;\n    m_start_line = 0;\n    m_checked_idx = 0;\n    m_read_idx = 0;\n    m_write_idx = 0;\n    memset( m_read_buf, '\\0', READ_BUFFER_SIZE );\n    memset( m_write_buf, '\\0', WRITE_BUFFER_SIZE );\n    memset( m_real_file, '\\0', FILENAME_LEN );\n}\n\nhttp_conn::LINE_STATUS http_conn::parse_line()\n{\n    char temp;\n    for ( ; m_checked_idx < m_read_idx; ++m_checked_idx )\n    {\n        temp = m_read_buf[ m_checked_idx ];\n        if ( temp == '\\r' )\n        {\n            if ( ( m_checked_idx + 1 ) == m_read_idx )\n            {\n                return LINE_OPEN;\n            }\n            else if ( m_read_buf[ m_checked_idx + 1 ] == '\\n' )\n            {\n                m_read_buf[ m_checked_idx++ ] = '\\0';\n                m_read_buf[ m_checked_idx++ ] = '\\0';\n                return LINE_OK;\n            }\n\n            return LINE_BAD;\n        }\n        else if( temp == '\\n' )\n        {\n            if( ( m_checked_idx > 1 ) && ( m_read_buf[ m_checked_idx - 1 ] == '\\r' ) )\n            {\n                m_read_buf[ m_checked_idx-1 ] = '\\0';\n                m_read_buf[ m_checked_idx++ ] = '\\0';\n                return LINE_OK;\n            }\n            return LINE_BAD;\n        }\n    }\n\n    return LINE_OPEN;\n}\n\nbool http_conn::read()\n{\n    if( m_read_idx >= READ_BUFFER_SIZE )\n    {\n        return false;\n    }\n\n    int bytes_read = 0;\n    while( true )\n    {\n        bytes_read = recv( m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0 );\n        if ( bytes_read == -1 )\n        {\n            if( errno == EAGAIN || errno == EWOULDBLOCK )\n            {\n                break;\n            }\n            return false;\n        }\n        else if ( bytes_read == 0 )\n        {\n            return false;\n        }\n\n        m_read_idx += bytes_read;\n    }\n    return true;\n}\n\nhttp_conn::HTTP_CODE http_conn::parse_request_line( char* text )\n{\n    m_url = strpbrk( text, \" \\t\" );\n    if ( ! m_url )\n    {\n        return BAD_REQUEST;\n    }\n    *m_url++ = '\\0';\n\n    char* method = text;\n    if ( strcasecmp( method, \"GET\" ) == 0 )\n    {\n        m_method = GET;\n    }\n    else\n    {\n        return BAD_REQUEST;\n    }\n\n    m_url += strspn( m_url, \" \\t\" );\n    m_version = strpbrk( m_url, \" \\t\" );\n    if ( ! m_version )\n    {\n        return BAD_REQUEST;\n    }\n    *m_version++ = '\\0';\n    m_version += strspn( m_version, \" \\t\" );\n    if ( strcasecmp( m_version, \"HTTP/1.1\" ) != 0 )\n    {\n        return BAD_REQUEST;\n    }\n\n    if ( strncasecmp( m_url, \"http://\", 7 ) == 0 )\n    {\n        m_url += 7;\n        m_url = strchr( m_url, '/' );\n    }\n\n    if ( ! m_url || m_url[ 0 ] != '/' )\n    {\n        return BAD_REQUEST;\n    }\n\n    m_check_state = CHECK_STATE_HEADER;\n    return NO_REQUEST;\n}\n\nhttp_conn::HTTP_CODE http_conn::parse_headers( char* text )\n{\n    if( text[ 0 ] == '\\0' )\n    {\n        if ( m_method == HEAD )\n        {\n            return GET_REQUEST;\n        }\n\n        if ( m_content_length != 0 )\n        {\n            m_check_state = CHECK_STATE_CONTENT;\n            return NO_REQUEST;\n        }\n\n        return GET_REQUEST;\n    }\n    else if ( strncasecmp( text, \"Connection:\", 11 ) == 0 )\n    {\n        text += 11;\n        text += strspn( text, \" \\t\" );\n        if ( strcasecmp( text, \"keep-alive\" ) == 0 )\n        {\n            m_linger = true;\n        }\n    }\n    else if ( strncasecmp( text, \"Content-Length:\", 15 ) == 0 )\n    {\n        text += 15;\n        text += strspn( text, \" \\t\" );\n        m_content_length = atol( text );\n    }\n    else if ( strncasecmp( text, \"Host:\", 5 ) == 0 )\n    {\n        text += 5;\n        text += strspn( text, \" \\t\" );\n        m_host = text;\n    }\n    else\n    {\n        printf( \"oop! unknow header %s\\n\", text );\n    }\n\n    return NO_REQUEST;\n\n}\n\nhttp_conn::HTTP_CODE http_conn::parse_content( char* text )\n{\n    if ( m_read_idx >= ( m_content_length + m_checked_idx ) )\n    {\n        text[ m_content_length ] = '\\0';\n        return GET_REQUEST;\n    }\n\n    return NO_REQUEST;\n}\n\nhttp_conn::HTTP_CODE http_conn::process_read()\n{\n    LINE_STATUS line_status = LINE_OK;\n    HTTP_CODE ret = NO_REQUEST;\n    char* text = 0;\n\n    printf(\"get data from: \");\n    show_addr(m_address);\n    while ( ( ( m_check_state == CHECK_STATE_CONTENT ) && ( line_status == LINE_OK  ) )\n                || ( ( line_status = parse_line() ) == LINE_OK ) )\n    {\n        text = get_line();\n        m_start_line = m_checked_idx;\n        printf( \"got 1 http line: %s\\n\", text );\n\n        switch ( m_check_state )\n        {\n            case CHECK_STATE_REQUESTLINE:\n            {\n                ret = parse_request_line( text );\n                if ( ret == BAD_REQUEST )\n                {\n                    return BAD_REQUEST;\n                }\n                break;\n            }\n            case CHECK_STATE_HEADER:\n            {\n                ret = parse_headers( text );\n                if ( ret == BAD_REQUEST )\n                {\n                    return BAD_REQUEST;\n                }\n                else if ( ret == GET_REQUEST )\n                {\n                    return do_request();\n                }\n                break;\n            }\n            case CHECK_STATE_CONTENT:\n            {\n                ret = parse_content( text );\n                if ( ret == GET_REQUEST )\n                {\n\n                    return do_request();\n                }\n                line_status = LINE_OPEN;\n                break;\n            }\n            default:\n            {\n                printf(\"INTERNAL_ERROR.\\n\");\n                return INTERNAL_ERROR;\n            }\n        }\n    }\n\n    return NO_REQUEST;\n}\n\nhttp_conn::HTTP_CODE http_conn::do_request()\n{\n    getcwd(doc_root,256);\n    strcpy( m_real_file, doc_root );\n    int len = strlen( doc_root );\n    strncpy( m_real_file + len, m_url, FILENAME_LEN - len - 1 );\n    if ( stat( m_real_file, &m_file_stat ) < 0 )\n    {\n        return NO_RESOURCE;\n    }\n\n    if ( ! ( m_file_stat.st_mode & S_IROTH ) )\n    {\n        return FORBIDDEN_REQUEST;\n    }\n\n    if ( S_ISDIR( m_file_stat.st_mode ) )\n    {\n        return BAD_REQUEST;\n    }\n\n    int fd = open( m_real_file, O_RDONLY );\n    m_file_address = ( char* )mmap( 0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0 );\n    close( fd );\n    return FILE_REQUEST;\n}\n\nvoid http_conn::unmap()\n{\n    if( m_file_address )\n    {\n        munmap( m_file_address, m_file_stat.st_size );\n        m_file_address = 0;\n    }\n}\n\nbool http_conn::write()\n{\n    int temp = 0;\n    int bytes_have_send = 0;\n    int bytes_to_send = m_write_idx;\n    if ( bytes_to_send == 0 )\n    {\n        modfd( m_epollfd, m_sockfd, EPOLLIN );\n        init();\n        return true;\n    }\n\n    while( 1 )\n    {\n        temp = writev( m_sockfd, m_iv, m_iv_count );\n        if ( temp <= -1 )\n        {\n            if( errno == EAGAIN )\n            {\n                modfd( m_epollfd, m_sockfd, EPOLLOUT );\n                return true;\n            }\n            unmap();\n            return false;\n        }\n\n        bytes_to_send -= temp;\n        bytes_have_send += temp;\n        if ( bytes_to_send <= bytes_have_send )\n        {\n            unmap();\n            if( m_linger )\n            {\n                init();\n                modfd( m_epollfd, m_sockfd, EPOLLIN );\n                return true;\n            }\n            else\n            {\n                modfd( m_epollfd, m_sockfd, EPOLLIN );\n                return false;\n            }\n        }\n    }\n}\n\nbool http_conn::add_response( const char* format, ... )\n{\n    if( m_write_idx >= WRITE_BUFFER_SIZE )\n    {\n        return false;\n    }\n    va_list arg_list;\n    va_start( arg_list, format );\n    int len = vsnprintf( m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list );\n    if( len >= ( WRITE_BUFFER_SIZE - 1 - m_write_idx ) )\n    {\n        return false;\n    }\n    m_write_idx += len;\n    va_end( arg_list );\n    return true;\n}\n\nbool http_conn::add_status_line( int status, const char* title )\n{\n    return add_response( \"%s %d %s\\r\\n\", \"HTTP/1.1\", status, title );\n}\n\nbool http_conn::add_headers( int content_len )\n{\n    add_content_length( content_len );\n    add_linger();\n    add_blank_line();\n}\n\nbool http_conn::add_content_length( int content_len )\n{\n    return add_response( \"Content-Length: %d\\r\\n\", content_len );\n}\n\nbool http_conn::add_linger()\n{\n    return add_response( \"Connection: %s\\r\\n\", ( m_linger == true ) ? \"keep-alive\" : \"close\" );\n}\n\nbool http_conn::add_blank_line()\n{\n    return add_response( \"%s\", \"\\r\\n\" );\n}\n\nbool http_conn::add_content( const char* content )\n{\n    return add_response( \"%s\", content );\n}\n\nbool http_conn::process_write( HTTP_CODE ret )\n{\n    switch ( ret )\n    {\n        case INTERNAL_ERROR:\n        {\n            add_status_line( 500, error_500_title );\n            add_headers( strlen( error_500_form ) );\n            if ( ! add_content( error_500_form ) )\n            {\n                return false;\n            }\n            break;\n        }\n        case BAD_REQUEST:\n        {\n            add_status_line( 400, error_400_title );\n            add_headers( strlen( error_400_form ) );\n            if ( ! add_content( error_400_form ) )\n            {\n                return false;\n            }\n            break;\n        }\n        case NO_RESOURCE:\n        {\n            add_status_line( 404, error_404_title );\n            add_headers( strlen( error_404_form ) );\n            if ( ! add_content( error_404_form ) )\n            {\n                return false;\n            }\n            break;\n        }\n        case FORBIDDEN_REQUEST:\n        {\n            add_status_line( 403, error_403_title );\n            add_headers( strlen( error_403_form ) );\n            if ( ! add_content( error_403_form ) )\n            {\n                return false;\n            }\n            break;\n        }\n        case FILE_REQUEST:\n        {\n            add_status_line( 200, ok_200_title );\n            if ( m_file_stat.st_size != 0 )\n            {\n                add_headers( m_file_stat.st_size );\n                m_iv[ 0 ].iov_base = m_write_buf;\n                m_iv[ 0 ].iov_len = m_write_idx;\n                m_iv[ 1 ].iov_base = m_file_address;\n                m_iv[ 1 ].iov_len = m_file_stat.st_size;\n                m_iv_count = 2;\n                return true;\n            }\n            else\n            {\n                const char* ok_string = \"<html><body></body></html>\";\n                add_headers( strlen( ok_string ) );\n                if ( ! add_content( ok_string ) )\n                {\n                    return false;\n                }\n            }\n        }\n        default:\n        {\n            return false;\n        }\n    }\n\n    m_iv[ 0 ].iov_base = m_write_buf;\n    m_iv[ 0 ].iov_len = m_write_idx;\n    m_iv_count = 1;\n    return true;\n}\n\n\nvoid http_conn::process()\n{\n    HTTP_CODE read_ret = process_read();//解析存放在读缓冲区的http报文\n    if ( read_ret == NO_REQUEST )//如果没有读到完整的报文数据\n    {\n        printf(\"read_data incomplete.\\n\");\n        modfd( m_epollfd, m_sockfd, EPOLLIN );//重置m_sockfd的读事件，等待下一次触发读取更多报文数据\n        return;\n    }\n\n    //读取到完整报文数据或者错误报文数据时开始填充写缓冲区\n    bool write_ret = process_write( read_ret );\n    if ( ! write_ret )//如果写的内容超过写缓冲区长度则关闭对应连接\n    {\n        printf(\"write_data overflow.\\n\");\n        close_conn();\n    }\n    else\n    {\n        modfd( m_epollfd, m_sockfd, EPOLLOUT );//修改m_sockfd的读事件为写事件，事件触发时执行写操作\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Server/Codes/main.cpp",
    "content": "#include\"web_thread.h\"\n#include\"web_function.h\"\n#include\"http_conn.h\"\n\nusing namespace std;\n\nwebthread<http_conn>* thread_ptr;//指向自定义线程类的指针,这是一个全局变量，主线程和子线程通过线程类的双向管道来实现通信\n\nint sig_pipefd[2];//系统中断信号处理函数与主函数之间的通信管道，本程序把信号处理放在主函数中进行\n\n\n\nvoid* thread(void* arg)//子线程主函数\n{\n    int* p=(int*)arg;\n    int index=*p;\n    delete arg;\n\n    thread_ptr[index].work();//对应的子线程开始工作\n}\n\nvoid create_son_thread()//创建子线程\n{\n    pthread_t tid;\n    printf( \"create son threads now\\n\" );\n    for(int i=0;i<thread_num;i++)\n    {\n        int* index=new int(i);\n        if(pthread_create(&tid,NULL,thread,index)!=0)//如果线程创建失败\n        {\n            delete [] thread_ptr;\n            unix_error(\"thread created failed\");\n        }\n        if(pthread_detach(tid))//如果线程分离失败\n        {\n            delete [] thread_ptr;\n            unix_error(\"thread detach failed\");\n        }\n        else\n        {\n            thread_ptr[i].tid=tid;\n            printf(\"create thread: %ld\\n\",tid);\n\n        }\n    }\n}\n\nvoid close_son_thread()\n{\n    printf( \"kill all the thread now\\n\" );\n    char info=CLOSE_THREAD;\n    for( int i = 0; i < thread_num; ++i )\n    {\n        send(thread_ptr[i].pipefd[1],&info,sizeof(info),0);\n    }\n}\n\nstatic void sig_handler( int sig)//信号发送给主函数\n{\n    int save_errno = errno;\n    int msg = sig;\n    send( sig_pipefd[1], ( char* )&msg, 1, 0 );\n    errno = save_errno;\n}\n\nvoid deal_new_conn(int &thread_index)//主线程分配新到的连接给子线程\n{\n    char info=NEW_CONN;\n    thread_index=thread_index%thread_num;\n    send(thread_ptr[thread_index].pipefd[1],&info,sizeof(info),0);//主线程通过对应的管道与子线程通信\n    thread_index++;\n}\nvoid deal_sig_recv(bool& stop)//主线程处理系统信号\n{\n    char infos[1024];\n    int ret=recv(sig_pipefd[0],infos,sizeof(infos),0);\n    if( ret <= 0 )\n    {\n        return;\n    }\n    else\n    {\n        for( int i = 0; i < ret; ++i )\n        {\n            switch( infos[i] )\n            {\n                case SIGTERM:\n                case SIGINT:\n                {\n                    close_son_thread();\n                    stop=true;\n                    break;\n                }\n                case SIGALRM:\n                {\n                    int info=CLOSE_DEAD_CONN;\n                    for( int i = 0; i < thread_num; ++i )\n                    {\n                        send(thread_ptr[i].pipefd[1],&info,sizeof(info),0);\n                    }\n                    alarm(ALARM_TIME);//重新设置定时信号\n                    break;\n                }\n                default:\n                {\n                    break;\n                }\n            }\n        }\n    }\n}\n\n\n\nint main(int argc, char *argv[])\n{\n    if(argc<=2)\n    {\n        fprintf(stderr,\"usage: %s ip_address port\\n\",argv[0]);;\n        exit(1);\n    }\n    char* ip=argv[1];\n    char* port=argv[2];\n//    char* ip=\"127.0.0.1\";\n//    char* port=\"12345\";\n\n    int listenfd=open_listenfd(ip,port,LISTENQ);\n\n    thread_ptr=new webthread<http_conn>[thread_num];\n    webthread<http_conn>::listenfd=listenfd;\n\n    pthread_mutex_init(&mutex_conn,NULL);\n\n    create_son_thread();//创建子线程，子线程开始运行\n\n    int epollfd = epoll_create( 5 );\n    assert( epollfd != -1 );\n\n    addfd(epollfd,listenfd,true);\n    webthread<http_conn>::m_epollfd=epollfd;\n\n\n\n    int ret = socketpair( PF_UNIX, SOCK_STREAM, 0, sig_pipefd );//创建信号处理函数与主函数之间的通信管道\n    assert( ret != -1 );\n\n    setnonblocking( sig_pipefd[1] );\n    addfd( epollfd, sig_pipefd[0] );\n\n\n    addsig( SIGTERM, sig_handler );\n    addsig( SIGINT, sig_handler );\n    addsig( SIGALRM,sig_handler );\n    addsig( SIGPIPE, SIG_IGN );\n\n    epoll_event events[MAX_EVENT_NUM];\n\n\n    int m_pid=(int)getpid();\n    printf(\"PID: %d\\n\",m_pid);\n    printf(\"server start\\n\");\n    alarm(ALARM_TIME);//开始定时\n\n    bool stop=false;\n    int thread_index=0;\n\n    while(!stop)\n    {\n        int number=epoll_wait(epollfd,events,MAX_EVENT_NUM,-1);\n        if(number<0 && errno!=EINTR)\n        {\n            close_son_thread();\n            unix_error(\"main thread epoll failed.\");\n            break;\n        }\n\n        for(int i=0;i<number;i++)\n        {\n            int sockfd=events[i].data.fd;\n            if(sockfd==listenfd)//有新连接到来，循环分配给线程\n            {\n                deal_new_conn(thread_index);\n            }\n            else if( ( sockfd == sig_pipefd[0] ) && ( events[i].events & EPOLLIN ) )//接收到新信号，处理信号事件\n            {\n                deal_sig_recv(stop);\n            }\n            else\n            {\n                continue;\n            }\n\n        }\n    }\n\n    sleep(1);\n    Close(epollfd);\n    Close(listenfd);\n    Close(sig_pipefd[1]);\n    Close(sig_pipefd[0]);\n    delete [] thread_ptr;\n    printf(\"max_user_online: %d\\n\",(int)max_user_online);//最大客户端连接数\n    printf(\"user_online_now: %d\\n\",(int)user_count);//C++11,检查当前所有连接是否关闭\n    show_sys_time();\n    printf(\"closed server.\\n\");\n\n    exit(0);\n}\n\n"
  },
  {
    "path": "Server/Codes/server.pro",
    "content": "QT += core\nQT -= gui\n\nTARGET = server\nCONFIG += console\nCONFIG -= app_bundle\nQMAKE_CXXFLAGS += -std=c++0x\n\nTEMPLATE = app\n\nSOURCES += main.cpp\n\nHEADERS += \\\n    http_conn.h \\\n    web_function.h \\\n    web_thread.h\n\n"
  },
  {
    "path": "Server/Codes/web_function.h",
    "content": "#ifndef WEB_FUNCTION\n#define WEB_FUNCTION\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <assert.h>\n#include <stdio.h>\n#include <unistd.h>\n#include <errno.h>\n#include <string.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <sys/epoll.h>\n#include <syslog.h>\n#include <signal.h>\n#include <sys/wait.h>\n#include <sys/stat.h>\n#include <pthread.h>\n#include <netdb.h>\n#include <atomic>\n#include<unordered_map>\n#include <sys/time.h>\n\n\nconst int thread_num=4;//线程数量\n#define LISTENQ 1024//listen()函数的参数\n#define MAX_EVENT_NUM 65535//epoll_event的事件数\n#define EVENT_TABLE_SIZE 4096//epoll_wait()的参数\n#define NEW_CONN '0'//发送给子线程的信号，表示有新连接到来\n#define CLOSE_DEAD_CONN '1'//发送给子线程的信号，表示关闭空闲的长连接\n#define CLOSE_THREAD '2'//发送给子线程的信号，表示结束子线程\n#define ALARM_TIME 30//定时时间，也是长连接的超时时间\n#define USER_PER_THREAD 500//每个子线程的最大用户数\n\n#define FILENAME_LEN  200//文件名称的最大长度\n#define READ_BUFFER_SIZE  256//给每个用户分配的读缓冲区大小\n#define WRITE_BUFFER_SIZE  256//给每个用户分配的写缓冲区大小\n\n\npthread_mutex_t mutex_conn;\n\n\nstatic std::atomic<int> user_count(0);//C++11的原子变量，统计所有线程的当前活跃用户数\nstatic std::atomic<int> max_user_online(0);\n\n\nvoid unix_error(char *msg) /* Unix-style error */\n{\n    fprintf(stderr,\"%s: %s\\n\", msg, strerror(errno));\n    exit(0);\n}\n\nvoid app_error(char *msg) /* Application error */\n{\n    fprintf(stderr, \"%s\\n\", msg);\n    exit(0);\n}\n\nvoid show_error(char *msg)\n{\n    fprintf(stderr, \"%s: %s\\n\", msg, strerror(errno));\n}\n\nvoid show_addr(sockaddr_in address)\n{\n    int save_errno=errno;\n    char host[NI_MAXHOST];\n    char service[NI_MAXSERV];\n    socklen_t addrlength = sizeof(address );\n\n    int ret=getnameinfo((struct sockaddr *)(&address), sizeof(struct sockaddr),host, sizeof(host), service, sizeof(service),NI_NUMERICHOST|NI_NUMERICSERV);\n    if(ret!=0)\n    {\n        show_error(\"address changed failed\");\n    }\n    else\n    {\n        printf(\"(%s: %s)\\n\",host,service);\n    }\n    errno=save_errno;\n\n}\n\nint Open(const char *pathname, int flags, mode_t mode)\n{\n    int rc;\n\n    if ((rc = open(pathname, flags, mode))  < 0)\n    unix_error(\"Open error\");\n    return rc;\n}\n\nssize_t Read(int fd, void *buf, size_t count)\n{\n    ssize_t rc;\n\n    if ((rc = read(fd, buf, count)) < 0)\n    unix_error(\"Read error\");\n    return rc;\n}\n\nssize_t Write(int fd, const void *buf, size_t count)\n{\n    ssize_t rc;\n\n    if ((rc = write(fd, buf, count)) < 0)\n    unix_error(\"Write error\");\n    return rc;\n}\n\n\nvoid Close(int fd)\n{\n    int rc;\n\n    if ((rc = close(fd)) < 0)\n    unix_error(\"Close error\");\n}\n\n\nstatic int setnonblocking( int fd )\n{\n    int old_option = fcntl( fd, F_GETFL );\n    int new_option = old_option | O_NONBLOCK;\n    fcntl( fd, F_SETFL, new_option );\n    return old_option;\n}\n\n\nstatic void removefd( int epollfd, int fd )//删除事件\n{\n    epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 );\n    Close( fd );\n}\n\nstatic void addfd( int epollfd, int fd )//添加事件\n{\n    epoll_event event;\n    event.data.fd = fd;\n    event.events = EPOLLIN | EPOLLET;\n    epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );\n    setnonblocking( fd );\n}\n\nvoid addfd( int epollfd, int fd, bool one_shot )//添加事件\n{\n    epoll_event event;\n    event.data.fd = fd;\n    event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;\n    if( one_shot )\n    {\n        event.events |= EPOLLONESHOT;\n    }\n    epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );\n    setnonblocking( fd );\n}\n\nvoid modfd( int epollfd, int fd, int ev )//修改事件\n{\n    epoll_event event;\n    event.data.fd = fd;\n    event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;\n    epoll_ctl( epollfd, EPOLL_CTL_MOD, fd, &event );\n}\n\nvoid show_sys_time()\n{\n\n    time_t timel;\n    time(&timel);\n    printf(\"time: %s\",asctime(gmtime(&timel)));\n}\n\nstatic void addsig( int sig, void( handler )(int), bool restart = true )\n{\n    struct sigaction sa;\n    memset( &sa, '\\0', sizeof( sa ) );\n    sa.sa_handler = handler;\n    if( restart )\n    {\n        sa.sa_flags |= SA_RESTART;\n    }\n    sigfillset( &sa.sa_mask );\n    assert( sigaction( sig, &sa, NULL ) != -1 );\n}\n\nint open_clientfd(char *ip, char *_port) {\n\n    int port=atoi(_port);\n\n    struct sockaddr_in address;\n    bzero( &address, sizeof( address ) );\n    address.sin_family = AF_INET;\n    inet_pton( AF_INET, ip, &address.sin_addr );\n    address.sin_port = htons( port );\n\n    int fd=socket(AF_INET,SOCK_STREAM,0);\n\n    int ret=connect(fd,(struct sockaddr*)&address,sizeof(address));\n    if(ret<0)\n    {\n        app_error(\"server connected failed\");\n    }\n    else\n    {\n        return fd;\n    }\n}\n\nint open_listenfd(char *ip, char *_port,int backlog)//仅ipv4\n{\n    int port=atoi(_port);\n\n    struct sockaddr_in address;\n    bzero( &address, sizeof( address ) );\n    address.sin_family = AF_INET;\n    inet_pton( AF_INET, ip, &address.sin_addr );\n    address.sin_port = htons( port );\n\n    int fd=socket(AF_INET,SOCK_STREAM,0);\n\n    int ret=bind(fd,(struct sockaddr*)&address,sizeof(address));\n    assert(ret!=-1);\n\n    ret=listen(fd,backlog);\n    if(ret<0)\n    {\n        unix_error(\"listenfd opened failed.\");\n    }\n    else\n    {\n        return fd;\n    }\n\n}\n\nint open_listenfd(char *port)//ipv4/ipv6通用\n{\n    struct addrinfo hints, *listp, *p;\n    int listenfd, rc, optval=1;\n\n    /* Get a list of potential server addresses */\n    memset(&hints, 0, sizeof(struct addrinfo));\n    hints.ai_socktype = SOCK_STREAM;             /* Accept connections */\n    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */\n    hints.ai_flags |= AI_NUMERICSERV;            /* ... using port number */\n    if ((rc = getaddrinfo(NULL, port, &hints, &listp)) != 0) {\n        fprintf(stderr, \"getaddrinfo failed (port %s): %s\\n\", port, gai_strerror(rc));\n        return -2;\n    }\n\n    /* Walk the list for one that we can bind to */\n    for (p = listp; p; p = p->ai_next) {\n        /* Create a socket descriptor */\n        if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)\n            continue;  /* Socket failed, try the next */\n\n        /* Eliminates \"Address already in use\" error from bind */\n        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,    //line:netp:csapp:setsockopt\n                   (const void *)&optval , sizeof(int));\n\n        /* Bind the descriptor to the address */\n        if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)\n            break; /* Success */\n        if (close(listenfd) < 0) { /* Bind failed, try the next */\n            fprintf(stderr, \"open_listenfd close failed: %s\\n\", strerror(errno));\n            return -1;\n        }\n    }\n\n\n    /* Clean up */\n    freeaddrinfo(listp);\n    if (!p) /* No address worked */\n        return -1;\n\n    /* Make it a listening socket ready to accept connection requests */\n    if (listen(listenfd, LISTENQ) < 0) {\n        close(listenfd);\n    return -1;\n    }\n    return listenfd;\n}\n\n\n#endif // WEB_FUNCTION\n\n"
  },
  {
    "path": "Server/Codes/web_thread.h",
    "content": "#ifndef WEB_THREAD_H\n#define WEB_THREAD_H\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <assert.h>\n#include <stdio.h>\n#include <unistd.h>\n#include <errno.h>\n#include <string.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <sys/epoll.h>\n#include <signal.h>\n#include <sys/wait.h>\n#include <sys/stat.h>\n#include<time.h>\n#include<queue>\n#include<map>\n#include<stack>\n#include<pthread.h>\n#include\"http_conn.h\"\n#include\"web_function.h\"\n\nusing namespace std;\n\nconst char* server_busy_info=\"HTTP/1.1 503 Service Unavailable\\r\\n\\r\\n\";//当子线程的用户数达到上限值时向新连接发送该信息\n\nstruct timer_cmp\n{\n    bool operator () (util_timer* a,util_timer* b)\n    {\n        return a->expire>b->expire;\n    }\n};\n\ntemplate<typename T>\nclass webthread\n{\npublic:\n    webthread() : user_num(0),stop(false)\n    {\n        epollfd = epoll_create(EVENT_TABLE_SIZE);\n        assert( epollfd != -1 );\n\n        int ret = socketpair( PF_UNIX, SOCK_STREAM, 0, pipefd);\n        assert( ret != -1 );\n\n        setnonblocking( pipefd[1] );\n        addfd( epollfd, pipefd[0] );\n        users=new T[USER_PER_THREAD];//提前分配用户空间\n    }\n    ~webthread()\n    {\n        Close(pipefd[1]);\n        Close(pipefd[0]);\n        Close(epollfd);\n    }\n\npublic:\n    pthread_t tid;\n    int pipefd[2];//通过该管道实现子线程与主线程的双向通信\n    static int listenfd;//主线程中的监听描述符\n    static int m_epollfd;//主线程的epollfd，在工作线程中访问该epollfd来重置listenfd的事件\n\n    void work();\nprivate:\n    int user_num;//当前在线客户端数量\n    int epollfd;//工作线程的epollfd\n    T* users;//客户端连接的工作空间\n    bool stop;//是否退出线程主循环\n\n};\ntemplate<typename T>\nint webthread<T>::listenfd=-1;\n\ntemplate<typename T>\nint webthread<T>::m_epollfd=-1;\n\ntemplate<typename T>\nvoid webthread<T>::work()\n{\n    epoll_event events[MAX_EVENT_NUM];\n    stack<int> index_free;//初始化空闲队列\n    for(int i=USER_PER_THREAD-1;i>=0;i--)\n    {\n        index_free.push(i);\n    }\n    unordered_map<int,int> mp;//用户索引与连接符的匹配\n    priority_queue<util_timer*,vector<util_timer*>,timer_cmp> timer_queue;//定时器容器\n    bool timeshot=false;//定时事件\n\n\n    while(!stop)\n    {\n        int number=epoll_wait(epollfd,events,MAX_EVENT_NUM,-1);\n        if(number<0 && errno!=EINTR)\n        {\n            unix_error(\"epoll failed.\");\n            break;\n        }\n        for(int i=0;i<number;i++)\n        {\n            int sockfd=events[i].data.fd;\n            if(sockfd==pipefd[0] && ( events[i].events & EPOLLIN ))//如果接收到主线程的消息\n            {\n                char infos[1024];\n                int ret=recv(sockfd,infos,sizeof(infos),0);\n                if( ( ( ret < 0 ) && ( errno != EAGAIN ) ) || ret == 0 )\n                {\n                    continue;\n                }\n                else\n                {   for(int j=0;j<ret;j++)\n                    {\n                        switch (infos[j])\n                        {\n                            case NEW_CONN://处理新到来的连接\n                            {\n                                struct sockaddr_in client_address;\n                                socklen_t client_addrlength = sizeof( client_address );\n                                int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );\n                                int conn_count=0;\n                                while(connfd>0)\n                                {\n                                    conn_count++;\n                                    if(!index_free.empty())//如果还没有达到本线程的最大用户数，则给新用户分配用户空间\n                                    {\n                                        int user_index=index_free.top();\n                                        index_free.pop();\n                                        mp[connfd]=user_index;\n                                        util_timer* _timer=new util_timer(time(NULL)+ALARM_TIME);//给每个用户分配一个定时器\n                                        _timer->client=&users[user_index];//将定时器与用户地址关联起来，方便后面通过定时器关闭非活动连接\n                                        timer_queue.push(_timer);//将定时器放入队列容器中\n                                        users[user_index].init( epollfd, connfd, client_address,_timer,user_index,&index_free);//初始化用户空间\n                                    }\n                                    else//如果用户已满，向新连接发送服务器繁忙消息\n                                    {\n                                        ret=send(connfd,server_busy_info,strlen(server_busy_info),0);\n                                        if(ret<=0)\n                                        {\n                                            printf(\"busy_info send failed.\\n\");\n                                        }\n                                        Close(connfd);\n                                    }\n                                    memset((char*)&client_address,0,client_addrlength);\n                                    connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );\n                                }\n                                modfd(m_epollfd, listenfd, EPOLLIN);//重置listenfd的读事件\n                                if(conn_count==0)\n                                {\n                                    printf(\"user_num_now: %d\\n\",user_num);\n                                    show_error(\"accept connection failed\");\n                                    continue;\n                                }\n                                break;\n                            }\n                            case CLOSE_DEAD_CONN:\n                            {\n                                timeshot=true;//定时事件优先级最低\n                                break;\n                            }\n                            case CLOSE_THREAD:\n                            {\n                                printf(\"thread:%ld get close info\\n\",tid);\n                                stop=true;//退出线程主循环\n                                break;\n                            }\n                            default:\n                                break;\n                        }\n                    }\n                }\n            }\n            else if( events[i].events & ( EPOLLRDHUP | EPOLLHUP | EPOLLERR ) )//如果有连接描述符出现错误\n            {\n                int user_index=mp[sockfd];\n                users[user_index].close_conn();\n            }\n            else if( events[i].events & EPOLLIN )//如果有连接描述符可读\n            {\n                int user_index=mp[sockfd];\n                if( users[user_index].read() )//读取连接描述符的消息到用户的读缓冲区\n                {\n                    users[user_index].process();//解析用户读缓冲区的http请求，解析后往用户的写缓冲区写http应答，并将该连接描述符的epoll注册事件改为写事件\n                }\n                else\n                {\n                    users[user_index].close_conn();//如果失败则关闭对应描述符，并释放被原用户占用的空间\n                }\n            }\n            else if( events[i].events & EPOLLOUT )//如果有连接描述符可写\n            {\n                int user_index=mp[sockfd];\n                if( !users[user_index].write() )//如果写失败或者是短连接则立即关闭连接\n                {\n                    users[user_index].close_conn();\n                }\n                else//如果是长连接则延长定时器的定时时间，并将该连接描述符的epoll注册事件改为读事件\n                {\n                    util_timer* timer_temp=users[user_index].timer;\n                    timer_temp->expire=time(NULL)+ALARM_TIME;\n                }\n            }\n            else\n            {}\n            if(timeshot)//定时时间到，回收非活动连接\n            {\n                while(!timer_queue.empty())\n                {\n                    util_timer* timer_temp=timer_queue.top();\n                    timer_queue.pop();\n                    timer_queue.push(timer_temp);\n                    timer_temp=timer_queue.top();//由于指针指向的元素值发生改变，需要重排元素\n                    time_t t=time(NULL);\n                    if(timer_temp->free)//如果定时器关联的连接已关闭，则释放定时器占用的空间\n                    {\n                        timer_queue.pop();\n                        delete timer_temp;\n                    }\n                    else if(timer_temp->expire<=t)//关闭定时器关联的连接并释放空间\n                    {\n                        timer_temp->timed_event();\n                        timer_queue.pop();\n                        delete timer_temp;\n                    }\n                    else//剩下的连接还没到超时时间\n                    {\n                        timeshot=false;\n                        break;\n                    }\n                }\n                timeshot=false;\n            }\n        }\n    }\n\n    //退出线程前先回收堆空间以避免内存泄漏\n    while (!timer_queue.empty())\n    {\n        util_timer* timer_temp=timer_queue.top();\n        timer_queue.pop();\n        if(!timer_temp->free)\n        {\n            timer_temp->timed_event();\n        }\n        delete timer_temp;\n    }\n    delete [] users;\n    printf(\"closed thread: %ld\\n\",tid);\n\n}\n\n#endif // WEB_THREAD_H\n\n"
  },
  {
    "path": "Server/Debug/Makefile",
    "content": "#############################################################################\n# Makefile for building: server\n# Generated by qmake (2.01a) (Qt 4.8.7) on: ?? 3? 28 13:48:52 2020\n# Project:  ../Codes/server.pro\n# Template: app\n# Command: /usr/lib/x86_64-linux-gnu/qt4/bin/qmake -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/server.pro\n#############################################################################\n\n####### Compiler, tools and options\n\nCC            = gcc\nCXX           = g++\nDEFINES       = -DQT_CORE_LIB -DQT_SHARED\nCFLAGS        = -m64 -pipe -g -Wall -W -D_REENTRANT $(DEFINES)\nCXXFLAGS      = -m64 -pipe -std=c++0x -g -Wall -W -D_REENTRANT $(DEFINES)\nINCPATH       = -I/usr/share/qt4/mkspecs/linux-g++-64 -I../Codes -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. -I../Codes -I.\nLINK          = g++\nLFLAGS        = -m64\nLIBS          = $(SUBLIBS)  -L/usr/lib/x86_64-linux-gnu -lQtCore -lpthread \nAR            = ar cqs\nRANLIB        = \nQMAKE         = /usr/lib/x86_64-linux-gnu/qt4/bin/qmake\nTAR           = tar -cf\nCOMPRESS      = gzip -9f\nCOPY          = cp -f\nSED           = sed\nCOPY_FILE     = $(COPY)\nCOPY_DIR      = $(COPY) -r\nSTRIP         = strip\nINSTALL_FILE  = install -m 644 -p\nINSTALL_DIR   = $(COPY_DIR)\nINSTALL_PROGRAM = install -m 755 -p\nDEL_FILE      = rm -f\nSYMLINK       = ln -f -s\nDEL_DIR       = rmdir\nMOVE          = mv -f\nCHK_DIR_EXISTS= test -d\nMKDIR         = mkdir -p\n\n####### Output directory\n\nOBJECTS_DIR   = ./\n\n####### Files\n\nSOURCES       = ../Codes/main.cpp \nOBJECTS       = main.o\nDIST          = /usr/share/qt4/mkspecs/common/unix.conf \\\n\t\t/usr/share/qt4/mkspecs/common/linux.conf \\\n\t\t/usr/share/qt4/mkspecs/common/gcc-base.conf \\\n\t\t/usr/share/qt4/mkspecs/common/gcc-base-unix.conf \\\n\t\t/usr/share/qt4/mkspecs/common/g++-base.conf \\\n\t\t/usr/share/qt4/mkspecs/common/g++-unix.conf \\\n\t\t/usr/share/qt4/mkspecs/qconfig.pri \\\n\t\t/usr/share/qt4/mkspecs/features/qt_functions.prf \\\n\t\t/usr/share/qt4/mkspecs/features/qt_config.prf \\\n\t\t/usr/share/qt4/mkspecs/features/exclusive_builds.prf \\\n\t\t/usr/share/qt4/mkspecs/features/default_pre.prf \\\n\t\t/usr/share/qt4/mkspecs/features/debug.prf \\\n\t\t/usr/share/qt4/mkspecs/features/default_post.prf \\\n\t\t/usr/share/qt4/mkspecs/features/shared.prf \\\n\t\t/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \\\n\t\t/usr/share/qt4/mkspecs/features/warn_on.prf \\\n\t\t/usr/share/qt4/mkspecs/features/qt.prf \\\n\t\t/usr/share/qt4/mkspecs/features/unix/thread.prf \\\n\t\t/usr/share/qt4/mkspecs/features/moc.prf \\\n\t\t/usr/share/qt4/mkspecs/features/resources.prf \\\n\t\t/usr/share/qt4/mkspecs/features/uic.prf \\\n\t\t/usr/share/qt4/mkspecs/features/yacc.prf \\\n\t\t/usr/share/qt4/mkspecs/features/lex.prf \\\n\t\t/usr/share/qt4/mkspecs/features/include_source_dir.prf \\\n\t\t../Codes/server.pro\nQMAKE_TARGET  = server\nDESTDIR       = \nTARGET        = server\n\nfirst: all\n####### Implicit rules\n\n.SUFFIXES: .o .c .cpp .cc .cxx .C\n\n.cpp.o:\n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n.cc.o:\n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n.cxx.o:\n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n.C.o:\n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n.c.o:\n\t$(CC) -c $(CFLAGS) $(INCPATH) -o \"$@\" \"$<\"\n\n####### Build rules\n\nall: Makefile $(TARGET)\n\n$(TARGET):  $(OBJECTS)  \n\t$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)\n\t{ test -n \"$(DESTDIR)\" && DESTDIR=\"$(DESTDIR)\" || DESTDIR=.; } && test $$(gdb --version | sed -e 's,[^0-9][^0-9]*\\([0-9]\\)\\.\\([0-9]\\).*,\\1\\2,;q') -gt 72 && gdb --nx --batch --quiet -ex 'set confirm off' -ex \"save gdb-index $$DESTDIR\" -ex quit '$(TARGET)' && test -f $(TARGET).gdb-index && objcopy --add-section '.gdb_index=$(TARGET).gdb-index' --set-section-flags '.gdb_index=readonly' '$(TARGET)' '$(TARGET)' && rm -f $(TARGET).gdb-index || true\n\nMakefile: ../Codes/server.pro  /usr/share/qt4/mkspecs/linux-g++-64/qmake.conf /usr/share/qt4/mkspecs/common/unix.conf \\\n\t\t/usr/share/qt4/mkspecs/common/linux.conf \\\n\t\t/usr/share/qt4/mkspecs/common/gcc-base.conf \\\n\t\t/usr/share/qt4/mkspecs/common/gcc-base-unix.conf \\\n\t\t/usr/share/qt4/mkspecs/common/g++-base.conf \\\n\t\t/usr/share/qt4/mkspecs/common/g++-unix.conf \\\n\t\t/usr/share/qt4/mkspecs/qconfig.pri \\\n\t\t/usr/share/qt4/mkspecs/features/qt_functions.prf \\\n\t\t/usr/share/qt4/mkspecs/features/qt_config.prf \\\n\t\t/usr/share/qt4/mkspecs/features/exclusive_builds.prf \\\n\t\t/usr/share/qt4/mkspecs/features/default_pre.prf \\\n\t\t/usr/share/qt4/mkspecs/features/debug.prf \\\n\t\t/usr/share/qt4/mkspecs/features/default_post.prf \\\n\t\t/usr/share/qt4/mkspecs/features/shared.prf \\\n\t\t/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \\\n\t\t/usr/share/qt4/mkspecs/features/warn_on.prf \\\n\t\t/usr/share/qt4/mkspecs/features/qt.prf \\\n\t\t/usr/share/qt4/mkspecs/features/unix/thread.prf \\\n\t\t/usr/share/qt4/mkspecs/features/moc.prf \\\n\t\t/usr/share/qt4/mkspecs/features/resources.prf \\\n\t\t/usr/share/qt4/mkspecs/features/uic.prf \\\n\t\t/usr/share/qt4/mkspecs/features/yacc.prf \\\n\t\t/usr/share/qt4/mkspecs/features/lex.prf \\\n\t\t/usr/share/qt4/mkspecs/features/include_source_dir.prf \\\n\t\t/usr/lib/x86_64-linux-gnu/libQtCore.prl\n\t$(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/server.pro\n/usr/share/qt4/mkspecs/common/unix.conf:\n/usr/share/qt4/mkspecs/common/linux.conf:\n/usr/share/qt4/mkspecs/common/gcc-base.conf:\n/usr/share/qt4/mkspecs/common/gcc-base-unix.conf:\n/usr/share/qt4/mkspecs/common/g++-base.conf:\n/usr/share/qt4/mkspecs/common/g++-unix.conf:\n/usr/share/qt4/mkspecs/qconfig.pri:\n/usr/share/qt4/mkspecs/features/qt_functions.prf:\n/usr/share/qt4/mkspecs/features/qt_config.prf:\n/usr/share/qt4/mkspecs/features/exclusive_builds.prf:\n/usr/share/qt4/mkspecs/features/default_pre.prf:\n/usr/share/qt4/mkspecs/features/debug.prf:\n/usr/share/qt4/mkspecs/features/default_post.prf:\n/usr/share/qt4/mkspecs/features/shared.prf:\n/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf:\n/usr/share/qt4/mkspecs/features/warn_on.prf:\n/usr/share/qt4/mkspecs/features/qt.prf:\n/usr/share/qt4/mkspecs/features/unix/thread.prf:\n/usr/share/qt4/mkspecs/features/moc.prf:\n/usr/share/qt4/mkspecs/features/resources.prf:\n/usr/share/qt4/mkspecs/features/uic.prf:\n/usr/share/qt4/mkspecs/features/yacc.prf:\n/usr/share/qt4/mkspecs/features/lex.prf:\n/usr/share/qt4/mkspecs/features/include_source_dir.prf:\n/usr/lib/x86_64-linux-gnu/libQtCore.prl:\nqmake:  FORCE\n\t@$(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/server.pro\n\ndist: \n\t@$(CHK_DIR_EXISTS) .tmp/server1.0.0 || $(MKDIR) .tmp/server1.0.0 \n\t$(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/server1.0.0/ && $(COPY_FILE) --parents ../Codes/http_conn.h ../Codes/web_function.h ../Codes/web_thread.h .tmp/server1.0.0/ && $(COPY_FILE) --parents ../Codes/main.cpp .tmp/server1.0.0/ && (cd `dirname .tmp/server1.0.0` && $(TAR) server1.0.0.tar server1.0.0 && $(COMPRESS) server1.0.0.tar) && $(MOVE) `dirname .tmp/server1.0.0`/server1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/server1.0.0\n\n\nclean:compiler_clean \n\t-$(DEL_FILE) $(OBJECTS)\n\t-$(DEL_FILE) *~ core *.core\n\n\n####### Sub-libraries\n\ndistclean: clean\n\t-$(DEL_FILE) $(TARGET) \n\t-$(DEL_FILE) Makefile\n\n\ncheck: first\n\nmocclean: compiler_moc_header_clean compiler_moc_source_clean\n\nmocables: compiler_moc_header_make_all compiler_moc_source_make_all\n\ncompiler_moc_header_make_all:\ncompiler_moc_header_clean:\ncompiler_rcc_make_all:\ncompiler_rcc_clean:\ncompiler_image_collection_make_all: qmake_image_collection.cpp\ncompiler_image_collection_clean:\n\t-$(DEL_FILE) qmake_image_collection.cpp\ncompiler_moc_source_make_all:\ncompiler_moc_source_clean:\ncompiler_uic_make_all:\ncompiler_uic_clean:\ncompiler_yacc_decl_make_all:\ncompiler_yacc_decl_clean:\ncompiler_yacc_impl_make_all:\ncompiler_yacc_impl_clean:\ncompiler_lex_make_all:\ncompiler_lex_clean:\ncompiler_clean: \n\n####### Compile\n\nmain.o: ../Codes/main.cpp ../Codes/web_thread.h \\\n\t\t../Codes/http_conn.h \\\n\t\t../Codes/web_function.h\n\t$(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o ../Codes/main.cpp\n\n####### Install\n\ninstall:   FORCE\n\nuninstall:   FORCE\n\nFORCE:\n\n"
  },
  {
    "path": "Server/Debug/html/index.html",
    "content": "<html>\n<head>\n<title>test</title>\n</head>\n<body>\n    <p>test</p>\n</body>\n</html>\n"
  }
]