├── Client ├── Codes │ ├── README.md │ ├── client.pro │ └── main.cpp └── Debug │ ├── Makefile │ ├── client │ └── main.o ├── README.md ├── Server ├── Codes │ ├── README.md │ ├── http_conn.h │ ├── main.cpp │ ├── server.pro │ ├── web_function.h │ └── web_thread.h └── Debug │ ├── Makefile │ ├── cgi-bin │ └── adder │ ├── html │ └── index.html │ ├── main.o │ └── server ├── runClient.png └── runServer.png /Client/Codes/README.md: -------------------------------------------------------------------------------- 1 | 一个针对服务器的压力测试程序,可自定义请求客户的数目 2 | -------------------------------------------------------------------------------- /Client/Codes/client.pro: -------------------------------------------------------------------------------- 1 | QT += core 2 | QT -= gui 3 | 4 | TARGET = client 5 | CONFIG += console 6 | QMAKE_CXXFLAGS += -std=c++11 7 | CONFIG -= app_bundle 8 | 9 | TEMPLATE = app 10 | 11 | SOURCES += main.cpp 12 | 13 | -------------------------------------------------------------------------------- /Client/Codes/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static const char* request = "GET http://localhost/html/index.html HTTP/1.1\r\nConnection: keep-alive\r\n\r\nxxxxxxxxxxxx"; 14 | 15 | int setnonblocking( int fd ) 16 | { 17 | int old_option = fcntl( fd, F_GETFL ); 18 | int new_option = old_option | O_NONBLOCK; 19 | fcntl( fd, F_SETFL, new_option ); 20 | return old_option; 21 | } 22 | 23 | void addfd( int epoll_fd, int fd ) 24 | { 25 | epoll_event event; 26 | event.data.fd = fd; 27 | event.events = EPOLLOUT | EPOLLET | EPOLLERR; 28 | epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event ); 29 | setnonblocking( fd ); 30 | } 31 | 32 | bool write_nbytes( int sockfd, const char* buffer, int len ) 33 | { 34 | int bytes_write = 0; 35 | printf( "write out %d bytes to socket %d\n", len, sockfd ); 36 | while( 1 ) 37 | { 38 | bytes_write = send( sockfd, buffer, len, 0 ); 39 | if ( bytes_write == -1 ) 40 | { 41 | return false; 42 | } 43 | else if ( bytes_write == 0 ) 44 | { 45 | return false; 46 | } 47 | 48 | len -= bytes_write; 49 | buffer = buffer + bytes_write; 50 | if ( len <= 0 ) 51 | { 52 | return true; 53 | } 54 | } 55 | } 56 | 57 | bool read_once( int sockfd, char* buffer, int len ) 58 | { 59 | int bytes_read = 0; 60 | memset( buffer, '\0', len ); 61 | bytes_read = recv( sockfd, buffer, len, 0 ); 62 | if ( bytes_read == -1 ) 63 | { 64 | return false; 65 | } 66 | else if ( bytes_read == 0 ) 67 | { 68 | return false; 69 | } 70 | printf( "read in %d bytes from socket %d with content: %s\n", bytes_read, sockfd, buffer ); 71 | 72 | return true; 73 | } 74 | 75 | void start_conn( int epoll_fd, int num, const char* ip, int port ) 76 | { 77 | int ret = 0; 78 | struct sockaddr_in address; 79 | bzero( &address, sizeof( address ) ); 80 | address.sin_family = AF_INET; 81 | inet_pton( AF_INET, ip, &address.sin_addr ); 82 | address.sin_port = htons( port ); 83 | 84 | for ( int i = 0; i < num; ++i ) 85 | { 86 | sleep( 0.01 ); 87 | int sockfd = socket( PF_INET, SOCK_STREAM, 0 ); 88 | printf( "create 1 sock\n" ); 89 | if( sockfd < 0 ) 90 | { 91 | continue; 92 | } 93 | 94 | if ( connect( sockfd, ( struct sockaddr* )&address, sizeof( address ) ) == 0 ) 95 | { 96 | printf( "build connection %d\n", i ); 97 | addfd( epoll_fd, sockfd ); 98 | } 99 | } 100 | } 101 | 102 | void close_conn( int epoll_fd, int sockfd ) 103 | { 104 | epoll_ctl( epoll_fd, EPOLL_CTL_DEL, sockfd, 0 ); 105 | close( sockfd ); 106 | } 107 | 108 | int main( int argc, char* argv[] ) 109 | { 110 | if(argc<4) 111 | { 112 | fprintf(stderr,"usage: %s server_ip server_port client_num ",argv[0]); 113 | exit(0); 114 | } 115 | int epoll_fd = epoll_create( 100 ); 116 | start_conn( epoll_fd, atoi( argv[ 3 ] ), argv[1], atoi( argv[2] ) ); 117 | epoll_event events[ 10000 ]; 118 | char buffer[ 2048 ]; 119 | while ( 1 ) 120 | { 121 | int fds = epoll_wait( epoll_fd, events, 10000, 2000 ); 122 | for ( int i = 0; i < fds; i++ ) 123 | { 124 | int sockfd = events[i].data.fd; 125 | if ( events[i].events & EPOLLIN ) 126 | { 127 | if ( ! read_once( sockfd, buffer, 2048 ) ) 128 | { 129 | close_conn( epoll_fd, sockfd ); 130 | } 131 | struct epoll_event event; 132 | event.events = EPOLLOUT | EPOLLET | EPOLLERR; 133 | event.data.fd = sockfd; 134 | epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event ); 135 | } 136 | else if( events[i].events & EPOLLOUT ) 137 | { 138 | if ( ! write_nbytes( sockfd, request, strlen( request ) ) ) 139 | { 140 | close_conn( epoll_fd, sockfd ); 141 | } 142 | struct epoll_event event; 143 | event.events = EPOLLIN | EPOLLET | EPOLLERR; 144 | event.data.fd = sockfd; 145 | epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event ); 146 | } 147 | else if( events[i].events & EPOLLERR ) 148 | { 149 | close_conn( epoll_fd, sockfd ); 150 | } 151 | } 152 | } 153 | } 154 | 155 | -------------------------------------------------------------------------------- /Client/Debug/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # Makefile for building: client 3 | # Generated by qmake (2.01a) (Qt 4.8.7) on: ?? 2? 27 15:14:57 2020 4 | # Project: ../Codes/client.pro 5 | # Template: app 6 | # 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 7 | ############################################################################# 8 | 9 | ####### Compiler, tools and options 10 | 11 | CC = gcc 12 | CXX = g++ 13 | DEFINES = -DQT_CORE_LIB -DQT_SHARED 14 | CFLAGS = -m64 -pipe -g -Wall -W -D_REENTRANT $(DEFINES) 15 | CXXFLAGS = -m64 -pipe -std=c++11 -g -Wall -W -D_REENTRANT $(DEFINES) 16 | INCPATH = -I/usr/share/qt4/mkspecs/linux-g++-64 -I../Codes -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. -I../Codes -I. 17 | LINK = g++ 18 | LFLAGS = -m64 19 | LIBS = $(SUBLIBS) -L/usr/lib/x86_64-linux-gnu -lQtCore -lpthread 20 | AR = ar cqs 21 | RANLIB = 22 | QMAKE = /usr/lib/x86_64-linux-gnu/qt4/bin/qmake 23 | TAR = tar -cf 24 | COMPRESS = gzip -9f 25 | COPY = cp -f 26 | SED = sed 27 | COPY_FILE = $(COPY) 28 | COPY_DIR = $(COPY) -r 29 | STRIP = strip 30 | INSTALL_FILE = install -m 644 -p 31 | INSTALL_DIR = $(COPY_DIR) 32 | INSTALL_PROGRAM = install -m 755 -p 33 | DEL_FILE = rm -f 34 | SYMLINK = ln -f -s 35 | DEL_DIR = rmdir 36 | MOVE = mv -f 37 | CHK_DIR_EXISTS= test -d 38 | MKDIR = mkdir -p 39 | 40 | ####### Output directory 41 | 42 | OBJECTS_DIR = ./ 43 | 44 | ####### Files 45 | 46 | SOURCES = ../Codes/main.cpp 47 | OBJECTS = main.o 48 | DIST = /usr/share/qt4/mkspecs/common/unix.conf \ 49 | /usr/share/qt4/mkspecs/common/linux.conf \ 50 | /usr/share/qt4/mkspecs/common/gcc-base.conf \ 51 | /usr/share/qt4/mkspecs/common/gcc-base-unix.conf \ 52 | /usr/share/qt4/mkspecs/common/g++-base.conf \ 53 | /usr/share/qt4/mkspecs/common/g++-unix.conf \ 54 | /usr/share/qt4/mkspecs/qconfig.pri \ 55 | /usr/share/qt4/mkspecs/features/qt_functions.prf \ 56 | /usr/share/qt4/mkspecs/features/qt_config.prf \ 57 | /usr/share/qt4/mkspecs/features/exclusive_builds.prf \ 58 | /usr/share/qt4/mkspecs/features/default_pre.prf \ 59 | /usr/share/qt4/mkspecs/features/debug.prf \ 60 | /usr/share/qt4/mkspecs/features/default_post.prf \ 61 | /usr/share/qt4/mkspecs/features/shared.prf \ 62 | /usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \ 63 | /usr/share/qt4/mkspecs/features/warn_on.prf \ 64 | /usr/share/qt4/mkspecs/features/qt.prf \ 65 | /usr/share/qt4/mkspecs/features/unix/thread.prf \ 66 | /usr/share/qt4/mkspecs/features/moc.prf \ 67 | /usr/share/qt4/mkspecs/features/resources.prf \ 68 | /usr/share/qt4/mkspecs/features/uic.prf \ 69 | /usr/share/qt4/mkspecs/features/yacc.prf \ 70 | /usr/share/qt4/mkspecs/features/lex.prf \ 71 | /usr/share/qt4/mkspecs/features/include_source_dir.prf \ 72 | ../Codes/client.pro 73 | QMAKE_TARGET = client 74 | DESTDIR = 75 | TARGET = client 76 | 77 | first: all 78 | ####### Implicit rules 79 | 80 | .SUFFIXES: .o .c .cpp .cc .cxx .C 81 | 82 | .cpp.o: 83 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" 84 | 85 | .cc.o: 86 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" 87 | 88 | .cxx.o: 89 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" 90 | 91 | .C.o: 92 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" 93 | 94 | .c.o: 95 | $(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<" 96 | 97 | ####### Build rules 98 | 99 | all: Makefile $(TARGET) 100 | 101 | $(TARGET): $(OBJECTS) 102 | $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS) 103 | { 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 104 | 105 | Makefile: ../Codes/client.pro /usr/share/qt4/mkspecs/linux-g++-64/qmake.conf /usr/share/qt4/mkspecs/common/unix.conf \ 106 | /usr/share/qt4/mkspecs/common/linux.conf \ 107 | /usr/share/qt4/mkspecs/common/gcc-base.conf \ 108 | /usr/share/qt4/mkspecs/common/gcc-base-unix.conf \ 109 | /usr/share/qt4/mkspecs/common/g++-base.conf \ 110 | /usr/share/qt4/mkspecs/common/g++-unix.conf \ 111 | /usr/share/qt4/mkspecs/qconfig.pri \ 112 | /usr/share/qt4/mkspecs/features/qt_functions.prf \ 113 | /usr/share/qt4/mkspecs/features/qt_config.prf \ 114 | /usr/share/qt4/mkspecs/features/exclusive_builds.prf \ 115 | /usr/share/qt4/mkspecs/features/default_pre.prf \ 116 | /usr/share/qt4/mkspecs/features/debug.prf \ 117 | /usr/share/qt4/mkspecs/features/default_post.prf \ 118 | /usr/share/qt4/mkspecs/features/shared.prf \ 119 | /usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \ 120 | /usr/share/qt4/mkspecs/features/warn_on.prf \ 121 | /usr/share/qt4/mkspecs/features/qt.prf \ 122 | /usr/share/qt4/mkspecs/features/unix/thread.prf \ 123 | /usr/share/qt4/mkspecs/features/moc.prf \ 124 | /usr/share/qt4/mkspecs/features/resources.prf \ 125 | /usr/share/qt4/mkspecs/features/uic.prf \ 126 | /usr/share/qt4/mkspecs/features/yacc.prf \ 127 | /usr/share/qt4/mkspecs/features/lex.prf \ 128 | /usr/share/qt4/mkspecs/features/include_source_dir.prf \ 129 | /usr/lib/x86_64-linux-gnu/libQtCore.prl 130 | $(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/client.pro 131 | /usr/share/qt4/mkspecs/common/unix.conf: 132 | /usr/share/qt4/mkspecs/common/linux.conf: 133 | /usr/share/qt4/mkspecs/common/gcc-base.conf: 134 | /usr/share/qt4/mkspecs/common/gcc-base-unix.conf: 135 | /usr/share/qt4/mkspecs/common/g++-base.conf: 136 | /usr/share/qt4/mkspecs/common/g++-unix.conf: 137 | /usr/share/qt4/mkspecs/qconfig.pri: 138 | /usr/share/qt4/mkspecs/features/qt_functions.prf: 139 | /usr/share/qt4/mkspecs/features/qt_config.prf: 140 | /usr/share/qt4/mkspecs/features/exclusive_builds.prf: 141 | /usr/share/qt4/mkspecs/features/default_pre.prf: 142 | /usr/share/qt4/mkspecs/features/debug.prf: 143 | /usr/share/qt4/mkspecs/features/default_post.prf: 144 | /usr/share/qt4/mkspecs/features/shared.prf: 145 | /usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf: 146 | /usr/share/qt4/mkspecs/features/warn_on.prf: 147 | /usr/share/qt4/mkspecs/features/qt.prf: 148 | /usr/share/qt4/mkspecs/features/unix/thread.prf: 149 | /usr/share/qt4/mkspecs/features/moc.prf: 150 | /usr/share/qt4/mkspecs/features/resources.prf: 151 | /usr/share/qt4/mkspecs/features/uic.prf: 152 | /usr/share/qt4/mkspecs/features/yacc.prf: 153 | /usr/share/qt4/mkspecs/features/lex.prf: 154 | /usr/share/qt4/mkspecs/features/include_source_dir.prf: 155 | /usr/lib/x86_64-linux-gnu/libQtCore.prl: 156 | qmake: FORCE 157 | @$(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/client.pro 158 | 159 | dist: 160 | @$(CHK_DIR_EXISTS) .tmp/client1.0.0 || $(MKDIR) .tmp/client1.0.0 161 | $(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 162 | 163 | 164 | clean:compiler_clean 165 | -$(DEL_FILE) $(OBJECTS) 166 | -$(DEL_FILE) *~ core *.core 167 | 168 | 169 | ####### Sub-libraries 170 | 171 | distclean: clean 172 | -$(DEL_FILE) $(TARGET) 173 | -$(DEL_FILE) Makefile 174 | 175 | 176 | check: first 177 | 178 | mocclean: compiler_moc_header_clean compiler_moc_source_clean 179 | 180 | mocables: compiler_moc_header_make_all compiler_moc_source_make_all 181 | 182 | compiler_moc_header_make_all: 183 | compiler_moc_header_clean: 184 | compiler_rcc_make_all: 185 | compiler_rcc_clean: 186 | compiler_image_collection_make_all: qmake_image_collection.cpp 187 | compiler_image_collection_clean: 188 | -$(DEL_FILE) qmake_image_collection.cpp 189 | compiler_moc_source_make_all: 190 | compiler_moc_source_clean: 191 | compiler_uic_make_all: 192 | compiler_uic_clean: 193 | compiler_yacc_decl_make_all: 194 | compiler_yacc_decl_clean: 195 | compiler_yacc_impl_make_all: 196 | compiler_yacc_impl_clean: 197 | compiler_lex_make_all: 198 | compiler_lex_clean: 199 | compiler_clean: 200 | 201 | ####### Compile 202 | 203 | main.o: ../Codes/main.cpp 204 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o ../Codes/main.cpp 205 | 206 | ####### Install 207 | 208 | install: FORCE 209 | 210 | uninstall: FORCE 211 | 212 | FORCE: 213 | 214 | -------------------------------------------------------------------------------- /Client/Debug/client: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Buerzhu/TinyWeb/62e7559a1ebd887c4ff748c015118a90f019b884/Client/Debug/client -------------------------------------------------------------------------------- /Client/Debug/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Buerzhu/TinyWeb/62e7559a1ebd887c4ff748c015118a90f019b884/Client/Debug/main.o -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | * **前言** 2 | 3 | 本项目是基于C++线程池的轻量级Web并发服务器,事件处理模式采用Reactor模式,主线程只负责监听文件描述符是否有事件发生,读写数据、接收新的连接、以及处理客户请求均在工作线程中实现;使用半同步/半异步模式,每个线程(主线程和工作线程)都通过一个epoll维护自己的事件循环,它们各自独立地监听不同事件。使用C++ 11的atomic原子变量来同步线程访问从而避免同步错误;使用STL的优先队列作为定时器容器来回收非活动长连接。本项目的具体设计思路可以参照本人的知乎文章[C++网络编程入门:轻量级Web并发服务器开发](https://zhuanlan.zhihu.com/p/109905285),纯属抛砖引玉,欢迎大佬们批评指正。 4 | 5 | * **运行环境** 6 | 7 | (1).系统:Ubuntu16.04; 8 | 9 | (2).语言:C++ 11及以上版本; 10 | 11 | * **支持功能** 12 | 13 | (1).支持Get请求; 14 | (2).支持长连接/短连接; 15 | (3).支持ipv4/ipv6;(4).支持tcp;(5).支持请求静态内容(6).支持并发请求 16 | 17 | * **开始运行** 18 | 19 | (1).下载程序; 20 | 21 | `git clone git@github.com:Buerzhu/TinyWeb.git` 22 | 23 | (2).打开新终端,指定ip地址和端口运行服务器程序: 24 | 25 | `cd ~/TinyWeb/Server/Debug` 26 | 27 | `./server 127.0.0.1 12345` 28 | 29 | (3).打开新终端,指定ip地址、端口、客户连接数运行压力测试程序: 30 | 31 | `cd ~/TinyWeb/Client/Debug` 32 | 33 | `./client 127.0.0.1 12345 1000` 34 | -------------------------------------------------------------------------------- /Server/Codes/README.md: -------------------------------------------------------------------------------- 1 | * **web_thread.h** 2 | 3 | (1). 定义了webthread类; 4 | 5 | (2). 该类是主线程与子线程通信的媒介,本程序对于每个子线程建立对应的wedthread全局对象,主线程通过该对象来与子线程通信,子线程通过该对象接收来自主线程的消息,并运行该对象的work()函数来处理主线程消息和用户的http请求。 6 | 7 | * **http_conn.h** 8 | 9 | (1).定义了http_conn类和util_timer类; 10 | 11 | (2).http_conn类是用于处理http请求和作出http应答的一个类,本程序对于每个新连接的用户都分配一个http_conn对象用于处理http请求; 12 | 13 | (3).util_timer类是定时器类,本程序对于每个新连接的用户都分配一个定时器对象,并设置该定时器的超时时间,如果长连接用户在超时时间内都处于非活动状态则关闭用户连接,如果用户在超时时间到达之前重新活动则延长超时时间。 14 | 15 | 16 | * **web_function.h** 17 | 18 | (1).该文件用于定义全局变量和常量,包括线程数、当前总用户数、读写缓冲区大小等,如果要支持更多的并发用户请求,应该修改该文件中定义的这些常量; 19 | 20 | (2).该文件还提供了一些通用的基本功能函数,例如显示当前时区时间的函数,显示用户ip地址的函数等; 21 | 22 | 23 | * **main.cpp** 24 | 25 | (1). 程序运行入口 26 | 27 | (2). 主线程只负责监听文件描述符是否有事件发生而不处理面向用户的业务逻辑(Reactor模式); 28 | 29 | * **server.pro** 30 | 31 | (1). 该文件是qtcreator关于qmake的工程文件,qt通过该文件来生成Makefile文件。 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Server/Codes/http_conn.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPCONNECTION_H 2 | #define HTTPCONNECTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include"web_function.h" 25 | 26 | 27 | using namespace std; 28 | 29 | const char* ok_200_title = "OK"; 30 | const char* error_400_title = "Bad Request"; 31 | const char* error_400_form = "Your request has bad syntax or is inherently impossible to satisfy.\n"; 32 | const char* error_403_title = "Forbidden"; 33 | const char* error_403_form = "You do not have permission to get file from this server.\n"; 34 | const char* error_404_title = "Not Found"; 35 | const char* error_404_form = "The requested file was not found on this server.\n"; 36 | const char* error_500_title = "Internal Error"; 37 | const char* error_500_form = "There was an unusual problem serving the requested file.\n"; 38 | char* doc_root=new char[256]; 39 | 40 | class util_timer; 41 | 42 | class http_conn 43 | { 44 | public: 45 | util_timer* timer; 46 | enum METHOD { GET = 0, POST, HEAD, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH };//http请求方法,本程序只支持get 47 | enum CHECK_STATE { CHECK_STATE_REQUESTLINE = 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT };//解析客户请求时当前所处的状态 48 | enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, NO_RESOURCE, FORBIDDEN_REQUEST, FILE_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION };//服务器处理http请求的可能结果 49 | enum LINE_STATUS { LINE_OK = 0, LINE_BAD, LINE_OPEN };//每一行的读取状态 50 | 51 | public: 52 | http_conn(){} 53 | ~http_conn(){} 54 | 55 | public: 56 | void init(int epollfd, int sockfd, const sockaddr_in& addr ,util_timer* _timer,int index,stack* _st);//初始化新接受的连接 57 | void close_conn( bool real_close = true );//关闭连接 58 | void process();//处理客户请求 59 | bool read();//非阻塞读操作 60 | bool write();//非阻塞写操作 61 | 62 | private: 63 | void init();//初始化连接 64 | 65 | HTTP_CODE process_read();//解析http请求 66 | bool process_write( HTTP_CODE ret );//填充http应答 67 | 68 | //下面这一组函数被process_read()函数调用以分析http请求 69 | HTTP_CODE parse_request_line( char* text ); 70 | HTTP_CODE parse_headers( char* text ); 71 | HTTP_CODE parse_content( char* text ); 72 | HTTP_CODE do_request(); 73 | char* get_line() { return m_read_buf + m_start_line; } 74 | LINE_STATUS parse_line(); 75 | 76 | //下面这一组函数被process_write()函数调用以填充http应答 77 | void unmap(); 78 | bool add_response( const char* format, ... ); 79 | bool add_content( const char* content ); 80 | bool add_status_line( int status, const char* title ); 81 | bool add_headers( int content_length ); 82 | bool add_content_length( int content_length ); 83 | bool add_linger(); 84 | bool add_blank_line(); 85 | 86 | public: 87 | int m_epollfd; 88 | 89 | private: 90 | int m_sockfd; 91 | sockaddr_in m_address; 92 | 93 | char m_read_buf[ READ_BUFFER_SIZE ]; 94 | int m_read_idx; 95 | int m_checked_idx; 96 | int m_start_line; 97 | char m_write_buf[ WRITE_BUFFER_SIZE ]; 98 | int m_write_idx; 99 | 100 | CHECK_STATE m_check_state; 101 | METHOD m_method; 102 | 103 | char m_real_file[ FILENAME_LEN ]; 104 | char* m_url; 105 | char* m_version; 106 | char* m_host; 107 | int m_content_length; 108 | bool m_linger; 109 | 110 | char* m_file_address; 111 | struct stat m_file_stat; 112 | struct iovec m_iv[2]; 113 | int m_iv_count; 114 | 115 | int user_index; 116 | stack* index_free; 117 | }; 118 | 119 | class util_timer 120 | { 121 | public: 122 | util_timer(time_t t):expire(t),free(true) {} 123 | time_t expire;//超时时间 124 | bool free;//是否被某个用户占用 125 | int sockfd;//关联的连接描述符 126 | void timed_event(){client->close_conn();}//超时操作,关闭关联的连接 127 | http_conn* client;//关联的用户地址 128 | }; 129 | 130 | 131 | void http_conn::close_conn( bool real_close ) 132 | { 133 | if( real_close && ( m_sockfd != -1 ) ) 134 | { 135 | removefd( m_epollfd, m_sockfd ); 136 | timer->free=true; 137 | index_free->push(user_index); 138 | m_sockfd = -1; 139 | 140 | show_sys_time(); 141 | printf("close connection: "); 142 | show_addr(m_address); 143 | user_count--;//C++11 144 | printf("user_online: %d\n",(int)user_count);//C++11 145 | } 146 | } 147 | 148 | void http_conn::init( int epollfd,int sockfd, const sockaddr_in& addr,util_timer* _timer,int index,stack* st) 149 | { 150 | m_epollfd=epollfd; 151 | m_sockfd = sockfd; 152 | m_address = addr; 153 | timer=_timer; 154 | timer->free=false; 155 | timer->sockfd=sockfd; 156 | user_index=index; 157 | index_free=st; 158 | int error = 0; 159 | socklen_t len = sizeof( error ); 160 | getsockopt( m_sockfd, SOL_SOCKET, SO_ERROR, &error, &len ); 161 | // int reuse = 1; 162 | // setsockopt( m_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof( reuse ) ); 163 | addfd( m_epollfd, sockfd, true ); 164 | 165 | init(); 166 | 167 | 168 | show_sys_time(); 169 | printf("new connection: "); 170 | show_addr(addr); 171 | user_count++;//C++11 172 | printf("user_count: %d\n",(int)user_count); 173 | if(user_count.load()>max_user_online.load()) 174 | { 175 | max_user_online=user_count.load(); 176 | } 177 | } 178 | 179 | void http_conn::init() 180 | { 181 | m_check_state = CHECK_STATE_REQUESTLINE; 182 | m_linger = false; 183 | 184 | m_method = GET; 185 | m_url = 0; 186 | m_version = 0; 187 | m_content_length = 0; 188 | m_host = 0; 189 | m_start_line = 0; 190 | m_checked_idx = 0; 191 | m_read_idx = 0; 192 | m_write_idx = 0; 193 | memset( m_read_buf, '\0', READ_BUFFER_SIZE ); 194 | memset( m_write_buf, '\0', WRITE_BUFFER_SIZE ); 195 | memset( m_real_file, '\0', FILENAME_LEN ); 196 | } 197 | 198 | http_conn::LINE_STATUS http_conn::parse_line() 199 | { 200 | char temp; 201 | for ( ; m_checked_idx < m_read_idx; ++m_checked_idx ) 202 | { 203 | temp = m_read_buf[ m_checked_idx ]; 204 | if ( temp == '\r' ) 205 | { 206 | if ( ( m_checked_idx + 1 ) == m_read_idx ) 207 | { 208 | return LINE_OPEN; 209 | } 210 | else if ( m_read_buf[ m_checked_idx + 1 ] == '\n' ) 211 | { 212 | m_read_buf[ m_checked_idx++ ] = '\0'; 213 | m_read_buf[ m_checked_idx++ ] = '\0'; 214 | return LINE_OK; 215 | } 216 | 217 | return LINE_BAD; 218 | } 219 | else if( temp == '\n' ) 220 | { 221 | if( ( m_checked_idx > 1 ) && ( m_read_buf[ m_checked_idx - 1 ] == '\r' ) ) 222 | { 223 | m_read_buf[ m_checked_idx-1 ] = '\0'; 224 | m_read_buf[ m_checked_idx++ ] = '\0'; 225 | return LINE_OK; 226 | } 227 | return LINE_BAD; 228 | } 229 | } 230 | 231 | return LINE_OPEN; 232 | } 233 | 234 | bool http_conn::read() 235 | { 236 | if( m_read_idx >= READ_BUFFER_SIZE ) 237 | { 238 | return false; 239 | } 240 | 241 | int bytes_read = 0; 242 | while( true ) 243 | { 244 | bytes_read = recv( m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0 ); 245 | if ( bytes_read == -1 ) 246 | { 247 | if( errno == EAGAIN || errno == EWOULDBLOCK ) 248 | { 249 | break; 250 | } 251 | return false; 252 | } 253 | else if ( bytes_read == 0 ) 254 | { 255 | return false; 256 | } 257 | 258 | m_read_idx += bytes_read; 259 | } 260 | return true; 261 | } 262 | 263 | http_conn::HTTP_CODE http_conn::parse_request_line( char* text ) 264 | { 265 | m_url = strpbrk( text, " \t" ); 266 | if ( ! m_url ) 267 | { 268 | return BAD_REQUEST; 269 | } 270 | *m_url++ = '\0'; 271 | 272 | char* method = text; 273 | if ( strcasecmp( method, "GET" ) == 0 ) 274 | { 275 | m_method = GET; 276 | } 277 | else 278 | { 279 | return BAD_REQUEST; 280 | } 281 | 282 | m_url += strspn( m_url, " \t" ); 283 | m_version = strpbrk( m_url, " \t" ); 284 | if ( ! m_version ) 285 | { 286 | return BAD_REQUEST; 287 | } 288 | *m_version++ = '\0'; 289 | m_version += strspn( m_version, " \t" ); 290 | if ( strcasecmp( m_version, "HTTP/1.1" ) != 0 ) 291 | { 292 | return BAD_REQUEST; 293 | } 294 | 295 | if ( strncasecmp( m_url, "http://", 7 ) == 0 ) 296 | { 297 | m_url += 7; 298 | m_url = strchr( m_url, '/' ); 299 | } 300 | 301 | if ( ! m_url || m_url[ 0 ] != '/' ) 302 | { 303 | return BAD_REQUEST; 304 | } 305 | 306 | m_check_state = CHECK_STATE_HEADER; 307 | return NO_REQUEST; 308 | } 309 | 310 | http_conn::HTTP_CODE http_conn::parse_headers( char* text ) 311 | { 312 | if( text[ 0 ] == '\0' ) 313 | { 314 | if ( m_method == HEAD ) 315 | { 316 | return GET_REQUEST; 317 | } 318 | 319 | if ( m_content_length != 0 ) 320 | { 321 | m_check_state = CHECK_STATE_CONTENT; 322 | return NO_REQUEST; 323 | } 324 | 325 | return GET_REQUEST; 326 | } 327 | else if ( strncasecmp( text, "Connection:", 11 ) == 0 ) 328 | { 329 | text += 11; 330 | text += strspn( text, " \t" ); 331 | if ( strcasecmp( text, "keep-alive" ) == 0 ) 332 | { 333 | m_linger = true; 334 | } 335 | } 336 | else if ( strncasecmp( text, "Content-Length:", 15 ) == 0 ) 337 | { 338 | text += 15; 339 | text += strspn( text, " \t" ); 340 | m_content_length = atol( text ); 341 | } 342 | else if ( strncasecmp( text, "Host:", 5 ) == 0 ) 343 | { 344 | text += 5; 345 | text += strspn( text, " \t" ); 346 | m_host = text; 347 | } 348 | else 349 | { 350 | printf( "oop! unknow header %s\n", text ); 351 | } 352 | 353 | return NO_REQUEST; 354 | 355 | } 356 | 357 | http_conn::HTTP_CODE http_conn::parse_content( char* text ) 358 | { 359 | if ( m_read_idx >= ( m_content_length + m_checked_idx ) ) 360 | { 361 | text[ m_content_length ] = '\0'; 362 | return GET_REQUEST; 363 | } 364 | 365 | return NO_REQUEST; 366 | } 367 | 368 | http_conn::HTTP_CODE http_conn::process_read() 369 | { 370 | LINE_STATUS line_status = LINE_OK; 371 | HTTP_CODE ret = NO_REQUEST; 372 | char* text = 0; 373 | 374 | printf("get data from: "); 375 | show_addr(m_address); 376 | while ( ( ( m_check_state == CHECK_STATE_CONTENT ) && ( line_status == LINE_OK ) ) 377 | || ( ( line_status = parse_line() ) == LINE_OK ) ) 378 | { 379 | text = get_line(); 380 | m_start_line = m_checked_idx; 381 | printf( "got 1 http line: %s\n", text ); 382 | 383 | switch ( m_check_state ) 384 | { 385 | case CHECK_STATE_REQUESTLINE: 386 | { 387 | ret = parse_request_line( text ); 388 | if ( ret == BAD_REQUEST ) 389 | { 390 | return BAD_REQUEST; 391 | } 392 | break; 393 | } 394 | case CHECK_STATE_HEADER: 395 | { 396 | ret = parse_headers( text ); 397 | if ( ret == BAD_REQUEST ) 398 | { 399 | return BAD_REQUEST; 400 | } 401 | else if ( ret == GET_REQUEST ) 402 | { 403 | return do_request(); 404 | } 405 | break; 406 | } 407 | case CHECK_STATE_CONTENT: 408 | { 409 | ret = parse_content( text ); 410 | if ( ret == GET_REQUEST ) 411 | { 412 | 413 | return do_request(); 414 | } 415 | line_status = LINE_OPEN; 416 | break; 417 | } 418 | default: 419 | { 420 | printf("INTERNAL_ERROR.\n"); 421 | return INTERNAL_ERROR; 422 | } 423 | } 424 | } 425 | 426 | return NO_REQUEST; 427 | } 428 | 429 | http_conn::HTTP_CODE http_conn::do_request() 430 | { 431 | getcwd(doc_root,256); 432 | strcpy( m_real_file, doc_root ); 433 | int len = strlen( doc_root ); 434 | strncpy( m_real_file + len, m_url, FILENAME_LEN - len - 1 ); 435 | if ( stat( m_real_file, &m_file_stat ) < 0 ) 436 | { 437 | return NO_RESOURCE; 438 | } 439 | 440 | if ( ! ( m_file_stat.st_mode & S_IROTH ) ) 441 | { 442 | return FORBIDDEN_REQUEST; 443 | } 444 | 445 | if ( S_ISDIR( m_file_stat.st_mode ) ) 446 | { 447 | return BAD_REQUEST; 448 | } 449 | 450 | int fd = open( m_real_file, O_RDONLY ); 451 | m_file_address = ( char* )mmap( 0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0 ); 452 | close( fd ); 453 | return FILE_REQUEST; 454 | } 455 | 456 | void http_conn::unmap() 457 | { 458 | if( m_file_address ) 459 | { 460 | munmap( m_file_address, m_file_stat.st_size ); 461 | m_file_address = 0; 462 | } 463 | } 464 | 465 | bool http_conn::write() 466 | { 467 | int temp = 0; 468 | int bytes_have_send = 0; 469 | int bytes_to_send = m_write_idx; 470 | if ( bytes_to_send == 0 ) 471 | { 472 | modfd( m_epollfd, m_sockfd, EPOLLIN ); 473 | init(); 474 | return true; 475 | } 476 | 477 | while( 1 ) 478 | { 479 | temp = writev( m_sockfd, m_iv, m_iv_count ); 480 | if ( temp <= -1 ) 481 | { 482 | if( errno == EAGAIN ) 483 | { 484 | modfd( m_epollfd, m_sockfd, EPOLLOUT ); 485 | return true; 486 | } 487 | unmap(); 488 | return false; 489 | } 490 | 491 | bytes_to_send -= temp; 492 | bytes_have_send += temp; 493 | if ( bytes_to_send <= bytes_have_send ) 494 | { 495 | unmap(); 496 | if( m_linger ) 497 | { 498 | init(); 499 | modfd( m_epollfd, m_sockfd, EPOLLIN ); 500 | return true; 501 | } 502 | else 503 | { 504 | modfd( m_epollfd, m_sockfd, EPOLLIN ); 505 | return false; 506 | } 507 | } 508 | } 509 | } 510 | 511 | bool http_conn::add_response( const char* format, ... ) 512 | { 513 | if( m_write_idx >= WRITE_BUFFER_SIZE ) 514 | { 515 | return false; 516 | } 517 | va_list arg_list; 518 | va_start( arg_list, format ); 519 | int len = vsnprintf( m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list ); 520 | if( len >= ( WRITE_BUFFER_SIZE - 1 - m_write_idx ) ) 521 | { 522 | return false; 523 | } 524 | m_write_idx += len; 525 | va_end( arg_list ); 526 | return true; 527 | } 528 | 529 | bool http_conn::add_status_line( int status, const char* title ) 530 | { 531 | return add_response( "%s %d %s\r\n", "HTTP/1.1", status, title ); 532 | } 533 | 534 | bool http_conn::add_headers( int content_len ) 535 | { 536 | add_content_length( content_len ); 537 | add_linger(); 538 | add_blank_line(); 539 | } 540 | 541 | bool http_conn::add_content_length( int content_len ) 542 | { 543 | return add_response( "Content-Length: %d\r\n", content_len ); 544 | } 545 | 546 | bool http_conn::add_linger() 547 | { 548 | return add_response( "Connection: %s\r\n", ( m_linger == true ) ? "keep-alive" : "close" ); 549 | } 550 | 551 | bool http_conn::add_blank_line() 552 | { 553 | return add_response( "%s", "\r\n" ); 554 | } 555 | 556 | bool http_conn::add_content( const char* content ) 557 | { 558 | return add_response( "%s", content ); 559 | } 560 | 561 | bool http_conn::process_write( HTTP_CODE ret ) 562 | { 563 | switch ( ret ) 564 | { 565 | case INTERNAL_ERROR: 566 | { 567 | add_status_line( 500, error_500_title ); 568 | add_headers( strlen( error_500_form ) ); 569 | if ( ! add_content( error_500_form ) ) 570 | { 571 | return false; 572 | } 573 | break; 574 | } 575 | case BAD_REQUEST: 576 | { 577 | add_status_line( 400, error_400_title ); 578 | add_headers( strlen( error_400_form ) ); 579 | if ( ! add_content( error_400_form ) ) 580 | { 581 | return false; 582 | } 583 | break; 584 | } 585 | case NO_RESOURCE: 586 | { 587 | add_status_line( 404, error_404_title ); 588 | add_headers( strlen( error_404_form ) ); 589 | if ( ! add_content( error_404_form ) ) 590 | { 591 | return false; 592 | } 593 | break; 594 | } 595 | case FORBIDDEN_REQUEST: 596 | { 597 | add_status_line( 403, error_403_title ); 598 | add_headers( strlen( error_403_form ) ); 599 | if ( ! add_content( error_403_form ) ) 600 | { 601 | return false; 602 | } 603 | break; 604 | } 605 | case FILE_REQUEST: 606 | { 607 | add_status_line( 200, ok_200_title ); 608 | if ( m_file_stat.st_size != 0 ) 609 | { 610 | add_headers( m_file_stat.st_size ); 611 | m_iv[ 0 ].iov_base = m_write_buf; 612 | m_iv[ 0 ].iov_len = m_write_idx; 613 | m_iv[ 1 ].iov_base = m_file_address; 614 | m_iv[ 1 ].iov_len = m_file_stat.st_size; 615 | m_iv_count = 2; 616 | return true; 617 | } 618 | else 619 | { 620 | const char* ok_string = ""; 621 | add_headers( strlen( ok_string ) ); 622 | if ( ! add_content( ok_string ) ) 623 | { 624 | return false; 625 | } 626 | } 627 | } 628 | default: 629 | { 630 | return false; 631 | } 632 | } 633 | 634 | m_iv[ 0 ].iov_base = m_write_buf; 635 | m_iv[ 0 ].iov_len = m_write_idx; 636 | m_iv_count = 1; 637 | return true; 638 | } 639 | 640 | 641 | void http_conn::process() 642 | { 643 | HTTP_CODE read_ret = process_read();//解析存放在读缓冲区的http报文 644 | if ( read_ret == NO_REQUEST )//如果没有读到完整的报文数据 645 | { 646 | printf("read_data incomplete.\n"); 647 | modfd( m_epollfd, m_sockfd, EPOLLIN );//重置m_sockfd的读事件,等待下一次触发读取更多报文数据 648 | return; 649 | } 650 | 651 | //读取到完整报文数据或者错误报文数据时开始填充写缓冲区 652 | bool write_ret = process_write( read_ret ); 653 | if ( ! write_ret )//如果写的内容超过写缓冲区长度则关闭对应连接 654 | { 655 | printf("write_data overflow.\n"); 656 | close_conn(); 657 | } 658 | else 659 | { 660 | modfd( m_epollfd, m_sockfd, EPOLLOUT );//修改m_sockfd的读事件为写事件,事件触发时执行写操作 661 | } 662 | } 663 | 664 | #endif 665 | -------------------------------------------------------------------------------- /Server/Codes/main.cpp: -------------------------------------------------------------------------------- 1 | #include"web_thread.h" 2 | #include"web_function.h" 3 | #include"http_conn.h" 4 | 5 | using namespace std; 6 | 7 | webthread* thread_ptr;//指向自定义线程类的指针,这是一个全局变量,主线程和子线程通过线程类的双向管道来实现通信 8 | 9 | int sig_pipefd[2];//系统中断信号处理函数与主函数之间的通信管道,本程序把信号处理放在主函数中进行 10 | 11 | 12 | 13 | void* thread(void* arg)//子线程主函数 14 | { 15 | int* p=(int*)arg; 16 | int index=*p; 17 | delete arg; 18 | 19 | thread_ptr[index].work();//对应的子线程开始工作 20 | } 21 | 22 | void create_son_thread()//创建子线程 23 | { 24 | pthread_t tid; 25 | printf( "create son threads now\n" ); 26 | for(int i=0;i[thread_num]; 130 | webthread::listenfd=listenfd; 131 | 132 | pthread_mutex_init(&mutex_conn,NULL); 133 | 134 | create_son_thread();//创建子线程,子线程开始运行 135 | 136 | int epollfd = epoll_create( 5 ); 137 | assert( epollfd != -1 ); 138 | 139 | addfd(epollfd,listenfd,true); 140 | webthread::m_epollfd=epollfd; 141 | 142 | 143 | 144 | int ret = socketpair( PF_UNIX, SOCK_STREAM, 0, sig_pipefd );//创建信号处理函数与主函数之间的通信管道 145 | assert( ret != -1 ); 146 | 147 | setnonblocking( sig_pipefd[1] ); 148 | addfd( epollfd, sig_pipefd[0] ); 149 | 150 | 151 | addsig( SIGTERM, sig_handler ); 152 | addsig( SIGINT, sig_handler ); 153 | addsig( SIGALRM,sig_handler ); 154 | addsig( SIGPIPE, SIG_IGN ); 155 | 156 | epoll_event events[MAX_EVENT_NUM]; 157 | 158 | 159 | int m_pid=(int)getpid(); 160 | printf("PID: %d\n",m_pid); 161 | printf("server start\n"); 162 | alarm(ALARM_TIME);//开始定时 163 | 164 | bool stop=false; 165 | int thread_index=0; 166 | 167 | while(!stop) 168 | { 169 | int number=epoll_wait(epollfd,events,MAX_EVENT_NUM,-1); 170 | if(number<0 && errno!=EINTR) 171 | { 172 | close_son_thread(); 173 | unix_error("main thread epoll failed."); 174 | break; 175 | } 176 | 177 | for(int i=0;i 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | const int thread_num=4;//线程数量 28 | #define LISTENQ 1024//listen()函数的参数 29 | #define MAX_EVENT_NUM 65535//epoll_event的事件数 30 | #define EVENT_TABLE_SIZE 4096//epoll_wait()的参数 31 | #define NEW_CONN '0'//发送给子线程的信号,表示有新连接到来 32 | #define CLOSE_DEAD_CONN '1'//发送给子线程的信号,表示关闭空闲的长连接 33 | #define CLOSE_THREAD '2'//发送给子线程的信号,表示结束子线程 34 | #define ALARM_TIME 30//定时时间,也是长连接的超时时间 35 | #define USER_PER_THREAD 500//每个子线程的最大用户数 36 | 37 | #define FILENAME_LEN 200//文件名称的最大长度 38 | #define READ_BUFFER_SIZE 256//给每个用户分配的读缓冲区大小 39 | #define WRITE_BUFFER_SIZE 256//给每个用户分配的写缓冲区大小 40 | 41 | 42 | pthread_mutex_t mutex_conn; 43 | 44 | 45 | static std::atomic user_count(0);//C++11的原子变量,统计所有线程的当前活跃用户数 46 | static std::atomic max_user_online(0); 47 | 48 | 49 | void unix_error(char *msg) /* Unix-style error */ 50 | { 51 | fprintf(stderr,"%s: %s\n", msg, strerror(errno)); 52 | exit(0); 53 | } 54 | 55 | void app_error(char *msg) /* Application error */ 56 | { 57 | fprintf(stderr, "%s\n", msg); 58 | exit(0); 59 | } 60 | 61 | void show_error(char *msg) 62 | { 63 | fprintf(stderr, "%s: %s\n", msg, strerror(errno)); 64 | } 65 | 66 | void show_addr(sockaddr_in address) 67 | { 68 | int save_errno=errno; 69 | char host[NI_MAXHOST]; 70 | char service[NI_MAXSERV]; 71 | socklen_t addrlength = sizeof(address ); 72 | 73 | int ret=getnameinfo((struct sockaddr *)(&address), sizeof(struct sockaddr),host, sizeof(host), service, sizeof(service),NI_NUMERICHOST|NI_NUMERICSERV); 74 | if(ret!=0) 75 | { 76 | show_error("address changed failed"); 77 | } 78 | else 79 | { 80 | printf("(%s: %s)\n",host,service); 81 | } 82 | errno=save_errno; 83 | 84 | } 85 | 86 | int Open(const char *pathname, int flags, mode_t mode) 87 | { 88 | int rc; 89 | 90 | if ((rc = open(pathname, flags, mode)) < 0) 91 | unix_error("Open error"); 92 | return rc; 93 | } 94 | 95 | ssize_t Read(int fd, void *buf, size_t count) 96 | { 97 | ssize_t rc; 98 | 99 | if ((rc = read(fd, buf, count)) < 0) 100 | unix_error("Read error"); 101 | return rc; 102 | } 103 | 104 | ssize_t Write(int fd, const void *buf, size_t count) 105 | { 106 | ssize_t rc; 107 | 108 | if ((rc = write(fd, buf, count)) < 0) 109 | unix_error("Write error"); 110 | return rc; 111 | } 112 | 113 | 114 | void Close(int fd) 115 | { 116 | int rc; 117 | 118 | if ((rc = close(fd)) < 0) 119 | unix_error("Close error"); 120 | } 121 | 122 | 123 | static int setnonblocking( int fd ) 124 | { 125 | int old_option = fcntl( fd, F_GETFL ); 126 | int new_option = old_option | O_NONBLOCK; 127 | fcntl( fd, F_SETFL, new_option ); 128 | return old_option; 129 | } 130 | 131 | 132 | static void removefd( int epollfd, int fd )//删除事件 133 | { 134 | epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 ); 135 | Close( fd ); 136 | } 137 | 138 | static void addfd( int epollfd, int fd )//添加事件 139 | { 140 | epoll_event event; 141 | event.data.fd = fd; 142 | event.events = EPOLLIN | EPOLLET; 143 | epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event ); 144 | setnonblocking( fd ); 145 | } 146 | 147 | void addfd( int epollfd, int fd, bool one_shot )//添加事件 148 | { 149 | epoll_event event; 150 | event.data.fd = fd; 151 | event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; 152 | if( one_shot ) 153 | { 154 | event.events |= EPOLLONESHOT; 155 | } 156 | epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event ); 157 | setnonblocking( fd ); 158 | } 159 | 160 | void modfd( int epollfd, int fd, int ev )//修改事件 161 | { 162 | epoll_event event; 163 | event.data.fd = fd; 164 | event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP; 165 | epoll_ctl( epollfd, EPOLL_CTL_MOD, fd, &event ); 166 | } 167 | 168 | void show_sys_time() 169 | { 170 | 171 | time_t timel; 172 | time(&timel); 173 | printf("time: %s",asctime(gmtime(&timel))); 174 | } 175 | 176 | static void addsig( int sig, void( handler )(int), bool restart = true ) 177 | { 178 | struct sigaction sa; 179 | memset( &sa, '\0', sizeof( sa ) ); 180 | sa.sa_handler = handler; 181 | if( restart ) 182 | { 183 | sa.sa_flags |= SA_RESTART; 184 | } 185 | sigfillset( &sa.sa_mask ); 186 | assert( sigaction( sig, &sa, NULL ) != -1 ); 187 | } 188 | 189 | int open_clientfd(char *ip, char *_port) { 190 | 191 | int port=atoi(_port); 192 | 193 | struct sockaddr_in address; 194 | bzero( &address, sizeof( address ) ); 195 | address.sin_family = AF_INET; 196 | inet_pton( AF_INET, ip, &address.sin_addr ); 197 | address.sin_port = htons( port ); 198 | 199 | int fd=socket(AF_INET,SOCK_STREAM,0); 200 | 201 | int ret=connect(fd,(struct sockaddr*)&address,sizeof(address)); 202 | if(ret<0) 203 | { 204 | app_error("server connected failed"); 205 | } 206 | else 207 | { 208 | return fd; 209 | } 210 | } 211 | 212 | int open_listenfd(char *ip, char *_port,int backlog)//仅ipv4 213 | { 214 | int port=atoi(_port); 215 | 216 | struct sockaddr_in address; 217 | bzero( &address, sizeof( address ) ); 218 | address.sin_family = AF_INET; 219 | inet_pton( AF_INET, ip, &address.sin_addr ); 220 | address.sin_port = htons( port ); 221 | 222 | int fd=socket(AF_INET,SOCK_STREAM,0); 223 | 224 | int ret=bind(fd,(struct sockaddr*)&address,sizeof(address)); 225 | assert(ret!=-1); 226 | 227 | ret=listen(fd,backlog); 228 | if(ret<0) 229 | { 230 | unix_error("listenfd opened failed."); 231 | } 232 | else 233 | { 234 | return fd; 235 | } 236 | 237 | } 238 | 239 | int open_listenfd(char *port)//ipv4/ipv6通用 240 | { 241 | struct addrinfo hints, *listp, *p; 242 | int listenfd, rc, optval=1; 243 | 244 | /* Get a list of potential server addresses */ 245 | memset(&hints, 0, sizeof(struct addrinfo)); 246 | hints.ai_socktype = SOCK_STREAM; /* Accept connections */ 247 | hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */ 248 | hints.ai_flags |= AI_NUMERICSERV; /* ... using port number */ 249 | if ((rc = getaddrinfo(NULL, port, &hints, &listp)) != 0) { 250 | fprintf(stderr, "getaddrinfo failed (port %s): %s\n", port, gai_strerror(rc)); 251 | return -2; 252 | } 253 | 254 | /* Walk the list for one that we can bind to */ 255 | for (p = listp; p; p = p->ai_next) { 256 | /* Create a socket descriptor */ 257 | if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 258 | continue; /* Socket failed, try the next */ 259 | 260 | /* Eliminates "Address already in use" error from bind */ 261 | setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, //line:netp:csapp:setsockopt 262 | (const void *)&optval , sizeof(int)); 263 | 264 | /* Bind the descriptor to the address */ 265 | if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0) 266 | break; /* Success */ 267 | if (close(listenfd) < 0) { /* Bind failed, try the next */ 268 | fprintf(stderr, "open_listenfd close failed: %s\n", strerror(errno)); 269 | return -1; 270 | } 271 | } 272 | 273 | 274 | /* Clean up */ 275 | freeaddrinfo(listp); 276 | if (!p) /* No address worked */ 277 | return -1; 278 | 279 | /* Make it a listening socket ready to accept connection requests */ 280 | if (listen(listenfd, LISTENQ) < 0) { 281 | close(listenfd); 282 | return -1; 283 | } 284 | return listenfd; 285 | } 286 | 287 | 288 | #endif // WEB_FUNCTION 289 | 290 | -------------------------------------------------------------------------------- /Server/Codes/web_thread.h: -------------------------------------------------------------------------------- 1 | #ifndef WEB_THREAD_H 2 | #define WEB_THREAD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include"http_conn.h" 25 | #include"web_function.h" 26 | 27 | using namespace std; 28 | 29 | const char* server_busy_info="HTTP/1.1 503 Service Unavailable\r\n\r\n";//当子线程的用户数达到上限值时向新连接发送该信息 30 | 31 | struct timer_cmp 32 | { 33 | bool operator () (util_timer* a,util_timer* b) 34 | { 35 | return a->expire>b->expire; 36 | } 37 | }; 38 | 39 | template 40 | class webthread 41 | { 42 | public: 43 | webthread() : user_num(0),stop(false) 44 | { 45 | epollfd = epoll_create(EVENT_TABLE_SIZE); 46 | assert( epollfd != -1 ); 47 | 48 | int ret = socketpair( PF_UNIX, SOCK_STREAM, 0, pipefd); 49 | assert( ret != -1 ); 50 | 51 | setnonblocking( pipefd[1] ); 52 | addfd( epollfd, pipefd[0] ); 53 | users=new T[USER_PER_THREAD];//提前分配用户空间 54 | } 55 | ~webthread() 56 | { 57 | Close(pipefd[1]); 58 | Close(pipefd[0]); 59 | Close(epollfd); 60 | } 61 | 62 | public: 63 | pthread_t tid; 64 | int pipefd[2];//通过该管道实现子线程与主线程的双向通信 65 | static int listenfd;//主线程中的监听描述符 66 | static int m_epollfd;//主线程的epollfd,在工作线程中访问该epollfd来重置listenfd的事件 67 | 68 | void work(); 69 | private: 70 | int user_num;//当前在线客户端数量 71 | int epollfd;//工作线程的epollfd 72 | T* users;//客户端连接的工作空间 73 | bool stop;//是否退出线程主循环 74 | 75 | }; 76 | template 77 | int webthread::listenfd=-1; 78 | 79 | template 80 | int webthread::m_epollfd=-1; 81 | 82 | template 83 | void webthread::work() 84 | { 85 | epoll_event events[MAX_EVENT_NUM]; 86 | stack index_free;//初始化空闲队列 87 | for(int i=USER_PER_THREAD-1;i>=0;i--) 88 | { 89 | index_free.push(i); 90 | } 91 | unordered_map mp;//用户索引与连接符的匹配 92 | priority_queue,timer_cmp> timer_queue;//定时器容器 93 | bool timeshot=false;//定时事件 94 | 95 | 96 | while(!stop) 97 | { 98 | int number=epoll_wait(epollfd,events,MAX_EVENT_NUM,-1); 99 | if(number<0 && errno!=EINTR) 100 | { 101 | unix_error("epoll failed."); 102 | break; 103 | } 104 | for(int i=0;i0) 127 | { 128 | conn_count++; 129 | if(!index_free.empty())//如果还没有达到本线程的最大用户数,则给新用户分配用户空间 130 | { 131 | int user_index=index_free.top(); 132 | index_free.pop(); 133 | mp[connfd]=user_index; 134 | util_timer* _timer=new util_timer(time(NULL)+ALARM_TIME);//给每个用户分配一个定时器 135 | _timer->client=&users[user_index];//将定时器与用户地址关联起来,方便后面通过定时器关闭非活动连接 136 | timer_queue.push(_timer);//将定时器放入队列容器中 137 | users[user_index].init( epollfd, connfd, client_address,_timer,user_index,&index_free);//初始化用户空间 138 | } 139 | else//如果用户已满,向新连接发送服务器繁忙消息 140 | { 141 | ret=send(connfd,server_busy_info,strlen(server_busy_info),0); 142 | if(ret<=0) 143 | { 144 | printf("busy_info send failed.\n"); 145 | } 146 | Close(connfd); 147 | } 148 | memset((char*)&client_address,0,client_addrlength); 149 | connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength ); 150 | } 151 | modfd(m_epollfd, listenfd, EPOLLIN);//重置listenfd的读事件 152 | if(conn_count==0) 153 | { 154 | printf("user_num_now: %d\n",user_num); 155 | show_error("accept connection failed"); 156 | continue; 157 | } 158 | break; 159 | } 160 | case CLOSE_DEAD_CONN: 161 | { 162 | timeshot=true;//定时事件优先级最低 163 | break; 164 | } 165 | case CLOSE_THREAD: 166 | { 167 | printf("thread:%ld get close info\n",tid); 168 | stop=true;//退出线程主循环 169 | break; 170 | } 171 | default: 172 | break; 173 | } 174 | } 175 | } 176 | } 177 | else if( events[i].events & ( EPOLLRDHUP | EPOLLHUP | EPOLLERR ) )//如果有连接描述符出现错误 178 | { 179 | int user_index=mp[sockfd]; 180 | users[user_index].close_conn(); 181 | } 182 | else if( events[i].events & EPOLLIN )//如果有连接描述符可读 183 | { 184 | int user_index=mp[sockfd]; 185 | if( users[user_index].read() )//读取连接描述符的消息到用户的读缓冲区 186 | { 187 | users[user_index].process();//解析用户读缓冲区的http请求,解析后往用户的写缓冲区写http应答,并将该连接描述符的epoll注册事件改为写事件 188 | } 189 | else 190 | { 191 | users[user_index].close_conn();//如果失败则关闭对应描述符,并释放被原用户占用的空间 192 | } 193 | } 194 | else if( events[i].events & EPOLLOUT )//如果有连接描述符可写 195 | { 196 | int user_index=mp[sockfd]; 197 | if( !users[user_index].write() )//如果写失败或者是短连接则立即关闭连接 198 | { 199 | users[user_index].close_conn(); 200 | } 201 | else//如果是长连接则延长定时器的定时时间,并将该连接描述符的epoll注册事件改为读事件 202 | { 203 | util_timer* timer_temp=users[user_index].timer; 204 | timer_temp->expire=time(NULL)+ALARM_TIME; 205 | } 206 | } 207 | else 208 | {} 209 | if(timeshot)//定时时间到,回收非活动连接 210 | { 211 | while(!timer_queue.empty()) 212 | { 213 | util_timer* timer_temp=timer_queue.top(); 214 | timer_queue.pop(); 215 | timer_queue.push(timer_temp); 216 | timer_temp=timer_queue.top();//由于指针指向的元素值发生改变,需要重排元素 217 | time_t t=time(NULL); 218 | if(timer_temp->free)//如果定时器关联的连接已关闭,则释放定时器占用的空间 219 | { 220 | timer_queue.pop(); 221 | delete timer_temp; 222 | } 223 | else if(timer_temp->expire<=t)//关闭定时器关联的连接并释放空间 224 | { 225 | timer_temp->timed_event(); 226 | timer_queue.pop(); 227 | delete timer_temp; 228 | } 229 | else//剩下的连接还没到超时时间 230 | { 231 | timeshot=false; 232 | break; 233 | } 234 | } 235 | timeshot=false; 236 | } 237 | } 238 | } 239 | 240 | //退出线程前先回收堆空间以避免内存泄漏 241 | while (!timer_queue.empty()) 242 | { 243 | util_timer* timer_temp=timer_queue.top(); 244 | timer_queue.pop(); 245 | if(!timer_temp->free) 246 | { 247 | timer_temp->timed_event(); 248 | } 249 | delete timer_temp; 250 | } 251 | delete [] users; 252 | printf("closed thread: %ld\n",tid); 253 | 254 | } 255 | 256 | #endif // WEB_THREAD_H 257 | 258 | -------------------------------------------------------------------------------- /Server/Debug/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # Makefile for building: server 3 | # Generated by qmake (2.01a) (Qt 4.8.7) on: ?? 3? 28 13:48:52 2020 4 | # Project: ../Codes/server.pro 5 | # Template: app 6 | # 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 7 | ############################################################################# 8 | 9 | ####### Compiler, tools and options 10 | 11 | CC = gcc 12 | CXX = g++ 13 | DEFINES = -DQT_CORE_LIB -DQT_SHARED 14 | CFLAGS = -m64 -pipe -g -Wall -W -D_REENTRANT $(DEFINES) 15 | CXXFLAGS = -m64 -pipe -std=c++0x -g -Wall -W -D_REENTRANT $(DEFINES) 16 | INCPATH = -I/usr/share/qt4/mkspecs/linux-g++-64 -I../Codes -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. -I../Codes -I. 17 | LINK = g++ 18 | LFLAGS = -m64 19 | LIBS = $(SUBLIBS) -L/usr/lib/x86_64-linux-gnu -lQtCore -lpthread 20 | AR = ar cqs 21 | RANLIB = 22 | QMAKE = /usr/lib/x86_64-linux-gnu/qt4/bin/qmake 23 | TAR = tar -cf 24 | COMPRESS = gzip -9f 25 | COPY = cp -f 26 | SED = sed 27 | COPY_FILE = $(COPY) 28 | COPY_DIR = $(COPY) -r 29 | STRIP = strip 30 | INSTALL_FILE = install -m 644 -p 31 | INSTALL_DIR = $(COPY_DIR) 32 | INSTALL_PROGRAM = install -m 755 -p 33 | DEL_FILE = rm -f 34 | SYMLINK = ln -f -s 35 | DEL_DIR = rmdir 36 | MOVE = mv -f 37 | CHK_DIR_EXISTS= test -d 38 | MKDIR = mkdir -p 39 | 40 | ####### Output directory 41 | 42 | OBJECTS_DIR = ./ 43 | 44 | ####### Files 45 | 46 | SOURCES = ../Codes/main.cpp 47 | OBJECTS = main.o 48 | DIST = /usr/share/qt4/mkspecs/common/unix.conf \ 49 | /usr/share/qt4/mkspecs/common/linux.conf \ 50 | /usr/share/qt4/mkspecs/common/gcc-base.conf \ 51 | /usr/share/qt4/mkspecs/common/gcc-base-unix.conf \ 52 | /usr/share/qt4/mkspecs/common/g++-base.conf \ 53 | /usr/share/qt4/mkspecs/common/g++-unix.conf \ 54 | /usr/share/qt4/mkspecs/qconfig.pri \ 55 | /usr/share/qt4/mkspecs/features/qt_functions.prf \ 56 | /usr/share/qt4/mkspecs/features/qt_config.prf \ 57 | /usr/share/qt4/mkspecs/features/exclusive_builds.prf \ 58 | /usr/share/qt4/mkspecs/features/default_pre.prf \ 59 | /usr/share/qt4/mkspecs/features/debug.prf \ 60 | /usr/share/qt4/mkspecs/features/default_post.prf \ 61 | /usr/share/qt4/mkspecs/features/shared.prf \ 62 | /usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \ 63 | /usr/share/qt4/mkspecs/features/warn_on.prf \ 64 | /usr/share/qt4/mkspecs/features/qt.prf \ 65 | /usr/share/qt4/mkspecs/features/unix/thread.prf \ 66 | /usr/share/qt4/mkspecs/features/moc.prf \ 67 | /usr/share/qt4/mkspecs/features/resources.prf \ 68 | /usr/share/qt4/mkspecs/features/uic.prf \ 69 | /usr/share/qt4/mkspecs/features/yacc.prf \ 70 | /usr/share/qt4/mkspecs/features/lex.prf \ 71 | /usr/share/qt4/mkspecs/features/include_source_dir.prf \ 72 | ../Codes/server.pro 73 | QMAKE_TARGET = server 74 | DESTDIR = 75 | TARGET = server 76 | 77 | first: all 78 | ####### Implicit rules 79 | 80 | .SUFFIXES: .o .c .cpp .cc .cxx .C 81 | 82 | .cpp.o: 83 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" 84 | 85 | .cc.o: 86 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" 87 | 88 | .cxx.o: 89 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" 90 | 91 | .C.o: 92 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" 93 | 94 | .c.o: 95 | $(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<" 96 | 97 | ####### Build rules 98 | 99 | all: Makefile $(TARGET) 100 | 101 | $(TARGET): $(OBJECTS) 102 | $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS) 103 | { 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 104 | 105 | Makefile: ../Codes/server.pro /usr/share/qt4/mkspecs/linux-g++-64/qmake.conf /usr/share/qt4/mkspecs/common/unix.conf \ 106 | /usr/share/qt4/mkspecs/common/linux.conf \ 107 | /usr/share/qt4/mkspecs/common/gcc-base.conf \ 108 | /usr/share/qt4/mkspecs/common/gcc-base-unix.conf \ 109 | /usr/share/qt4/mkspecs/common/g++-base.conf \ 110 | /usr/share/qt4/mkspecs/common/g++-unix.conf \ 111 | /usr/share/qt4/mkspecs/qconfig.pri \ 112 | /usr/share/qt4/mkspecs/features/qt_functions.prf \ 113 | /usr/share/qt4/mkspecs/features/qt_config.prf \ 114 | /usr/share/qt4/mkspecs/features/exclusive_builds.prf \ 115 | /usr/share/qt4/mkspecs/features/default_pre.prf \ 116 | /usr/share/qt4/mkspecs/features/debug.prf \ 117 | /usr/share/qt4/mkspecs/features/default_post.prf \ 118 | /usr/share/qt4/mkspecs/features/shared.prf \ 119 | /usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \ 120 | /usr/share/qt4/mkspecs/features/warn_on.prf \ 121 | /usr/share/qt4/mkspecs/features/qt.prf \ 122 | /usr/share/qt4/mkspecs/features/unix/thread.prf \ 123 | /usr/share/qt4/mkspecs/features/moc.prf \ 124 | /usr/share/qt4/mkspecs/features/resources.prf \ 125 | /usr/share/qt4/mkspecs/features/uic.prf \ 126 | /usr/share/qt4/mkspecs/features/yacc.prf \ 127 | /usr/share/qt4/mkspecs/features/lex.prf \ 128 | /usr/share/qt4/mkspecs/features/include_source_dir.prf \ 129 | /usr/lib/x86_64-linux-gnu/libQtCore.prl 130 | $(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/server.pro 131 | /usr/share/qt4/mkspecs/common/unix.conf: 132 | /usr/share/qt4/mkspecs/common/linux.conf: 133 | /usr/share/qt4/mkspecs/common/gcc-base.conf: 134 | /usr/share/qt4/mkspecs/common/gcc-base-unix.conf: 135 | /usr/share/qt4/mkspecs/common/g++-base.conf: 136 | /usr/share/qt4/mkspecs/common/g++-unix.conf: 137 | /usr/share/qt4/mkspecs/qconfig.pri: 138 | /usr/share/qt4/mkspecs/features/qt_functions.prf: 139 | /usr/share/qt4/mkspecs/features/qt_config.prf: 140 | /usr/share/qt4/mkspecs/features/exclusive_builds.prf: 141 | /usr/share/qt4/mkspecs/features/default_pre.prf: 142 | /usr/share/qt4/mkspecs/features/debug.prf: 143 | /usr/share/qt4/mkspecs/features/default_post.prf: 144 | /usr/share/qt4/mkspecs/features/shared.prf: 145 | /usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf: 146 | /usr/share/qt4/mkspecs/features/warn_on.prf: 147 | /usr/share/qt4/mkspecs/features/qt.prf: 148 | /usr/share/qt4/mkspecs/features/unix/thread.prf: 149 | /usr/share/qt4/mkspecs/features/moc.prf: 150 | /usr/share/qt4/mkspecs/features/resources.prf: 151 | /usr/share/qt4/mkspecs/features/uic.prf: 152 | /usr/share/qt4/mkspecs/features/yacc.prf: 153 | /usr/share/qt4/mkspecs/features/lex.prf: 154 | /usr/share/qt4/mkspecs/features/include_source_dir.prf: 155 | /usr/lib/x86_64-linux-gnu/libQtCore.prl: 156 | qmake: FORCE 157 | @$(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++-64 CONFIG+=debug -o Makefile ../Codes/server.pro 158 | 159 | dist: 160 | @$(CHK_DIR_EXISTS) .tmp/server1.0.0 || $(MKDIR) .tmp/server1.0.0 161 | $(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 162 | 163 | 164 | clean:compiler_clean 165 | -$(DEL_FILE) $(OBJECTS) 166 | -$(DEL_FILE) *~ core *.core 167 | 168 | 169 | ####### Sub-libraries 170 | 171 | distclean: clean 172 | -$(DEL_FILE) $(TARGET) 173 | -$(DEL_FILE) Makefile 174 | 175 | 176 | check: first 177 | 178 | mocclean: compiler_moc_header_clean compiler_moc_source_clean 179 | 180 | mocables: compiler_moc_header_make_all compiler_moc_source_make_all 181 | 182 | compiler_moc_header_make_all: 183 | compiler_moc_header_clean: 184 | compiler_rcc_make_all: 185 | compiler_rcc_clean: 186 | compiler_image_collection_make_all: qmake_image_collection.cpp 187 | compiler_image_collection_clean: 188 | -$(DEL_FILE) qmake_image_collection.cpp 189 | compiler_moc_source_make_all: 190 | compiler_moc_source_clean: 191 | compiler_uic_make_all: 192 | compiler_uic_clean: 193 | compiler_yacc_decl_make_all: 194 | compiler_yacc_decl_clean: 195 | compiler_yacc_impl_make_all: 196 | compiler_yacc_impl_clean: 197 | compiler_lex_make_all: 198 | compiler_lex_clean: 199 | compiler_clean: 200 | 201 | ####### Compile 202 | 203 | main.o: ../Codes/main.cpp ../Codes/web_thread.h \ 204 | ../Codes/http_conn.h \ 205 | ../Codes/web_function.h 206 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o ../Codes/main.cpp 207 | 208 | ####### Install 209 | 210 | install: FORCE 211 | 212 | uninstall: FORCE 213 | 214 | FORCE: 215 | 216 | -------------------------------------------------------------------------------- /Server/Debug/cgi-bin/adder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Buerzhu/TinyWeb/62e7559a1ebd887c4ff748c015118a90f019b884/Server/Debug/cgi-bin/adder -------------------------------------------------------------------------------- /Server/Debug/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | test 4 | 5 | 6 |

test

7 | 8 | 9 | -------------------------------------------------------------------------------- /Server/Debug/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Buerzhu/TinyWeb/62e7559a1ebd887c4ff748c015118a90f019b884/Server/Debug/main.o -------------------------------------------------------------------------------- /Server/Debug/server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Buerzhu/TinyWeb/62e7559a1ebd887c4ff748c015118a90f019b884/Server/Debug/server -------------------------------------------------------------------------------- /runClient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Buerzhu/TinyWeb/62e7559a1ebd887c4ff748c015118a90f019b884/runClient.png -------------------------------------------------------------------------------- /runServer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Buerzhu/TinyWeb/62e7559a1ebd887c4ff748c015118a90f019b884/runServer.png --------------------------------------------------------------------------------