├── .clang-format ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── WebBench ├── LICENSE ├── Makefile ├── README.md ├── bin │ └── webbench ├── build.sh ├── debian │ ├── changelog │ ├── control │ ├── copyright │ ├── dirs │ └── rules ├── man │ └── man1 │ │ └── webbench.1 ├── share │ └── doc │ │ └── webbench │ │ ├── changelog │ │ └── copyright ├── socket.c ├── tags ├── test.sh ├── webbench ├── webbench.1 └── webbench.c ├── WebServer ├── CMakeLists.txt ├── Channel.cpp ├── Channel.h ├── Epoll.cpp ├── Epoll.h ├── EventLoop.cpp ├── EventLoop.h ├── EventLoopThread.cpp ├── EventLoopThread.h ├── EventLoopThreadPool.cpp ├── EventLoopThreadPool.h ├── HttpData.cpp ├── HttpData.h ├── Main.cpp ├── Makefile ├── Makefile.bak ├── Server.cpp ├── Server.h ├── ThreadPool.cpp ├── ThreadPool.h ├── Timer.cpp ├── Timer.h ├── Util.cpp ├── Util.h ├── base │ ├── AsyncLogging.cpp │ ├── AsyncLogging.h │ ├── CMakeLists.txt │ ├── Condition.h │ ├── CountDownLatch.cpp │ ├── CountDownLatch.h │ ├── CurrentThread.h │ ├── FileUtil.cpp │ ├── FileUtil.h │ ├── LogFile.cpp │ ├── LogFile.h │ ├── LogStream.cpp │ ├── LogStream.h │ ├── Logging.cpp │ ├── Logging.h │ ├── Log的设计.txt │ ├── MutexLock.h │ ├── Thread.cpp │ ├── Thread.h │ ├── noncopyable.h │ └── tests │ │ ├── CMakeLists.txt │ │ └── LoggingTest.cpp ├── config.h └── tests │ ├── CMakeLists.txt │ └── HTTPClient.cpp ├── build.sh ├── datum ├── WebServer.png ├── WebServerk.png ├── cloc.png ├── close.png ├── gdb.png ├── idle.png ├── keepalive.png ├── model.png ├── muduo.png └── muduok.png ├── old_version ├── old_version_0.1 │ ├── .vscode │ │ └── settings.json │ ├── Makefile │ ├── Makefile1 │ ├── epoll.cpp │ ├── epoll.h │ ├── improvement.txt │ ├── index.html │ ├── main.cpp │ ├── requestData.cpp │ ├── requestData.h │ ├── threadpool.cpp │ ├── threadpool.h │ ├── util.cpp │ └── util.h ├── old_version_0.2 │ ├── Makefile │ ├── Makefile1 │ ├── epoll.cpp │ ├── epoll.h │ ├── favicon.ico │ ├── improvement.txt │ ├── index.html │ ├── main.cpp │ ├── requestData.cpp │ ├── requestData.h │ ├── threadpool.cpp │ ├── threadpool.h │ ├── util.cpp │ └── util.h ├── old_version_0.3 │ ├── Makefile │ ├── Makefile1 │ ├── config.h │ ├── epoll.cpp │ ├── epoll.h │ ├── favicon.ico │ ├── improvement.txt │ ├── index.html │ ├── main.cpp │ ├── requestData.cpp │ ├── requestData.h │ ├── threadpool.cpp │ ├── threadpool.h │ ├── util.cpp │ └── util.h ├── old_version_0.4 │ ├── Makefile │ ├── Makefile1 │ ├── base │ │ ├── mutexLock.hpp │ │ └── nocopyable.hpp │ ├── config.h │ ├── epoll.cpp │ ├── epoll.h │ ├── favicon.ico │ ├── improvement.txt │ ├── index.html │ ├── main.cpp │ ├── requestData.cpp │ ├── requestData.h │ ├── threadpool.cpp │ ├── threadpool.h │ ├── timer.cpp │ ├── timer.h │ ├── util.cpp │ └── util.h ├── old_version_0.5 │ ├── Makefile │ ├── Makefile1 │ ├── base │ │ ├── condition.hpp │ │ ├── mutexLock.hpp │ │ └── nocopyable.hpp │ ├── config.h │ ├── epoll.cpp │ ├── epoll.h │ ├── favicon.ico │ ├── index.html │ ├── main.cpp │ ├── requestData.cpp │ ├── requestData.h │ ├── threadpool.cpp │ ├── threadpool.h │ ├── timer.cpp │ ├── timer.h │ ├── util.cpp │ └── util.h └── old_version_0.6 │ ├── AsyncLogging.cpp │ ├── AsyncLogging.h │ ├── Condition.h │ ├── CountDownLatch.cpp │ ├── CountDownLatch.h │ ├── CurrentThread.hpp │ ├── FileUtil.cpp │ ├── FileUtil.h │ ├── LogFile.cpp │ ├── LogFile.h │ ├── LogStream.cpp │ ├── LogStream.h │ ├── Logging.cpp │ ├── Logging.h │ ├── Makefile │ ├── Makefile1 │ ├── MutexLock.h │ ├── Thread.cpp │ ├── Thread.h │ ├── config.h │ ├── epoll.cpp │ ├── epoll.h │ ├── favicon.ico │ ├── index.html │ ├── main.cpp │ ├── noncopyable.h │ ├── requestData.cpp │ ├── requestData.h │ ├── threadpool.cpp │ ├── threadpool.h │ ├── timer.cpp │ ├── timer.h │ ├── util.cpp │ └── util.h ├── 并发模型.md ├── 测试及改进.md ├── 版本历史.md ├── 连接的维护.md ├── 遇到的困难.md └── 项目目的.md /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: Google -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Otheres 35 | core 36 | .vscode 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | dist: trusty 4 | compiler: 5 | - g++ 6 | os: 7 | - linux 8 | install: 9 | - sudo apt-get install cmake 10 | env: 11 | - BUILD_TYPE=debug 12 | - BUILD_TYPE=release 13 | script: 14 | - ./build.sh 15 | 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(WebServer CXX) 4 | 5 | if(NOT CMAKE_BUILD_TYPE) 6 | set(CMAKE_BUILD_TYPE "Debug") 7 | endif() 8 | 9 | set(CXX_FLAGS 10 | -g 11 | -Wall 12 | -std=c++11 13 | -D_PTHREADS 14 | -Wno-unused-parameter 15 | ) 16 | 17 | 18 | set(CMAKE_CXX_COMPILER "g++") 19 | set(CMAKE_CXX_FLAGS_DEBUG "-O0") 20 | set(CMAKE_CXX_FLAGS_RELEASE "-O0") 21 | 22 | string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}") 23 | 24 | 25 | string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE) 26 | message(STATUS "CXX_FLAGS = " ${CMAKE_CXX_FLAGS} " " ${CMAKE_CXX_FLAGS_${BUILD_TYPE}}) 27 | 28 | add_subdirectory(WebServer) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 林亚 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A C++ High Performance Web Server 2 | 3 | [![Build Status](https://travis-ci.org/linyacool/WebServer.svg?branch=master)](https://travis-ci.org/linyacool/WebServer) 4 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) 5 | 6 | 7 | ## Introduction 8 | 9 | 本项目为C++11编写的Web服务器,解析了get、head请求,可处理静态资源,支持HTTP长连接,支持管线化请求,并实现了异步日志,记录服务器运行状态。 10 | 11 | 12 | 13 | | Part Ⅰ | Part Ⅱ | Part Ⅲ | Part Ⅳ | Part Ⅴ | Part Ⅵ | 14 | | :--------: | :---------: | :---------: | :---------: | :---------: | :---------: | 15 | | [并发模型](https://github.com/linyacool/WebServer/blob/master/并发模型.md)|[连接的维护](https://github.com/linyacool/WebServer/blob/master/连接的维护.md)|[版本历史](https://github.com/linyacool/WebServer/blob/master/%E7%89%88%E6%9C%AC%E5%8E%86%E5%8F%B2.md) | [测试及改进](https://github.com/linyacool/WebServer/blob/master/测试及改进.md) | [项目目的](https://github.com/linyacool/WebServer/blob/master/%E9%A1%B9%E7%9B%AE%E7%9B%AE%E7%9A%84.md) | [面试问题](https://github.com/linyacool/WebServer/blob/master/%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98.md) 16 | 17 | ## Envoirment 18 | * OS: Ubuntu 14.04 19 | * Complier: g++ 4.8 20 | 21 | ## Build 22 | 23 | ./build.sh 24 | 25 | ## Usage 26 | 27 | ./WebServer [-t thread_numbers] [-p port] [-l log_file_path(should begin with '/')] 28 | 29 | ## Technical points 30 | * 使用Epoll边沿触发的IO多路复用技术,非阻塞IO,使用Reactor模式 31 | * 使用多线程充分利用多核CPU,并使用线程池避免线程频繁创建销毁的开销 32 | * 使用基于小根堆的定时器关闭超时请求 33 | * 主线程只负责accept请求,并以Round Robin的方式分发给其它IO线程(兼计算线程),锁的争用只会出现在主线程和某一特定线程中 34 | * 使用eventfd实现了线程的异步唤醒 35 | * 使用双缓冲区技术实现了简单的异步日志系统 36 | * 为减少内存泄漏的可能,使用智能指针等RAII机制 37 | * 使用状态机解析了HTTP请求,支持管线化 38 | * 支持优雅关闭连接 39 |   40 | ## Model 41 | 42 | 并发模型为Reactor+非阻塞IO+线程池,新连接Round Robin分配,详细介绍请参考[并发模型](https://github.com/linyacool/WebServer/blob/master/并发模型.md) 43 | ![并发模型](https://github.com/linyacool/WebServer/blob/master/datum/model.png) 44 | 45 | ## 代码统计 46 | 47 | ![cloc](https://github.com/linyacool/WebServer/blob/master/datum/cloc.png) 48 | 49 | 50 | ## Others 51 | 除了项目基本的代码,进服务器进行压测时,对开源测试工具Webbench增加了Keep-Alive选项和测试功能: 改写后的[Webbench](https://github.com/linyacool/WebBench) 52 | 53 | -------------------------------------------------------------------------------- /WebBench/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS?= -Wall -ggdb -W -O 2 | CC?= gcc 3 | LIBS?= 4 | LDFLAGS?= 5 | PREFIX?= /usr/local/webbench 6 | VERSION=1.5 7 | TMPDIR=/tmp/webbench-$(VERSION) 8 | 9 | all: webbench tags 10 | 11 | tags: *.c 12 | -ctags *.c 13 | 14 | install: webbench 15 | install -d $(DESTDIR)$(PREFIX)/bin 16 | install -s webbench $(DESTDIR)$(PREFIX)/bin 17 | ln -sf $(DESTDIR)$(PREFIX)/bin/webbench $(DESTDIR)/usr/local/bin/webbench 18 | 19 | install -d $(DESTDIR)/usr/local/man/man1 20 | install -d $(DESTDIR)$(PREFIX)/man/man1 21 | install -m 644 webbench.1 $(DESTDIR)$(PREFIX)/man/man1 22 | ln -sf $(DESTDIR)$(PREFIX)/man/man1/webbench.1 $(DESTDIR)/usr/local/man/man1/webbench.1 23 | 24 | install -d $(DESTDIR)$(PREFIX)/share/doc/webbench 25 | install -m 644 debian/copyright $(DESTDIR)$(PREFIX)/share/doc/webbench 26 | install -m 644 debian/changelog $(DESTDIR)$(PREFIX)/share/doc/webbench 27 | 28 | webbench: webbench.o Makefile 29 | $(CC) $(CFLAGS) $(LDFLAGS) -o webbench webbench.o $(LIBS) 30 | 31 | clean: 32 | -rm -f *.o webbench *~ core *.core tags 33 | 34 | tar: clean 35 | -debian/rules clean 36 | rm -rf $(TMPDIR) 37 | install -d $(TMPDIR) 38 | cp -p Makefile webbench.c socket.c webbench.1 $(TMPDIR) 39 | install -d $(TMPDIR)/debian 40 | -cp -p debian/* $(TMPDIR)/debian 41 | ln -sf debian/copyright $(TMPDIR)/COPYRIGHT 42 | ln -sf debian/changelog $(TMPDIR)/ChangeLog 43 | -cd $(TMPDIR) && cd .. && tar cozf webbench-$(VERSION).tar.gz webbench-$(VERSION) 44 | 45 | webbench.o: webbench.c socket.c Makefile 46 | 47 | .PHONY: clean install all tar 48 | -------------------------------------------------------------------------------- /WebBench/README.md: -------------------------------------------------------------------------------- 1 | # WebBench 2 | 3 | Webbench是一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能,最多可以模拟3万个并发连接去测试网站的负载能力。 4 | 5 | ## 依赖 6 | ctags 7 | 8 | ## 使用: 9 | 10 | sudo make && sudo make install PREFIX=your_path_to_webbench 11 | 12 | ## 命令行选项: 13 | 14 | 15 | 16 | 17 | | 短参 | 长参数 | 作用 | 18 | | ------------- |:-------------:| -----:| 19 | |-f |--force |不需要等待服务器响应 | 20 | |-r |--reload |发送重新加载请求 | 21 | |-t |--time |运行多长时间,单位:秒" | 22 | |-p |--proxy |使用代理服务器来发送请求 | 23 | |-c |--clients |创建多少个客户端,默认1个" | 24 | |-9 |--http09 |使用 HTTP/0.9 | 25 | |-1 |--http10 |使用 HTTP/1.0 协议 | 26 | |-2 |--http11 |使用 HTTP/1.1 协议 | 27 | | |--get |使用 GET请求方法 | 28 | | |--head |使用 HEAD请求方法 | 29 | | |--options |使用 OPTIONS请求方法 | 30 | | |--trace |使用 TRACE请求方法 | 31 | |-?/-h |--help |打印帮助信息 | 32 | |-V |--version |显示版本号 | 33 | -------------------------------------------------------------------------------- /WebBench/bin/webbench: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/WebBench/bin/webbench -------------------------------------------------------------------------------- /WebBench/build.sh: -------------------------------------------------------------------------------- 1 | sudo make && sudo make install PREFIX=. -------------------------------------------------------------------------------- /WebBench/debian/changelog: -------------------------------------------------------------------------------- 1 | webbench (1.5) unstable; urgency=low 2 | 3 | * allow building with both Gnu and BSD make 4 | 5 | -- Radim Kolar Fri, Jun 25 12:00:20 CEST 2004 6 | 7 | webbench (1.4) unstable; urgency=low 8 | 9 | * check if url is not too long 10 | * report correct program version number 11 | * use yield() when waiting for test start 12 | * corrected error codes 13 | * check availability of test server first 14 | * do not abort test if first request failed 15 | * report when some childrens are dead. 16 | * use alarm, not time() for lower syscal use by bench 17 | * use mode 644 for installed doc 18 | * makefile cleaned for better freebsd ports integration 19 | 20 | -- Radim Kolar Thu, 15 Jan 2004 11:15:52 +0100 21 | 22 | webbench (1.3) unstable; urgency=low 23 | 24 | * Build fixes for freeBSD 25 | * Default benchmark time 60 -> 30 26 | * generate tar with subdirectory 27 | * added to freeBSD ports collection 28 | 29 | -- Radim Kolar Mon, 12 Jan 2004 17:00:24 +0100 30 | 31 | webbench (1.2) unstable; urgency=low 32 | 33 | * Only debian-related bugfixes 34 | * Updated Debian/rules 35 | * Adapted to fit new directory system 36 | * moved from debstd to dh_* 37 | 38 | -- Radim Kolar Fri, 18 Jan 2002 12:33:04 +0100 39 | 40 | webbench (1.1) unstable; urgency=medium 41 | 42 | * Program debianized 43 | * added support for multiple methods (GET, HEAD, OPTIONS, TRACE) 44 | * added support for multiple HTTP versions (0.9 -- 1.1) 45 | * added long options 46 | * added multiple clients 47 | * wait for start of second before test 48 | * test time can be specified 49 | * better error checking when reading reply from server 50 | * FIX: tests was one second longer than expected 51 | 52 | -- Radim Kolar Thu, 16 Sep 1999 18:48:00 +0200 53 | 54 | Local variables: 55 | mode: debian-changelog 56 | End: 57 | -------------------------------------------------------------------------------- /WebBench/debian/control: -------------------------------------------------------------------------------- 1 | Source: webbench 2 | Section: web 3 | Priority: extra 4 | Maintainer: Radim Kolar 5 | Build-Depends: debhelper (>> 3.0.0) 6 | Standards-Version: 3.5.2 7 | 8 | Package: webbench 9 | Architecture: any 10 | Depends: ${shlibs:Depends} 11 | Description: Simple forking Web benchmark 12 | webbench is very simple program for benchmarking WWW or Proxy servers. 13 | Uses fork() for simulating multiple clients load. Can use HTTP 0.9 - 1.1 14 | requests, but Keep-Alive connections are not supported. 15 | -------------------------------------------------------------------------------- /WebBench/debian/copyright: -------------------------------------------------------------------------------- 1 | Webbench was written by Radim Kolar 1997-2004 (hsn@netmag.cz). 2 | 3 | UNIX sockets code (socket.c) taken from popclient 1.5 4/1/94 4 | public domain code, created by Virginia Tech Computing Center. 5 | 6 | Copyright: GPL (see /usr/share/common-licenses/GPL) 7 | -------------------------------------------------------------------------------- /WebBench/debian/dirs: -------------------------------------------------------------------------------- 1 | usr/bin -------------------------------------------------------------------------------- /WebBench/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Sample debian/rules that uses debhelper. 3 | # GNU copyright 1997 to 1999 by Joey Hess. 4 | 5 | # Uncomment this to turn on verbose mode. 6 | #export DH_VERBOSE=1 7 | 8 | # This is the debhelper compatability version to use. 9 | export DH_COMPAT=3 10 | 11 | configure: configure-stamp 12 | configure-stamp: 13 | dh_testdir 14 | touch configure-stamp 15 | 16 | build: configure-stamp build-stamp 17 | build-stamp: 18 | dh_testdir 19 | $(MAKE) 20 | touch build-stamp 21 | 22 | clean: 23 | dh_testdir 24 | rm -f build-stamp configure-stamp 25 | 26 | # Add here commands to clean up after the build process. 27 | -$(MAKE) clean 28 | 29 | dh_clean 30 | 31 | install: build 32 | dh_testdir 33 | dh_testroot 34 | dh_clean -k 35 | dh_installdirs 36 | 37 | # Add here commands to install the package into debian/webbench. 38 | $(MAKE) install DESTDIR=$(CURDIR)/debian/webbench 39 | 40 | 41 | # Build architecture-independent files here. 42 | binary-indep: build install 43 | # We have nothing to do by default. 44 | 45 | # Build architecture-dependent files here. 46 | binary-arch: build install 47 | dh_testdir 48 | dh_testroot 49 | dh_installdocs 50 | dh_installman webbench.1 51 | dh_installchangelogs 52 | dh_link 53 | dh_strip 54 | dh_compress 55 | dh_fixperms 56 | # dh_makeshlibs 57 | dh_installdeb 58 | dh_shlibdeps 59 | dh_gencontrol 60 | dh_md5sums 61 | dh_builddeb 62 | 63 | binary: binary-indep binary-arch 64 | .PHONY: build clean binary-indep binary-arch binary install configure 65 | -------------------------------------------------------------------------------- /WebBench/man/man1/webbench.1: -------------------------------------------------------------------------------- 1 | -.TH WEBBENCH 1 "14 Jan 2004" 2 | -.\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection 3 | -.\" other parms are allowed: see man(7), man(1) 4 | -.SH NAME 5 | -webbench \- simple forking web benchmark 6 | -.SH SYNOPSIS 7 | -.B webbench 8 | -.I "[options] URL" 9 | -.br 10 | -.SH "AUTHOR" 11 | -This program and manual page was written by Radim Kolar, 12 | -for the 13 | -.B Supreme Personality of Godhead 14 | -(but may be used by others). 15 | -.SH "DESCRIPTION" 16 | -.B webbench 17 | -is simple program for benchmarking HTTP servers or any 18 | -other servers, which can be accessed via HTTP proxy. Unlike others 19 | -benchmarks, 20 | -.B webbench 21 | -uses multiple processes for simulating traffic 22 | -generated by multiple users. This allows better operating 23 | -on SMP systems and on systems with slow or buggy implementation 24 | -of select(). 25 | -.SH OPTIONS 26 | -The programs follow the usual GNU command line syntax, with long 27 | -options starting with two dashes (`-'). 28 | -A summary of options are included below. 29 | -.TP 30 | -.B \-?, \-h, \-\-help 31 | -Show summary of options. 32 | -.TP 33 | -.B \-v, \-\-version 34 | -Show version of program. 35 | -.TP 36 | -.B \-f, \-\-force 37 | -Do not wait for any response from server. Close connection after 38 | -request is send. This option produce quite a good denial of service 39 | -attack. 40 | -.TP 41 | -.B \-9, \-\-http09 42 | -Use HTTP/0.9 protocol, if possible. 43 | -.TP 44 | -.B \-1, \-\-http10 45 | -Use HTTP/1.0 protocol, if possible. 46 | -.TP 47 | -.B \-2, \-\-http11 48 | -Use HTTP/1.1 protocol (without 49 | -.I Keep-Alive 50 | -), if possible. 51 | -.TP 52 | -.B \-r, \-\-reload 53 | -Forces proxy to reload document. If proxy is not 54 | -set, option has no effect. 55 | -.TP 56 | -.B \-t, \-\-time 57 | -Run benchmark for 58 | -.I 59 | -seconds. Default value is 30. 60 | -.TP 61 | -.B \-p, \-\-proxy 62 | -Send request via proxy server. Needed for supporting others protocols 63 | -than HTTP. 64 | -.TP 65 | -.B \-\-get 66 | -Use GET request method. 67 | -.TP 68 | -.B \-\-head 69 | -Use HEAD request method. 70 | -.TP 71 | -.B \-\-options 72 | -Use OPTIONS request method. 73 | -.TP 74 | -.B \-\-trace 75 | -Use TRACE request method. 76 | -.TP 77 | -.B \-c, \-\-clients 78 | -Use 79 | -.I 80 | -multiple clients for benchmark. Default value 81 | -is 1. 82 | -.SH "EXIT STATUS" 83 | -.TP 84 | -0 - sucess 85 | -.TP 86 | -1 - benchmark failed, can not connect to server 87 | -.TP 88 | -2 - bad command line argument(s) 89 | -.TP 90 | -3 - internal error, i.e. fork failed 91 | -.SH "TODO" 92 | -Include support for using 93 | -.I Keep-Alive 94 | -HTTP/1.1 connections. 95 | -.SH "COPYING" 96 | -Webbench is distributed under GPL. Copyright 1997-2004 97 | -Radim Kolar (hsn@netmag.cz). 98 | -UNIX sockets code taken from popclient 1.5 4/1/94 99 | -public domain code, created by Virginia Tech Computing Center. 100 | -.BR 101 | -This man page is public domain. 102 | -------------------------------------------------------------------------------- /WebBench/share/doc/webbench/changelog: -------------------------------------------------------------------------------- 1 | webbench (1.5) unstable; urgency=low 2 | 3 | * allow building with both Gnu and BSD make 4 | 5 | -- Radim Kolar Fri, Jun 25 12:00:20 CEST 2004 6 | 7 | webbench (1.4) unstable; urgency=low 8 | 9 | * check if url is not too long 10 | * report correct program version number 11 | * use yield() when waiting for test start 12 | * corrected error codes 13 | * check availability of test server first 14 | * do not abort test if first request failed 15 | * report when some childrens are dead. 16 | * use alarm, not time() for lower syscal use by bench 17 | * use mode 644 for installed doc 18 | * makefile cleaned for better freebsd ports integration 19 | 20 | -- Radim Kolar Thu, 15 Jan 2004 11:15:52 +0100 21 | 22 | webbench (1.3) unstable; urgency=low 23 | 24 | * Build fixes for freeBSD 25 | * Default benchmark time 60 -> 30 26 | * generate tar with subdirectory 27 | * added to freeBSD ports collection 28 | 29 | -- Radim Kolar Mon, 12 Jan 2004 17:00:24 +0100 30 | 31 | webbench (1.2) unstable; urgency=low 32 | 33 | * Only debian-related bugfixes 34 | * Updated Debian/rules 35 | * Adapted to fit new directory system 36 | * moved from debstd to dh_* 37 | 38 | -- Radim Kolar Fri, 18 Jan 2002 12:33:04 +0100 39 | 40 | webbench (1.1) unstable; urgency=medium 41 | 42 | * Program debianized 43 | * added support for multiple methods (GET, HEAD, OPTIONS, TRACE) 44 | * added support for multiple HTTP versions (0.9 -- 1.1) 45 | * added long options 46 | * added multiple clients 47 | * wait for start of second before test 48 | * test time can be specified 49 | * better error checking when reading reply from server 50 | * FIX: tests was one second longer than expected 51 | 52 | -- Radim Kolar Thu, 16 Sep 1999 18:48:00 +0200 53 | 54 | Local variables: 55 | mode: debian-changelog 56 | End: 57 | -------------------------------------------------------------------------------- /WebBench/share/doc/webbench/copyright: -------------------------------------------------------------------------------- 1 | Webbench was written by Radim Kolar 1997-2004 (hsn@netmag.cz). 2 | 3 | UNIX sockets code (socket.c) taken from popclient 1.5 4/1/94 4 | public domain code, created by Virginia Tech Computing Center. 5 | 6 | Copyright: GPL (see /usr/share/common-licenses/GPL) 7 | -------------------------------------------------------------------------------- /WebBench/socket.c: -------------------------------------------------------------------------------- 1 | /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $ 2 | * 3 | * This module has been modified by Radim Kolar for OS/2 emx 4 | */ 5 | 6 | /*********************************************************************** 7 | module: socket.c 8 | program: popclient 9 | SCCS ID: @(#)socket.c 1.5 4/1/94 10 | programmer: Virginia Tech Computing Center 11 | compiler: DEC RISC C compiler (Ultrix 4.1) 12 | environment: DEC Ultrix 4.3 13 | description: UNIX sockets code. 14 | ***********************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | int Socket(const char *host, int clientPort) 30 | { 31 | int sock; 32 | unsigned long inaddr; 33 | struct sockaddr_in ad; 34 | struct hostent *hp; 35 | 36 | memset(&ad, 0, sizeof(ad)); 37 | ad.sin_family = AF_INET; 38 | 39 | inaddr = inet_addr(host); 40 | if (inaddr != INADDR_NONE) 41 | memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); 42 | else 43 | { 44 | hp = gethostbyname(host); 45 | if (hp == NULL) 46 | return -1; 47 | memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); 48 | } 49 | ad.sin_port = htons(clientPort); 50 | 51 | sock = socket(AF_INET, SOCK_STREAM, 0); 52 | 53 | 54 | if (sock < 0) 55 | return sock; 56 | 57 | // int optval = 1; 58 | // if(setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) 59 | // return -1; 60 | if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0) 61 | { 62 | close(sock); 63 | return -1; 64 | } 65 | 66 | return sock; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /WebBench/test.sh: -------------------------------------------------------------------------------- 1 | ./bin/webbench -t 60 -c 1000 -2 --get http://127.0.0.1:80/hello -------------------------------------------------------------------------------- /WebBench/webbench: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/WebBench/webbench -------------------------------------------------------------------------------- /WebBench/webbench.1: -------------------------------------------------------------------------------- 1 | -.TH WEBBENCH 1 "14 Jan 2004" 2 | -.\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection 3 | -.\" other parms are allowed: see man(7), man(1) 4 | -.SH NAME 5 | -webbench \- simple forking web benchmark 6 | -.SH SYNOPSIS 7 | -.B webbench 8 | -.I "[options] URL" 9 | -.br 10 | -.SH "AUTHOR" 11 | -This program and manual page was written by Radim Kolar, 12 | -for the 13 | -.B Supreme Personality of Godhead 14 | -(but may be used by others). 15 | -.SH "DESCRIPTION" 16 | -.B webbench 17 | -is simple program for benchmarking HTTP servers or any 18 | -other servers, which can be accessed via HTTP proxy. Unlike others 19 | -benchmarks, 20 | -.B webbench 21 | -uses multiple processes for simulating traffic 22 | -generated by multiple users. This allows better operating 23 | -on SMP systems and on systems with slow or buggy implementation 24 | -of select(). 25 | -.SH OPTIONS 26 | -The programs follow the usual GNU command line syntax, with long 27 | -options starting with two dashes (`-'). 28 | -A summary of options are included below. 29 | -.TP 30 | -.B \-?, \-h, \-\-help 31 | -Show summary of options. 32 | -.TP 33 | -.B \-v, \-\-version 34 | -Show version of program. 35 | -.TP 36 | -.B \-f, \-\-force 37 | -Do not wait for any response from server. Close connection after 38 | -request is send. This option produce quite a good denial of service 39 | -attack. 40 | -.TP 41 | -.B \-9, \-\-http09 42 | -Use HTTP/0.9 protocol, if possible. 43 | -.TP 44 | -.B \-1, \-\-http10 45 | -Use HTTP/1.0 protocol, if possible. 46 | -.TP 47 | -.B \-2, \-\-http11 48 | -Use HTTP/1.1 protocol (without 49 | -.I Keep-Alive 50 | -), if possible. 51 | -.TP 52 | -.B \-r, \-\-reload 53 | -Forces proxy to reload document. If proxy is not 54 | -set, option has no effect. 55 | -.TP 56 | -.B \-t, \-\-time 57 | -Run benchmark for 58 | -.I 59 | -seconds. Default value is 30. 60 | -.TP 61 | -.B \-p, \-\-proxy 62 | -Send request via proxy server. Needed for supporting others protocols 63 | -than HTTP. 64 | -.TP 65 | -.B \-\-get 66 | -Use GET request method. 67 | -.TP 68 | -.B \-\-head 69 | -Use HEAD request method. 70 | -.TP 71 | -.B \-\-options 72 | -Use OPTIONS request method. 73 | -.TP 74 | -.B \-\-trace 75 | -Use TRACE request method. 76 | -.TP 77 | -.B \-c, \-\-clients 78 | -Use 79 | -.I 80 | -multiple clients for benchmark. Default value 81 | -is 1. 82 | -.SH "EXIT STATUS" 83 | -.TP 84 | -0 - sucess 85 | -.TP 86 | -1 - benchmark failed, can not connect to server 87 | -.TP 88 | -2 - bad command line argument(s) 89 | -.TP 90 | -3 - internal error, i.e. fork failed 91 | -.SH "TODO" 92 | -Include support for using 93 | -.I Keep-Alive 94 | -HTTP/1.1 connections. 95 | -.SH "COPYING" 96 | -Webbench is distributed under GPL. Copyright 1997-2004 97 | -Radim Kolar (hsn@netmag.cz). 98 | -UNIX sockets code taken from popclient 1.5 4/1/94 99 | -public domain code, created by Virginia Tech Computing Center. 100 | -.BR 101 | -This man page is public domain. 102 | -------------------------------------------------------------------------------- /WebServer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SRCS 2 | Channel.cpp 3 | Epoll.cpp 4 | EventLoop.cpp 5 | EventLoopThread.cpp 6 | EventLoopThreadPool.cpp 7 | HttpData.cpp 8 | Main.cpp 9 | Server.cpp 10 | #ThreadPool.cpp 11 | Timer.cpp 12 | Util.cpp 13 | ) 14 | include_directories(${PROJECT_SOURCE_DIR}/base) 15 | 16 | 17 | add_executable(WebServer ${SRCS}) 18 | target_link_libraries(WebServer libserver_base) 19 | 20 | 21 | add_subdirectory(base) 22 | add_subdirectory(tests) -------------------------------------------------------------------------------- /WebServer/Channel.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "Channel.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "Epoll.h" 12 | #include "EventLoop.h" 13 | #include "Util.h" 14 | 15 | using namespace std; 16 | 17 | Channel::Channel(EventLoop *loop) 18 | : loop_(loop), events_(0), lastEvents_(0), fd_(0) {} 19 | 20 | Channel::Channel(EventLoop *loop, int fd) 21 | : loop_(loop), fd_(fd), events_(0), lastEvents_(0) {} 22 | 23 | Channel::~Channel() { 24 | // loop_->poller_->epoll_del(fd, events_); 25 | // close(fd_); 26 | } 27 | 28 | int Channel::getFd() { return fd_; } 29 | void Channel::setFd(int fd) { fd_ = fd; } 30 | 31 | void Channel::handleRead() { 32 | if (readHandler_) { 33 | readHandler_(); 34 | } 35 | } 36 | 37 | void Channel::handleWrite() { 38 | if (writeHandler_) { 39 | writeHandler_(); 40 | } 41 | } 42 | 43 | void Channel::handleConn() { 44 | if (connHandler_) { 45 | connHandler_(); 46 | } 47 | } -------------------------------------------------------------------------------- /WebServer/Channel.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "Timer.h" 11 | 12 | class EventLoop; 13 | class HttpData; 14 | 15 | class Channel { 16 | private: 17 | typedef std::function CallBack; 18 | EventLoop *loop_; 19 | int fd_; 20 | __uint32_t events_; 21 | __uint32_t revents_; 22 | __uint32_t lastEvents_; 23 | 24 | // 方便找到上层持有该Channel的对象 25 | std::weak_ptr holder_; 26 | 27 | private: 28 | int parse_URI(); 29 | int parse_Headers(); 30 | int analysisRequest(); 31 | 32 | CallBack readHandler_; 33 | CallBack writeHandler_; 34 | CallBack errorHandler_; 35 | CallBack connHandler_; 36 | 37 | public: 38 | Channel(EventLoop *loop); 39 | Channel(EventLoop *loop, int fd); 40 | ~Channel(); 41 | int getFd(); 42 | void setFd(int fd); 43 | 44 | void setHolder(std::shared_ptr holder) { holder_ = holder; } 45 | std::shared_ptr getHolder() { 46 | std::shared_ptr ret(holder_.lock()); 47 | return ret; 48 | } 49 | 50 | void setReadHandler(CallBack &&readHandler) { readHandler_ = readHandler; } 51 | void setWriteHandler(CallBack &&writeHandler) { 52 | writeHandler_ = writeHandler; 53 | } 54 | void setErrorHandler(CallBack &&errorHandler) { 55 | errorHandler_ = errorHandler; 56 | } 57 | void setConnHandler(CallBack &&connHandler) { connHandler_ = connHandler; } 58 | 59 | void handleEvents() { 60 | events_ = 0; 61 | if ((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)) { 62 | events_ = 0; 63 | return; 64 | } 65 | if (revents_ & EPOLLERR) { 66 | if (errorHandler_) errorHandler_(); 67 | events_ = 0; 68 | return; 69 | } 70 | if (revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) { 71 | handleRead(); 72 | } 73 | if (revents_ & EPOLLOUT) { 74 | handleWrite(); 75 | } 76 | handleConn(); 77 | } 78 | void handleRead(); 79 | void handleWrite(); 80 | void handleError(int fd, int err_num, std::string short_msg); 81 | void handleConn(); 82 | 83 | void setRevents(__uint32_t ev) { revents_ = ev; } 84 | 85 | void setEvents(__uint32_t ev) { events_ = ev; } 86 | __uint32_t &getEvents() { return events_; } 87 | 88 | bool EqualAndUpdateLastEvents() { 89 | bool ret = (lastEvents_ == events_); 90 | lastEvents_ = events_; 91 | return ret; 92 | } 93 | 94 | __uint32_t getLastEvents() { return lastEvents_; } 95 | }; 96 | 97 | typedef std::shared_ptr SP_Channel; -------------------------------------------------------------------------------- /WebServer/Epoll.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Channel.h" 9 | #include "HttpData.h" 10 | #include "Timer.h" 11 | 12 | 13 | class Epoll { 14 | public: 15 | Epoll(); 16 | ~Epoll(); 17 | void epoll_add(SP_Channel request, int timeout); 18 | void epoll_mod(SP_Channel request, int timeout); 19 | void epoll_del(SP_Channel request); 20 | std::vector> poll(); 21 | std::vector> getEventsRequest(int events_num); 22 | void add_timer(std::shared_ptr request_data, int timeout); 23 | int getEpollFd() { return epollFd_; } 24 | void handleExpired(); 25 | 26 | private: 27 | static const int MAXFDS = 100000; 28 | int epollFd_; 29 | std::vector events_; 30 | std::shared_ptr fd2chan_[MAXFDS]; 31 | std::shared_ptr fd2http_[MAXFDS]; 32 | TimerManager timerManager_; 33 | }; -------------------------------------------------------------------------------- /WebServer/EventLoop.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include "Channel.h" 8 | #include "Epoll.h" 9 | #include "Util.h" 10 | #include "base/CurrentThread.h" 11 | #include "base/Logging.h" 12 | #include "base/Thread.h" 13 | 14 | 15 | #include 16 | using namespace std; 17 | 18 | class EventLoop { 19 | public: 20 | typedef std::function Functor; 21 | EventLoop(); 22 | ~EventLoop(); 23 | void loop(); 24 | void quit(); 25 | void runInLoop(Functor&& cb); 26 | void queueInLoop(Functor&& cb); 27 | bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); } 28 | void assertInLoopThread() { assert(isInLoopThread()); } 29 | void shutdown(shared_ptr channel) { shutDownWR(channel->getFd()); } 30 | void removeFromPoller(shared_ptr channel) { 31 | // shutDownWR(channel->getFd()); 32 | poller_->epoll_del(channel); 33 | } 34 | void updatePoller(shared_ptr channel, int timeout = 0) { 35 | poller_->epoll_mod(channel, timeout); 36 | } 37 | void addToPoller(shared_ptr channel, int timeout = 0) { 38 | poller_->epoll_add(channel, timeout); 39 | } 40 | 41 | private: 42 | // 声明顺序 wakeupFd_ > pwakeupChannel_ 43 | bool looping_; 44 | shared_ptr poller_; 45 | int wakeupFd_; 46 | bool quit_; 47 | bool eventHandling_; 48 | mutable MutexLock mutex_; 49 | std::vector pendingFunctors_; 50 | bool callingPendingFunctors_; 51 | const pid_t threadId_; 52 | shared_ptr pwakeupChannel_; 53 | 54 | void wakeup(); 55 | void handleRead(); 56 | void doPendingFunctors(); 57 | void handleConn(); 58 | }; 59 | -------------------------------------------------------------------------------- /WebServer/EventLoopThread.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "EventLoopThread.h" 4 | #include 5 | 6 | EventLoopThread::EventLoopThread() 7 | : loop_(NULL), 8 | exiting_(false), 9 | thread_(bind(&EventLoopThread::threadFunc, this), "EventLoopThread"), 10 | mutex_(), 11 | cond_(mutex_) {} 12 | 13 | EventLoopThread::~EventLoopThread() { 14 | exiting_ = true; 15 | if (loop_ != NULL) { 16 | loop_->quit(); 17 | thread_.join(); 18 | } 19 | } 20 | 21 | EventLoop* EventLoopThread::startLoop() { 22 | assert(!thread_.started()); 23 | thread_.start(); 24 | { 25 | MutexLockGuard lock(mutex_); 26 | // 一直等到threadFun在Thread里真正跑起来 27 | while (loop_ == NULL) cond_.wait(); 28 | } 29 | return loop_; 30 | } 31 | 32 | void EventLoopThread::threadFunc() { 33 | EventLoop loop; 34 | 35 | { 36 | MutexLockGuard lock(mutex_); 37 | loop_ = &loop; 38 | cond_.notify(); 39 | } 40 | 41 | loop.loop(); 42 | // assert(exiting_); 43 | loop_ = NULL; 44 | } -------------------------------------------------------------------------------- /WebServer/EventLoopThread.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include "EventLoop.h" 5 | #include "base/Condition.h" 6 | #include "base/MutexLock.h" 7 | #include "base/Thread.h" 8 | #include "base/noncopyable.h" 9 | 10 | 11 | class EventLoopThread : noncopyable { 12 | public: 13 | EventLoopThread(); 14 | ~EventLoopThread(); 15 | EventLoop* startLoop(); 16 | 17 | private: 18 | void threadFunc(); 19 | EventLoop* loop_; 20 | bool exiting_; 21 | Thread thread_; 22 | MutexLock mutex_; 23 | Condition cond_; 24 | }; -------------------------------------------------------------------------------- /WebServer/EventLoopThreadPool.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "EventLoopThreadPool.h" 4 | 5 | EventLoopThreadPool::EventLoopThreadPool(EventLoop *baseLoop, int numThreads) 6 | : baseLoop_(baseLoop), started_(false), numThreads_(numThreads), next_(0) { 7 | if (numThreads_ <= 0) { 8 | LOG << "numThreads_ <= 0"; 9 | abort(); 10 | } 11 | } 12 | 13 | void EventLoopThreadPool::start() { 14 | baseLoop_->assertInLoopThread(); 15 | started_ = true; 16 | for (int i = 0; i < numThreads_; ++i) { 17 | std::shared_ptr t(new EventLoopThread()); 18 | threads_.push_back(t); 19 | loops_.push_back(t->startLoop()); 20 | } 21 | } 22 | 23 | EventLoop *EventLoopThreadPool::getNextLoop() { 24 | baseLoop_->assertInLoopThread(); 25 | assert(started_); 26 | EventLoop *loop = baseLoop_; 27 | if (!loops_.empty()) { 28 | loop = loops_[next_]; 29 | next_ = (next_ + 1) % numThreads_; 30 | } 31 | return loop; 32 | } -------------------------------------------------------------------------------- /WebServer/EventLoopThreadPool.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include "EventLoopThread.h" 7 | #include "base/Logging.h" 8 | #include "base/noncopyable.h" 9 | 10 | 11 | class EventLoopThreadPool : noncopyable { 12 | public: 13 | EventLoopThreadPool(EventLoop* baseLoop, int numThreads); 14 | 15 | ~EventLoopThreadPool() { LOG << "~EventLoopThreadPool()"; } 16 | void start(); 17 | 18 | EventLoop* getNextLoop(); 19 | 20 | private: 21 | EventLoop* baseLoop_; 22 | bool started_; 23 | int numThreads_; 24 | int next_; 25 | std::vector> threads_; 26 | std::vector loops_; 27 | }; -------------------------------------------------------------------------------- /WebServer/HttpData.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "Timer.h" 12 | 13 | 14 | class EventLoop; 15 | class TimerNode; 16 | class Channel; 17 | 18 | enum ProcessState { 19 | STATE_PARSE_URI = 1, 20 | STATE_PARSE_HEADERS, 21 | STATE_RECV_BODY, 22 | STATE_ANALYSIS, 23 | STATE_FINISH 24 | }; 25 | 26 | enum URIState { 27 | PARSE_URI_AGAIN = 1, 28 | PARSE_URI_ERROR, 29 | PARSE_URI_SUCCESS, 30 | }; 31 | 32 | enum HeaderState { 33 | PARSE_HEADER_SUCCESS = 1, 34 | PARSE_HEADER_AGAIN, 35 | PARSE_HEADER_ERROR 36 | }; 37 | 38 | enum AnalysisState { ANALYSIS_SUCCESS = 1, ANALYSIS_ERROR }; 39 | 40 | enum ParseState { 41 | H_START = 0, 42 | H_KEY, 43 | H_COLON, 44 | H_SPACES_AFTER_COLON, 45 | H_VALUE, 46 | H_CR, 47 | H_LF, 48 | H_END_CR, 49 | H_END_LF 50 | }; 51 | 52 | enum ConnectionState { H_CONNECTED = 0, H_DISCONNECTING, H_DISCONNECTED }; 53 | 54 | enum HttpMethod { METHOD_POST = 1, METHOD_GET, METHOD_HEAD }; 55 | 56 | enum HttpVersion { HTTP_10 = 1, HTTP_11 }; 57 | 58 | class MimeType { 59 | private: 60 | static void init(); 61 | static std::unordered_map mime; 62 | MimeType(); 63 | MimeType(const MimeType &m); 64 | 65 | public: 66 | static std::string getMime(const std::string &suffix); 67 | 68 | private: 69 | static pthread_once_t once_control; 70 | }; 71 | 72 | class HttpData : public std::enable_shared_from_this { 73 | public: 74 | HttpData(EventLoop *loop, int connfd); 75 | ~HttpData() { close(fd_); } 76 | void reset(); 77 | void seperateTimer(); 78 | void linkTimer(std::shared_ptr mtimer) { 79 | // shared_ptr重载了bool, 但weak_ptr没有 80 | timer_ = mtimer; 81 | } 82 | std::shared_ptr getChannel() { return channel_; } 83 | EventLoop *getLoop() { return loop_; } 84 | void handleClose(); 85 | void newEvent(); 86 | 87 | private: 88 | EventLoop *loop_; 89 | std::shared_ptr channel_; 90 | int fd_; 91 | std::string inBuffer_; 92 | std::string outBuffer_; 93 | bool error_; 94 | ConnectionState connectionState_; 95 | 96 | HttpMethod method_; 97 | HttpVersion HTTPVersion_; 98 | std::string fileName_; 99 | std::string path_; 100 | int nowReadPos_; 101 | ProcessState state_; 102 | ParseState hState_; 103 | bool keepAlive_; 104 | std::map headers_; 105 | std::weak_ptr timer_; 106 | 107 | void handleRead(); 108 | void handleWrite(); 109 | void handleConn(); 110 | void handleError(int fd, int err_num, std::string short_msg); 111 | URIState parseURI(); 112 | HeaderState parseHeaders(); 113 | AnalysisState analysisRequest(); 114 | }; -------------------------------------------------------------------------------- /WebServer/Main.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include 4 | #include 5 | #include "EventLoop.h" 6 | #include "Server.h" 7 | #include "base/Logging.h" 8 | 9 | 10 | int main(int argc, char *argv[]) { 11 | int threadNum = 4; 12 | int port = 80; 13 | std::string logPath = "./WebServer.log"; 14 | 15 | // parse args 16 | int opt; 17 | const char *str = "t:l:p:"; 18 | while ((opt = getopt(argc, argv, str)) != -1) { 19 | switch (opt) { 20 | case 't': { 21 | threadNum = atoi(optarg); 22 | break; 23 | } 24 | case 'l': { 25 | logPath = optarg; 26 | if (logPath.size() < 2 || optarg[0] != '/') { 27 | printf("logPath should start with \"/\"\n"); 28 | abort(); 29 | } 30 | break; 31 | } 32 | case 'p': { 33 | port = atoi(optarg); 34 | break; 35 | } 36 | default: 37 | break; 38 | } 39 | } 40 | Logger::setLogFileName(logPath); 41 | // STL库在多线程上应用 42 | #ifndef _PTHREADS 43 | LOG << "_PTHREADS is not defined !"; 44 | #endif 45 | EventLoop mainLoop; 46 | Server myHTTPServer(&mainLoop, threadNum, port); 47 | myHTTPServer.start(); 48 | mainLoop.loop(); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /WebServer/Makefile: -------------------------------------------------------------------------------- 1 | # MAINSOURCE代表含有main入口函数的cpp文件,因为含有测试代码, 2 | # 所以要为多个目标编译,这里把Makefile写的通用了一点, 3 | # 以后加东西Makefile不用做多少改动 4 | MAINSOURCE := Main.cpp base/tests/LoggingTest.cpp tests/HTTPClient.cpp 5 | # MAINOBJS := $(patsubst %.cpp,%.o,$(MAINSOURCE)) 6 | SOURCE := $(wildcard *.cpp base/*.cpp tests/*.cpp) 7 | override SOURCE := $(filter-out $(MAINSOURCE),$(SOURCE)) 8 | OBJS := $(patsubst %.cpp,%.o,$(SOURCE)) 9 | 10 | TARGET := WebServer 11 | CC := g++ 12 | LIBS := -lpthread 13 | INCLUDE:= -I./usr/local/lib 14 | CFLAGS := -std=c++11 -g -Wall -O3 -D_PTHREADS 15 | CXXFLAGS:= $(CFLAGS) 16 | 17 | # Test object 18 | SUBTARGET1 := LoggingTest 19 | SUBTARGET2 := HTTPClient 20 | 21 | .PHONY : objs clean veryclean rebuild all tests debug 22 | all : $(TARGET) $(SUBTARGET1) $(SUBTARGET2) 23 | objs : $(OBJS) 24 | rebuild: veryclean all 25 | 26 | tests : $(SUBTARGET1) $(SUBTARGET2) 27 | clean : 28 | find . -name '*.o' | xargs rm -f 29 | veryclean : 30 | find . -name '*.o' | xargs rm -f 31 | find . -name $(TARGET) | xargs rm -f 32 | find . -name $(SUBTARGET1) | xargs rm -f 33 | find . -name $(SUBTARGET2) | xargs rm -f 34 | debug: 35 | @echo $(SOURCE) 36 | 37 | $(TARGET) : $(OBJS) Main.o 38 | $(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) 39 | # $@代表目标,这里是$(TARGET) 40 | 41 | $(SUBTARGET1) : $(OBJS) base/tests/LoggingTest.o 42 | $(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) 43 | 44 | $(SUBTARGET2) : $(OBJS) tests/HTTPClient.o 45 | $(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) 46 | -------------------------------------------------------------------------------- /WebServer/Makefile.bak: -------------------------------------------------------------------------------- 1 | # @Author Lin Ya 2 | # @Email xxbbb@vip.qq.com 3 | # MAINSOURCE代表含有main入口函数的cpp文件,因为含有测试代码, 4 | # 所以要为多个目标编译,这里把Makefile写的通用了一点, 5 | # 以后加东西Makefile不用做多少改动 6 | MAINSOURCE := Main.cpp base/tests/LoggingTest.cpp tests/HTTPClient.cpp 7 | # MAINOBJS := $(patsubst %.cpp,%.o,$(MAINSOURCE)) 8 | SOURCE := $(wildcard *.cpp base/*.cpp tests/*.cpp) 9 | override SOURCE := $(filter-out $(MAINSOURCE),$(SOURCE)) 10 | OBJS := $(patsubst %.cpp,%.o,$(SOURCE)) 11 | 12 | TARGET := WebServer 13 | CC := g++ 14 | LIBS := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_imgcodecs 15 | INCLUDE:= -I./usr/local/lib 16 | CFLAGS := -std=c++11 -g -Wall -O3 -D_PTHREADS 17 | CXXFLAGS:= $(CFLAGS) 18 | 19 | # Test object 20 | SUBTARGET1 := LoggingTest 21 | SUBTARGET2 := HTTPClient 22 | 23 | .PHONY : objs clean veryclean rebuild all tests debug 24 | all : $(TARGET) $(SUBTARGET1) $(SUBTARGET2) 25 | objs : $(OBJS) 26 | rebuild: veryclean all 27 | 28 | tests : $(SUBTARGET1) $(SUBTARGET2) 29 | clean : 30 | find . -name '*.o' | xargs rm -f 31 | veryclean : 32 | find . -name '*.o' | xargs rm -f 33 | find . -name $(TARGET) | xargs rm -f 34 | find . -name $(SUBTARGET1) | xargs rm -f 35 | find . -name $(SUBTARGET2) | xargs rm -f 36 | debug: 37 | @echo $(SOURCE) 38 | 39 | $(TARGET) : $(OBJS) Main.o 40 | $(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) 41 | # $@代表目标,这里是$(TARGET) 42 | 43 | $(SUBTARGET1) : $(OBJS) base/tests/LoggingTest.o 44 | $(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) 45 | 46 | $(SUBTARGET2) : $(OBJS) tests/HTTPClient.o 47 | $(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) -------------------------------------------------------------------------------- /WebServer/Server.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "Server.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Util.h" 9 | #include "base/Logging.h" 10 | 11 | Server::Server(EventLoop *loop, int threadNum, int port) 12 | : loop_(loop), 13 | threadNum_(threadNum), 14 | eventLoopThreadPool_(new EventLoopThreadPool(loop_, threadNum)), 15 | started_(false), 16 | acceptChannel_(new Channel(loop_)), 17 | port_(port), 18 | listenFd_(socket_bind_listen(port_)) { 19 | acceptChannel_->setFd(listenFd_); 20 | handle_for_sigpipe(); 21 | if (setSocketNonBlocking(listenFd_) < 0) { 22 | perror("set socket non block failed"); 23 | abort(); 24 | } 25 | } 26 | 27 | void Server::start() { 28 | eventLoopThreadPool_->start(); 29 | // acceptChannel_->setEvents(EPOLLIN | EPOLLET | EPOLLONESHOT); 30 | acceptChannel_->setEvents(EPOLLIN | EPOLLET); 31 | acceptChannel_->setReadHandler(bind(&Server::handNewConn, this)); 32 | acceptChannel_->setConnHandler(bind(&Server::handThisConn, this)); 33 | loop_->addToPoller(acceptChannel_, 0); 34 | started_ = true; 35 | } 36 | 37 | void Server::handNewConn() { 38 | struct sockaddr_in client_addr; 39 | memset(&client_addr, 0, sizeof(struct sockaddr_in)); 40 | socklen_t client_addr_len = sizeof(client_addr); 41 | int accept_fd = 0; 42 | while ((accept_fd = accept(listenFd_, (struct sockaddr *)&client_addr, 43 | &client_addr_len)) > 0) { 44 | EventLoop *loop = eventLoopThreadPool_->getNextLoop(); 45 | LOG << "New connection from " << inet_ntoa(client_addr.sin_addr) << ":" 46 | << ntohs(client_addr.sin_port); 47 | // cout << "new connection" << endl; 48 | // cout << inet_ntoa(client_addr.sin_addr) << endl; 49 | // cout << ntohs(client_addr.sin_port) << endl; 50 | /* 51 | // TCP的保活机制默认是关闭的 52 | int optval = 0; 53 | socklen_t len_optval = 4; 54 | getsockopt(accept_fd, SOL_SOCKET, SO_KEEPALIVE, &optval, &len_optval); 55 | cout << "optval ==" << optval << endl; 56 | */ 57 | // 限制服务器的最大并发连接数 58 | if (accept_fd >= MAXFDS) { 59 | close(accept_fd); 60 | continue; 61 | } 62 | // 设为非阻塞模式 63 | if (setSocketNonBlocking(accept_fd) < 0) { 64 | LOG << "Set non block failed!"; 65 | // perror("Set non block failed!"); 66 | return; 67 | } 68 | 69 | setSocketNodelay(accept_fd); 70 | // setSocketNoLinger(accept_fd); 71 | 72 | shared_ptr req_info(new HttpData(loop, accept_fd)); 73 | req_info->getChannel()->setHolder(req_info); 74 | loop->queueInLoop(std::bind(&HttpData::newEvent, req_info)); 75 | } 76 | acceptChannel_->setEvents(EPOLLIN | EPOLLET); 77 | } -------------------------------------------------------------------------------- /WebServer/Server.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include "Channel.h" 6 | #include "EventLoop.h" 7 | #include "EventLoopThreadPool.h" 8 | 9 | class Server { 10 | public: 11 | Server(EventLoop *loop, int threadNum, int port); 12 | ~Server() {} 13 | EventLoop *getLoop() const { return loop_; } 14 | void start(); 15 | void handNewConn(); 16 | void handThisConn() { loop_->updatePoller(acceptChannel_); } 17 | 18 | private: 19 | EventLoop *loop_; 20 | int threadNum_; 21 | std::unique_ptr eventLoopThreadPool_; 22 | bool started_; 23 | std::shared_ptr acceptChannel_; 24 | int port_; 25 | int listenFd_; 26 | static const int MAXFDS = 100000; 27 | }; -------------------------------------------------------------------------------- /WebServer/ThreadPool.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | 4 | // This file has not been used 5 | #pragma once 6 | #include "Channel.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | const int THREADPOOL_INVALID = -1; 13 | const int THREADPOOL_LOCK_FAILURE = -2; 14 | const int THREADPOOL_QUEUE_FULL = -3; 15 | const int THREADPOOL_SHUTDOWN = -4; 16 | const int THREADPOOL_THREAD_FAILURE = -5; 17 | const int THREADPOOL_GRACEFUL = 1; 18 | 19 | const int MAX_THREADS = 1024; 20 | const int MAX_QUEUE = 65535; 21 | 22 | typedef enum 23 | { 24 | immediate_shutdown = 1, 25 | graceful_shutdown = 2 26 | } ShutDownOption; 27 | 28 | struct ThreadPoolTask 29 | { 30 | std::function)> fun; 31 | std::shared_ptr args; 32 | }; 33 | 34 | 35 | class ThreadPool 36 | { 37 | private: 38 | static pthread_mutex_t lock; 39 | static pthread_cond_t notify; 40 | 41 | static std::vector threads; 42 | static std::vector queue; 43 | static int thread_count; 44 | static int queue_size; 45 | static int head; 46 | // tail 指向尾节点的下一节点 47 | static int tail; 48 | static int count; 49 | static int shutdown; 50 | static int started; 51 | public: 52 | static int threadpool_create(int _thread_count, int _queue_size); 53 | static int threadpool_add(std::shared_ptr args, std::function)> fun); 54 | static int threadpool_destroy(ShutDownOption shutdown_option = graceful_shutdown); 55 | static int threadpool_free(); 56 | static void *threadpool_thread(void *args); 57 | }; 58 | -------------------------------------------------------------------------------- /WebServer/Timer.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "Timer.h" 4 | #include 5 | #include 6 | #include 7 | 8 | TimerNode::TimerNode(std::shared_ptr requestData, int timeout) 9 | : deleted_(false), SPHttpData(requestData) { 10 | struct timeval now; 11 | gettimeofday(&now, NULL); 12 | // 以毫秒计 13 | expiredTime_ = 14 | (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000)) + timeout; 15 | } 16 | 17 | TimerNode::~TimerNode() { 18 | if (SPHttpData) SPHttpData->handleClose(); 19 | } 20 | 21 | TimerNode::TimerNode(TimerNode &tn) 22 | : SPHttpData(tn.SPHttpData), expiredTime_(0) {} 23 | 24 | void TimerNode::update(int timeout) { 25 | struct timeval now; 26 | gettimeofday(&now, NULL); 27 | expiredTime_ = 28 | (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000)) + timeout; 29 | } 30 | 31 | bool TimerNode::isValid() { 32 | struct timeval now; 33 | gettimeofday(&now, NULL); 34 | size_t temp = (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000)); 35 | if (temp < expiredTime_) 36 | return true; 37 | else { 38 | this->setDeleted(); 39 | return false; 40 | } 41 | } 42 | 43 | void TimerNode::clearReq() { 44 | SPHttpData.reset(); 45 | this->setDeleted(); 46 | } 47 | 48 | TimerManager::TimerManager() {} 49 | 50 | TimerManager::~TimerManager() {} 51 | 52 | void TimerManager::addTimer(std::shared_ptr SPHttpData, int timeout) { 53 | SPTimerNode new_node(new TimerNode(SPHttpData, timeout)); 54 | timerNodeQueue.push(new_node); 55 | SPHttpData->linkTimer(new_node); 56 | } 57 | 58 | /* 处理逻辑是这样的~ 59 | 因为(1) 优先队列不支持随机访问 60 | (2) 即使支持,随机删除某节点后破坏了堆的结构,需要重新更新堆结构。 61 | 所以对于被置为deleted的时间节点,会延迟到它(1)超时 或 62 | (2)它前面的节点都被删除时,它才会被删除。 63 | 一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。 64 | 这样做有两个好处: 65 | (1) 第一个好处是不需要遍历优先队列,省时。 66 | (2) 67 | 第二个好处是给超时时间一个容忍的时间,就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除),如果监听的请求在超时后的下一次请求中又一次出现了, 68 | 就不用再重新申请RequestData节点了,这样可以继续重复利用前面的RequestData,减少了一次delete和一次new的时间。 69 | */ 70 | 71 | void TimerManager::handleExpiredEvent() { 72 | // MutexLockGuard locker(lock); 73 | while (!timerNodeQueue.empty()) { 74 | SPTimerNode ptimer_now = timerNodeQueue.top(); 75 | if (ptimer_now->isDeleted()) 76 | timerNodeQueue.pop(); 77 | else if (ptimer_now->isValid() == false) 78 | timerNodeQueue.pop(); 79 | else 80 | break; 81 | } 82 | } -------------------------------------------------------------------------------- /WebServer/Timer.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "HttpData.h" 9 | #include "base/MutexLock.h" 10 | #include "base/noncopyable.h" 11 | 12 | 13 | class HttpData; 14 | 15 | class TimerNode { 16 | public: 17 | TimerNode(std::shared_ptr requestData, int timeout); 18 | ~TimerNode(); 19 | TimerNode(TimerNode &tn); 20 | void update(int timeout); 21 | bool isValid(); 22 | void clearReq(); 23 | void setDeleted() { deleted_ = true; } 24 | bool isDeleted() const { return deleted_; } 25 | size_t getExpTime() const { return expiredTime_; } 26 | 27 | private: 28 | bool deleted_; 29 | size_t expiredTime_; 30 | std::shared_ptr SPHttpData; 31 | }; 32 | 33 | struct TimerCmp { 34 | bool operator()(std::shared_ptr &a, 35 | std::shared_ptr &b) const { 36 | return a->getExpTime() > b->getExpTime(); 37 | } 38 | }; 39 | 40 | class TimerManager { 41 | public: 42 | TimerManager(); 43 | ~TimerManager(); 44 | void addTimer(std::shared_ptr SPHttpData, int timeout); 45 | void handleExpiredEvent(); 46 | 47 | private: 48 | typedef std::shared_ptr SPTimerNode; 49 | std::priority_queue, TimerCmp> 50 | timerNodeQueue; 51 | // MutexLock lock; 52 | }; -------------------------------------------------------------------------------- /WebServer/Util.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | 7 | ssize_t readn(int fd, void *buff, size_t n); 8 | ssize_t readn(int fd, std::string &inBuffer, bool &zero); 9 | ssize_t readn(int fd, std::string &inBuffer); 10 | ssize_t writen(int fd, void *buff, size_t n); 11 | ssize_t writen(int fd, std::string &sbuff); 12 | void handle_for_sigpipe(); 13 | int setSocketNonBlocking(int fd); 14 | void setSocketNodelay(int fd); 15 | void setSocketNoLinger(int fd); 16 | void shutDownWR(int fd); 17 | int socket_bind_listen(int port); -------------------------------------------------------------------------------- /WebServer/base/AsyncLogging.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include "CountDownLatch.h" 8 | #include "LogStream.h" 9 | #include "MutexLock.h" 10 | #include "Thread.h" 11 | #include "noncopyable.h" 12 | 13 | 14 | class AsyncLogging : noncopyable { 15 | public: 16 | AsyncLogging(const std::string basename, int flushInterval = 2); 17 | ~AsyncLogging() { 18 | if (running_) stop(); 19 | } 20 | void append(const char* logline, int len); 21 | 22 | void start() { 23 | running_ = true; 24 | thread_.start(); 25 | latch_.wait(); 26 | } 27 | 28 | void stop() { 29 | running_ = false; 30 | cond_.notify(); 31 | thread_.join(); 32 | } 33 | 34 | private: 35 | void threadFunc(); 36 | typedef FixedBuffer Buffer; 37 | typedef std::vector> BufferVector; 38 | typedef std::shared_ptr BufferPtr; 39 | const int flushInterval_; 40 | bool running_; 41 | std::string basename_; 42 | Thread thread_; 43 | MutexLock mutex_; 44 | Condition cond_; 45 | BufferPtr currentBuffer_; 46 | BufferPtr nextBuffer_; 47 | BufferVector buffers_; 48 | CountDownLatch latch_; 49 | }; -------------------------------------------------------------------------------- /WebServer/base/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LIB_SRC 2 | AsyncLogging.cpp 3 | CountDownLatch.cpp 4 | FileUtil.cpp 5 | LogFile.cpp 6 | Logging.cpp 7 | LogStream.cpp 8 | Thread.cpp 9 | ) 10 | 11 | add_library(libserver_base ${LIB_SRC}) 12 | target_link_libraries(libserver_base pthread rt) 13 | 14 | set_target_properties(libserver_base PROPERTIES OUTPUT_NAME "server_base") 15 | 16 | add_subdirectory(tests) -------------------------------------------------------------------------------- /WebServer/base/Condition.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "MutexLock.h" 10 | #include "noncopyable.h" 11 | 12 | 13 | class Condition : noncopyable { 14 | public: 15 | explicit Condition(MutexLock &_mutex) : mutex(_mutex) { 16 | pthread_cond_init(&cond, NULL); 17 | } 18 | ~Condition() { pthread_cond_destroy(&cond); } 19 | void wait() { pthread_cond_wait(&cond, mutex.get()); } 20 | void notify() { pthread_cond_signal(&cond); } 21 | void notifyAll() { pthread_cond_broadcast(&cond); } 22 | bool waitForSeconds(int seconds) { 23 | struct timespec abstime; 24 | clock_gettime(CLOCK_REALTIME, &abstime); 25 | abstime.tv_sec += static_cast(seconds); 26 | return ETIMEDOUT == pthread_cond_timedwait(&cond, mutex.get(), &abstime); 27 | } 28 | 29 | private: 30 | MutexLock &mutex; 31 | pthread_cond_t cond; 32 | }; -------------------------------------------------------------------------------- /WebServer/base/CountDownLatch.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "CountDownLatch.h" 4 | 5 | CountDownLatch::CountDownLatch(int count) 6 | : mutex_(), condition_(mutex_), count_(count) {} 7 | 8 | void CountDownLatch::wait() { 9 | MutexLockGuard lock(mutex_); 10 | while (count_ > 0) condition_.wait(); 11 | } 12 | 13 | void CountDownLatch::countDown() { 14 | MutexLockGuard lock(mutex_); 15 | --count_; 16 | if (count_ == 0) condition_.notifyAll(); 17 | } -------------------------------------------------------------------------------- /WebServer/base/CountDownLatch.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include "Condition.h" 5 | #include "MutexLock.h" 6 | #include "noncopyable.h" 7 | 8 | // CountDownLatch的主要作用是确保Thread中传进去的func真的启动了以后 9 | // 外层的start才返回 10 | class CountDownLatch : noncopyable { 11 | public: 12 | explicit CountDownLatch(int count); 13 | void wait(); 14 | void countDown(); 15 | 16 | private: 17 | mutable MutexLock mutex_; 18 | Condition condition_; 19 | int count_; 20 | }; -------------------------------------------------------------------------------- /WebServer/base/CurrentThread.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | 6 | namespace CurrentThread { 7 | // internal 8 | extern __thread int t_cachedTid; 9 | extern __thread char t_tidString[32]; 10 | extern __thread int t_tidStringLength; 11 | extern __thread const char* t_threadName; 12 | void cacheTid(); 13 | inline int tid() { 14 | if (__builtin_expect(t_cachedTid == 0, 0)) { 15 | cacheTid(); 16 | } 17 | return t_cachedTid; 18 | } 19 | 20 | inline const char* tidString() // for logging 21 | { 22 | return t_tidString; 23 | } 24 | 25 | inline int tidStringLength() // for logging 26 | { 27 | return t_tidStringLength; 28 | } 29 | 30 | inline const char* name() { return t_threadName; } 31 | } 32 | -------------------------------------------------------------------------------- /WebServer/base/FileUtil.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "FileUtil.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | AppendFile::AppendFile(string filename) : fp_(fopen(filename.c_str(), "ae")) { 14 | // 用户提供缓冲区 15 | setbuffer(fp_, buffer_, sizeof buffer_); 16 | } 17 | 18 | AppendFile::~AppendFile() { fclose(fp_); } 19 | 20 | void AppendFile::append(const char* logline, const size_t len) { 21 | size_t n = this->write(logline, len); 22 | size_t remain = len - n; 23 | while (remain > 0) { 24 | size_t x = this->write(logline + n, remain); 25 | if (x == 0) { 26 | int err = ferror(fp_); 27 | if (err) fprintf(stderr, "AppendFile::append() failed !\n"); 28 | break; 29 | } 30 | n += x; 31 | remain = len - n; 32 | } 33 | } 34 | 35 | void AppendFile::flush() { fflush(fp_); } 36 | 37 | size_t AppendFile::write(const char* logline, size_t len) { 38 | return fwrite_unlocked(logline, 1, len, fp_); 39 | } -------------------------------------------------------------------------------- /WebServer/base/FileUtil.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include "noncopyable.h" 6 | 7 | 8 | class AppendFile : noncopyable { 9 | public: 10 | explicit AppendFile(std::string filename); 11 | ~AppendFile(); 12 | // append 会向文件写 13 | void append(const char *logline, const size_t len); 14 | void flush(); 15 | 16 | private: 17 | size_t write(const char *logline, size_t len); 18 | FILE *fp_; 19 | char buffer_[64 * 1024]; 20 | }; -------------------------------------------------------------------------------- /WebServer/base/LogFile.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "LogFile.h" 4 | #include 5 | #include 6 | #include 7 | #include "FileUtil.h" 8 | 9 | 10 | using namespace std; 11 | 12 | LogFile::LogFile(const string& basename, int flushEveryN) 13 | : basename_(basename), 14 | flushEveryN_(flushEveryN), 15 | count_(0), 16 | mutex_(new MutexLock) { 17 | // assert(basename.find('/') >= 0); 18 | file_.reset(new AppendFile(basename)); 19 | } 20 | 21 | LogFile::~LogFile() {} 22 | 23 | void LogFile::append(const char* logline, int len) { 24 | MutexLockGuard lock(*mutex_); 25 | append_unlocked(logline, len); 26 | } 27 | 28 | void LogFile::flush() { 29 | MutexLockGuard lock(*mutex_); 30 | file_->flush(); 31 | } 32 | 33 | void LogFile::append_unlocked(const char* logline, int len) { 34 | file_->append(logline, len); 35 | ++count_; 36 | if (count_ >= flushEveryN_) { 37 | count_ = 0; 38 | file_->flush(); 39 | } 40 | } -------------------------------------------------------------------------------- /WebServer/base/LogFile.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include "FileUtil.h" 7 | #include "MutexLock.h" 8 | #include "noncopyable.h" 9 | 10 | 11 | // TODO 提供自动归档功能 12 | class LogFile : noncopyable { 13 | public: 14 | // 每被append flushEveryN次,flush一下,会往文件写,只不过,文件也是带缓冲区的 15 | LogFile(const std::string& basename, int flushEveryN = 1024); 16 | ~LogFile(); 17 | 18 | void append(const char* logline, int len); 19 | void flush(); 20 | bool rollFile(); 21 | 22 | private: 23 | void append_unlocked(const char* logline, int len); 24 | 25 | const std::string basename_; 26 | const int flushEveryN_; 27 | 28 | int count_; 29 | std::unique_ptr mutex_; 30 | std::unique_ptr file_; 31 | }; -------------------------------------------------------------------------------- /WebServer/base/LogStream.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "LogStream.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | const char digits[] = "9876543210123456789"; 12 | const char* zero = digits + 9; 13 | 14 | // From muduo 15 | template 16 | size_t convert(char buf[], T value) { 17 | T i = value; 18 | char* p = buf; 19 | 20 | do { 21 | int lsd = static_cast(i % 10); 22 | i /= 10; 23 | *p++ = zero[lsd]; 24 | } while (i != 0); 25 | 26 | if (value < 0) { 27 | *p++ = '-'; 28 | } 29 | *p = '\0'; 30 | std::reverse(buf, p); 31 | 32 | return p - buf; 33 | } 34 | 35 | template class FixedBuffer; 36 | template class FixedBuffer; 37 | 38 | template 39 | void LogStream::formatInteger(T v) { 40 | // buffer容不下kMaxNumericSize个字符的话会被直接丢弃 41 | if (buffer_.avail() >= kMaxNumericSize) { 42 | size_t len = convert(buffer_.current(), v); 43 | buffer_.add(len); 44 | } 45 | } 46 | 47 | LogStream& LogStream::operator<<(short v) { 48 | *this << static_cast(v); 49 | return *this; 50 | } 51 | 52 | LogStream& LogStream::operator<<(unsigned short v) { 53 | *this << static_cast(v); 54 | return *this; 55 | } 56 | 57 | LogStream& LogStream::operator<<(int v) { 58 | formatInteger(v); 59 | return *this; 60 | } 61 | 62 | LogStream& LogStream::operator<<(unsigned int v) { 63 | formatInteger(v); 64 | return *this; 65 | } 66 | 67 | LogStream& LogStream::operator<<(long v) { 68 | formatInteger(v); 69 | return *this; 70 | } 71 | 72 | LogStream& LogStream::operator<<(unsigned long v) { 73 | formatInteger(v); 74 | return *this; 75 | } 76 | 77 | LogStream& LogStream::operator<<(long long v) { 78 | formatInteger(v); 79 | return *this; 80 | } 81 | 82 | LogStream& LogStream::operator<<(unsigned long long v) { 83 | formatInteger(v); 84 | return *this; 85 | } 86 | 87 | LogStream& LogStream::operator<<(double v) { 88 | if (buffer_.avail() >= kMaxNumericSize) { 89 | int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12g", v); 90 | buffer_.add(len); 91 | } 92 | return *this; 93 | } 94 | 95 | LogStream& LogStream::operator<<(long double v) { 96 | if (buffer_.avail() >= kMaxNumericSize) { 97 | int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12Lg", v); 98 | buffer_.add(len); 99 | } 100 | return *this; 101 | } -------------------------------------------------------------------------------- /WebServer/base/LogStream.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include "noncopyable.h" 8 | 9 | class AsyncLogging; 10 | const int kSmallBuffer = 4000; 11 | const int kLargeBuffer = 4000 * 1000; 12 | 13 | template 14 | class FixedBuffer : noncopyable { 15 | public: 16 | FixedBuffer() : cur_(data_) {} 17 | 18 | ~FixedBuffer() {} 19 | 20 | void append(const char* buf, size_t len) { 21 | if (avail() > static_cast(len)) { 22 | memcpy(cur_, buf, len); 23 | cur_ += len; 24 | } 25 | } 26 | 27 | const char* data() const { return data_; } 28 | int length() const { return static_cast(cur_ - data_); } 29 | 30 | char* current() { return cur_; } 31 | int avail() const { return static_cast(end() - cur_); } 32 | void add(size_t len) { cur_ += len; } 33 | 34 | void reset() { cur_ = data_; } 35 | void bzero() { memset(data_, 0, sizeof data_); } 36 | 37 | private: 38 | const char* end() const { return data_ + sizeof data_; } 39 | 40 | char data_[SIZE]; 41 | char* cur_; 42 | }; 43 | 44 | class LogStream : noncopyable { 45 | public: 46 | typedef FixedBuffer Buffer; 47 | 48 | LogStream& operator<<(bool v) { 49 | buffer_.append(v ? "1" : "0", 1); 50 | return *this; 51 | } 52 | 53 | LogStream& operator<<(short); 54 | LogStream& operator<<(unsigned short); 55 | LogStream& operator<<(int); 56 | LogStream& operator<<(unsigned int); 57 | LogStream& operator<<(long); 58 | LogStream& operator<<(unsigned long); 59 | LogStream& operator<<(long long); 60 | LogStream& operator<<(unsigned long long); 61 | 62 | LogStream& operator<<(const void*); 63 | 64 | LogStream& operator<<(float v) { 65 | *this << static_cast(v); 66 | return *this; 67 | } 68 | LogStream& operator<<(double); 69 | LogStream& operator<<(long double); 70 | 71 | LogStream& operator<<(char v) { 72 | buffer_.append(&v, 1); 73 | return *this; 74 | } 75 | 76 | LogStream& operator<<(const char* str) { 77 | if (str) 78 | buffer_.append(str, strlen(str)); 79 | else 80 | buffer_.append("(null)", 6); 81 | return *this; 82 | } 83 | 84 | LogStream& operator<<(const unsigned char* str) { 85 | return operator<<(reinterpret_cast(str)); 86 | } 87 | 88 | LogStream& operator<<(const std::string& v) { 89 | buffer_.append(v.c_str(), v.size()); 90 | return *this; 91 | } 92 | 93 | void append(const char* data, int len) { buffer_.append(data, len); } 94 | const Buffer& buffer() const { return buffer_; } 95 | void resetBuffer() { buffer_.reset(); } 96 | 97 | private: 98 | void staticCheck(); 99 | 100 | template 101 | void formatInteger(T); 102 | 103 | Buffer buffer_; 104 | 105 | static const int kMaxNumericSize = 32; 106 | }; -------------------------------------------------------------------------------- /WebServer/base/Logging.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "Logging.h" 4 | #include "CurrentThread.h" 5 | #include "Thread.h" 6 | #include "AsyncLogging.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | static pthread_once_t once_control_ = PTHREAD_ONCE_INIT; 14 | static AsyncLogging *AsyncLogger_; 15 | 16 | std::string Logger::logFileName_ = "./WebServer.log"; 17 | 18 | void once_init() 19 | { 20 | AsyncLogger_ = new AsyncLogging(Logger::getLogFileName()); 21 | AsyncLogger_->start(); 22 | } 23 | 24 | void output(const char* msg, int len) 25 | { 26 | pthread_once(&once_control_, once_init); 27 | AsyncLogger_->append(msg, len); 28 | } 29 | 30 | Logger::Impl::Impl(const char *fileName, int line) 31 | : stream_(), 32 | line_(line), 33 | basename_(fileName) 34 | { 35 | formatTime(); 36 | } 37 | 38 | void Logger::Impl::formatTime() 39 | { 40 | struct timeval tv; 41 | time_t time; 42 | char str_t[26] = {0}; 43 | gettimeofday (&tv, NULL); 44 | time = tv.tv_sec; 45 | struct tm* p_time = localtime(&time); 46 | strftime(str_t, 26, "%Y-%m-%d %H:%M:%S\n", p_time); 47 | stream_ << str_t; 48 | } 49 | 50 | Logger::Logger(const char *fileName, int line) 51 | : impl_(fileName, line) 52 | { } 53 | 54 | Logger::~Logger() 55 | { 56 | impl_.stream_ << " -- " << impl_.basename_ << ':' << impl_.line_ << '\n'; 57 | const LogStream::Buffer& buf(stream().buffer()); 58 | output(buf.data(), buf.length()); 59 | } -------------------------------------------------------------------------------- /WebServer/base/Logging.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "LogStream.h" 9 | 10 | 11 | class AsyncLogging; 12 | 13 | class Logger { 14 | public: 15 | Logger(const char *fileName, int line); 16 | ~Logger(); 17 | LogStream &stream() { return impl_.stream_; } 18 | 19 | static void setLogFileName(std::string fileName) { logFileName_ = fileName; } 20 | static std::string getLogFileName() { return logFileName_; } 21 | 22 | private: 23 | class Impl { 24 | public: 25 | Impl(const char *fileName, int line); 26 | void formatTime(); 27 | 28 | LogStream stream_; 29 | int line_; 30 | std::string basename_; 31 | }; 32 | Impl impl_; 33 | static std::string logFileName_; 34 | }; 35 | 36 | #define LOG Logger(__FILE__, __LINE__).stream() -------------------------------------------------------------------------------- /WebServer/base/Log的设计.txt: -------------------------------------------------------------------------------- 1 | Log的设计仿照了muduo库的设计,但我写的没那么复杂 2 | https://github.com/chenshuo/muduo 3 | 4 | 与Log相关的类包括FileUtil、LogFile、AsyncLogging、LogStream、Logging。 5 | 其中前4个类每一个类都含有一个append函数,Log的设计也是主要围绕这个append函数展开的。 6 | 7 | FileUtil是最底层的文件类,封装了Log文件的打开、写入并在类析构的时候关闭文件,底层使用了标准IO,该append函数直接向文件写。 8 | LogFile进一步封装了FileUtil,并设置了一个循环次数,没过这么多次就flush一次。 9 | AsyncLogging是核心,它负责启动一个log线程,专门用来将log写入LogFile,应用了“双缓冲技术”,其实有4个以上的缓冲区,但思想是一样的。 10 | AsyncLogging负责(定时到或被填满时)将缓冲区中的数据写入LogFile中。 11 | LogStream主要用来格式化输出,重载了<<运算符,同时也有自己的一块缓冲区,这里缓冲区的存在是为了缓存一行,把多个<<的结果连成一块。 12 | Logging是对外接口,Logging类内涵一个LogStream对象,主要是为了每次打log的时候在log之前和之后加上固定的格式化的信息,比如打log的行、 13 | 文件名等信息。 14 | -------------------------------------------------------------------------------- /WebServer/base/MutexLock.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include "noncopyable.h" 7 | 8 | 9 | class MutexLock : noncopyable { 10 | public: 11 | MutexLock() { pthread_mutex_init(&mutex, NULL); } 12 | ~MutexLock() { 13 | pthread_mutex_lock(&mutex); 14 | pthread_mutex_destroy(&mutex); 15 | } 16 | void lock() { pthread_mutex_lock(&mutex); } 17 | void unlock() { pthread_mutex_unlock(&mutex); } 18 | pthread_mutex_t *get() { return &mutex; } 19 | 20 | private: 21 | pthread_mutex_t mutex; 22 | 23 | // 友元类不受访问权限影响 24 | private: 25 | friend class Condition; 26 | }; 27 | 28 | class MutexLockGuard : noncopyable { 29 | public: 30 | explicit MutexLockGuard(MutexLock &_mutex) : mutex(_mutex) { mutex.lock(); } 31 | ~MutexLockGuard() { mutex.unlock(); } 32 | 33 | private: 34 | MutexLock &mutex; 35 | }; -------------------------------------------------------------------------------- /WebServer/base/Thread.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "Thread.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "CurrentThread.h" 14 | 15 | 16 | #include 17 | using namespace std; 18 | 19 | namespace CurrentThread { 20 | __thread int t_cachedTid = 0; 21 | __thread char t_tidString[32]; 22 | __thread int t_tidStringLength = 6; 23 | __thread const char* t_threadName = "default"; 24 | } 25 | 26 | pid_t gettid() { return static_cast(::syscall(SYS_gettid)); } 27 | 28 | void CurrentThread::cacheTid() { 29 | if (t_cachedTid == 0) { 30 | t_cachedTid = gettid(); 31 | t_tidStringLength = 32 | snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid); 33 | } 34 | } 35 | 36 | // 为了在线程中保留name,tid这些数据 37 | struct ThreadData { 38 | typedef Thread::ThreadFunc ThreadFunc; 39 | ThreadFunc func_; 40 | string name_; 41 | pid_t* tid_; 42 | CountDownLatch* latch_; 43 | 44 | ThreadData(const ThreadFunc& func, const string& name, pid_t* tid, 45 | CountDownLatch* latch) 46 | : func_(func), name_(name), tid_(tid), latch_(latch) {} 47 | 48 | void runInThread() { 49 | *tid_ = CurrentThread::tid(); 50 | tid_ = NULL; 51 | latch_->countDown(); 52 | latch_ = NULL; 53 | 54 | CurrentThread::t_threadName = name_.empty() ? "Thread" : name_.c_str(); 55 | prctl(PR_SET_NAME, CurrentThread::t_threadName); 56 | 57 | func_(); 58 | CurrentThread::t_threadName = "finished"; 59 | } 60 | }; 61 | 62 | void* startThread(void* obj) { 63 | ThreadData* data = static_cast(obj); 64 | data->runInThread(); 65 | delete data; 66 | return NULL; 67 | } 68 | 69 | Thread::Thread(const ThreadFunc& func, const string& n) 70 | : started_(false), 71 | joined_(false), 72 | pthreadId_(0), 73 | tid_(0), 74 | func_(func), 75 | name_(n), 76 | latch_(1) { 77 | setDefaultName(); 78 | } 79 | 80 | Thread::~Thread() { 81 | if (started_ && !joined_) pthread_detach(pthreadId_); 82 | } 83 | 84 | void Thread::setDefaultName() { 85 | if (name_.empty()) { 86 | char buf[32]; 87 | snprintf(buf, sizeof buf, "Thread"); 88 | name_ = buf; 89 | } 90 | } 91 | 92 | void Thread::start() { 93 | assert(!started_); 94 | started_ = true; 95 | ThreadData* data = new ThreadData(func_, name_, &tid_, &latch_); 96 | if (pthread_create(&pthreadId_, NULL, &startThread, data)) { 97 | started_ = false; 98 | delete data; 99 | } else { 100 | latch_.wait(); 101 | assert(tid_ > 0); 102 | } 103 | } 104 | 105 | int Thread::join() { 106 | assert(started_); 107 | assert(!joined_); 108 | joined_ = true; 109 | return pthread_join(pthreadId_, NULL); 110 | } -------------------------------------------------------------------------------- /WebServer/base/Thread.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "CountDownLatch.h" 11 | #include "noncopyable.h" 12 | 13 | class Thread : noncopyable { 14 | public: 15 | typedef std::function ThreadFunc; 16 | explicit Thread(const ThreadFunc&, const std::string& name = std::string()); 17 | ~Thread(); 18 | void start(); 19 | int join(); 20 | bool started() const { return started_; } 21 | pid_t tid() const { return tid_; } 22 | const std::string& name() const { return name_; } 23 | 24 | private: 25 | void setDefaultName(); 26 | bool started_; 27 | bool joined_; 28 | pthread_t pthreadId_; 29 | pid_t tid_; 30 | ThreadFunc func_; 31 | std::string name_; 32 | CountDownLatch latch_; 33 | }; -------------------------------------------------------------------------------- /WebServer/base/noncopyable.h: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #pragma once 4 | 5 | class noncopyable { 6 | protected: 7 | noncopyable() {} 8 | ~noncopyable() {} 9 | 10 | private: 11 | noncopyable(const noncopyable&); 12 | const noncopyable& operator=(const noncopyable&); 13 | }; -------------------------------------------------------------------------------- /WebServer/base/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(LoggingTest LoggingTest.cpp) 2 | target_link_libraries(LoggingTest libserver_base) -------------------------------------------------------------------------------- /WebServer/base/tests/LoggingTest.cpp: -------------------------------------------------------------------------------- 1 | // @Author Lin Ya 2 | // @Email xxbbb@vip.qq.com 3 | #include "../Logging.h" 4 | #include "../Thread.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | void threadFunc() 13 | { 14 | for (int i = 0; i < 100000; ++i) 15 | { 16 | LOG << i; 17 | } 18 | } 19 | 20 | void type_test() 21 | { 22 | // 13 lines 23 | cout << "----------type test-----------" << endl; 24 | LOG << 0; 25 | LOG << 1234567890123; 26 | LOG << 1.0f; 27 | LOG << 3.1415926; 28 | LOG << (short) 1; 29 | LOG << (long long) 1; 30 | LOG << (unsigned int) 1; 31 | LOG << (unsigned long) 1; 32 | LOG << (long double) 1.6555556; 33 | LOG << (unsigned long long) 1; 34 | LOG << 'c'; 35 | LOG << "abcdefg"; 36 | LOG << string("This is a string"); 37 | } 38 | 39 | void stressing_single_thread() 40 | { 41 | // 100000 lines 42 | cout << "----------stressing test single thread-----------" << endl; 43 | for (int i = 0; i < 100000; ++i) 44 | { 45 | LOG << i; 46 | } 47 | } 48 | 49 | void stressing_multi_threads(int threadNum = 4) 50 | { 51 | // threadNum * 100000 lines 52 | cout << "----------stressing test multi thread-----------" << endl; 53 | vector> vsp; 54 | for (int i = 0; i < threadNum; ++i) 55 | { 56 | shared_ptr tmp(new Thread(threadFunc, "testFunc")); 57 | vsp.push_back(tmp); 58 | } 59 | for (int i = 0; i < threadNum; ++i) 60 | { 61 | vsp[i]->start(); 62 | } 63 | sleep(3); 64 | } 65 | 66 | void other() 67 | { 68 | // 1 line 69 | cout << "----------other test-----------" << endl; 70 | LOG << "fddsa" << 'c' << 0 << 3.666 << string("This is a string"); 71 | } 72 | 73 | 74 | int main() 75 | { 76 | // 共500014行 77 | type_test(); 78 | sleep(3); 79 | 80 | stressing_single_thread(); 81 | sleep(3); 82 | 83 | other(); 84 | sleep(3); 85 | 86 | stressing_multi_threads(); 87 | sleep(3); 88 | return 0; 89 | } -------------------------------------------------------------------------------- /WebServer/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | -------------------------------------------------------------------------------- /WebServer/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(HTTPClient HTTPClient.cpp) -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | 5 | SOURCE_DIR=`pwd` 6 | BUILD_DIR=${BUILD_DIR:-../build} 7 | BUILD_TYPE=${BUILD_TYPE:-Debug} 8 | 9 | mkdir -p $BUILD_DIR/$BUILD_TYPE \ 10 | && cd $BUILD_DIR/$BUILD_TYPE \ 11 | && cmake \ 12 | -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ 13 | $SOURCE_DIR \ 14 | && make $* -------------------------------------------------------------------------------- /datum/WebServer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/WebServer.png -------------------------------------------------------------------------------- /datum/WebServerk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/WebServerk.png -------------------------------------------------------------------------------- /datum/cloc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/cloc.png -------------------------------------------------------------------------------- /datum/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/close.png -------------------------------------------------------------------------------- /datum/gdb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/gdb.png -------------------------------------------------------------------------------- /datum/idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/idle.png -------------------------------------------------------------------------------- /datum/keepalive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/keepalive.png -------------------------------------------------------------------------------- /datum/model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/model.png -------------------------------------------------------------------------------- /datum/muduo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/muduo.png -------------------------------------------------------------------------------- /datum/muduok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/datum/muduok.png -------------------------------------------------------------------------------- /old_version/old_version_0.1/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "string": "cpp", 4 | "xiosbase": "cpp", 5 | "streambuf": "cpp", 6 | "xstring": "cpp", 7 | "utility": "cpp", 8 | "queue": "cpp" 9 | } 10 | } -------------------------------------------------------------------------------- /old_version/old_version_0.1/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE := $(wildcard *.cpp) 2 | OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE))) 3 | 4 | TARGET := myserver 5 | CC := g++ 6 | LIBS := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui 7 | INCLUDE:= -I./usr/local/include/opencv 8 | CFLAGS := -std=c++11 -g -Wall -O3 $(INCLUDE) 9 | CXXFLAGS:= $(CFLAGS) 10 | 11 | .PHONY : objs clean veryclean rebuild all 12 | all : $(TARGET) 13 | objs : $(OBJS) 14 | rebuild: veryclean all 15 | clean : 16 | rm -fr *.o 17 | veryclean : clean 18 | rm -rf $(TARGET) 19 | 20 | $(TARGET) : $(OBJS) 21 | $(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) -------------------------------------------------------------------------------- /old_version/old_version_0.1/Makefile1: -------------------------------------------------------------------------------- 1 | cc=g++ -std=c++11 2 | main: main.o requestData.o epoll.o threadpool.o 3 | $(cc) -o main main.o requestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml 4 | main.o: main.cpp 5 | $(cc) -I/usr/local/include/opencv -c main.cpp 6 | requestData.o: requestData.h requestData.cpp 7 | $(cc) -I/usr/local/include/opencv -c requestData.cpp 8 | epoll.o: epoll.h epoll.cpp 9 | $(cc) -I/usr/local/include/opencv -c epoll.cpp 10 | threadpool.o: threadpool.h threadpool.cpp 11 | $(cc) -I/usr/local/include/opencv -c threadpool.cpp 12 | .PHONY: clean 13 | clean: 14 | -rm main *.o 15 | 16 | 17 | 18 | 19 | .PHONY : everything objs clean veryclean rebuild 20 | everything : 21 | $(TARGET) 22 | all : 23 | $(TARGET) 24 | objs : 25 | $(OBJS) 26 | rebuild: 27 | veryclean everything 28 | clean : 29 | rm -fr *.o 30 | veryclean : 31 | clean rm -fr $(TARGET) -------------------------------------------------------------------------------- /old_version/old_version_0.1/epoll.cpp: -------------------------------------------------------------------------------- 1 | #include "epoll.h" 2 | #include 3 | #include 4 | #include "threadpool.h" 5 | 6 | struct epoll_event* events; 7 | 8 | int epoll_init() 9 | { 10 | int epoll_fd = epoll_create(LISTENQ + 1); 11 | if(epoll_fd == -1) 12 | return -1; 13 | //events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * MAXEVENTS); 14 | events = new epoll_event[MAXEVENTS]; 15 | return epoll_fd; 16 | } 17 | 18 | // 注册新描述符 19 | int epoll_add(int epoll_fd, int fd, void *request, __uint32_t events) 20 | { 21 | struct epoll_event event; 22 | event.data.ptr = request; 23 | event.events = events; 24 | //printf("add to epoll %d\n", fd); 25 | if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0) 26 | { 27 | perror("epoll_add error"); 28 | return -1; 29 | } 30 | return 0; 31 | } 32 | 33 | // 修改描述符状态 34 | int epoll_mod(int epoll_fd, int fd, void *request, __uint32_t events) 35 | { 36 | struct epoll_event event; 37 | event.data.ptr = request; 38 | event.events = events; 39 | if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0) 40 | { 41 | perror("epoll_mod error"); 42 | return -1; 43 | } 44 | return 0; 45 | } 46 | 47 | // 从epoll中删除描述符 48 | int epoll_del(int epoll_fd, int fd, void *request, __uint32_t events) 49 | { 50 | struct epoll_event event; 51 | event.data.ptr = request; 52 | event.events = events; 53 | if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event) < 0) 54 | { 55 | perror("epoll_del error"); 56 | return -1; 57 | } 58 | return 0; 59 | } 60 | 61 | // 返回活跃事件数 62 | int my_epoll_wait(int epoll_fd, struct epoll_event* events, int max_events, int timeout) 63 | { 64 | int ret_count = epoll_wait(epoll_fd, events, max_events, timeout); 65 | if (ret_count < 0) 66 | { 67 | perror("epoll wait error"); 68 | } 69 | return ret_count; 70 | } -------------------------------------------------------------------------------- /old_version/old_version_0.1/epoll.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTPOLL 2 | #define EVENTPOLL 3 | #include "requestData.h" 4 | 5 | const int MAXEVENTS = 5000; 6 | const int LISTENQ = 1024; 7 | 8 | int epoll_init(); 9 | int epoll_add(int epoll_fd, int fd, void *request, __uint32_t events); 10 | int epoll_mod(int epoll_fd, int fd, void *request, __uint32_t events); 11 | int epoll_del(int epoll_fd, int fd, void *request, __uint32_t events); 12 | int my_epoll_wait(int epoll_fd, struct epoll_event *events, int max_events, int timeout); 13 | 14 | #endif -------------------------------------------------------------------------------- /old_version/old_version_0.1/improvement.txt: -------------------------------------------------------------------------------- 1 | 1. 指针可以改成shared_ptr,不需要delete。 2 | 2. 想法在某些地方写成单例模式。 3 | 3. readn 和 writen 改成写到不能写后或读到不能读后加入epoll等待,要记录写到哪或读到哪,维护这样一个状态。比较两者的性能差异。 4 | 4. 信号处理部分可以将 epoll_wait 替换为更安全的 epoll_pwait。 5 | 6 | 7 | 踩坑: 8 | 1. 对EPOLLONESHOT的误解,原以为当epoll_wait监听到相应的事件触发后,epoll会把与事件关联的fd从epoll描述符中禁止掉并且彻底删除,实际上并不会删除,man手册上的解释如下: 9 | EPOLLONESHOT (since Linux 2.6.2) 10 | Sets the one-shot behavior for the associated file descriptor. 11 | This means that after an event is pulled out with 12 | epoll_wait(2) the associated file descriptor is internally 13 | disabled and no other events will be reported by the epoll 14 | interface. The user must call epoll_ctl() with EPOLL_CTL_MOD 15 | to rearm the file descriptor with a new event mask. 16 | 另外: 17 | Linux Programmer's Manual 中第六个问题: 18 | Q6 Will closing a file descriptor cause it to be removed from all 19 | epoll sets automatically? 20 | 21 | A6 Yes, but be aware of the following point. A file descriptor is a 22 | reference to an open file description (see open(2)). Whenever a 23 | file descriptor is duplicated via dup(2), dup2(2), fcntl(2) 24 | F_DUPFD, or fork(2), a new file descriptor referring to the same 25 | open file description is created. An open file description con‐ 26 | tinues to exist until all file descriptors referring to it have 27 | been closed. A file descriptor is removed from an epoll set only 28 | after all the file descriptors referring to the underlying open 29 | file description have been closed (or before if the file descrip‐ 30 | tor is explicitly removed using epoll_ctl(2) EPOLL_CTL_DEL). 31 | This means that even after a file descriptor that is part of an 32 | epoll set has been closed, events may be reported for that file 33 | descriptor if other file descriptors referring to the same under‐ 34 | lying file description remain open. 35 | 36 | 当调用close()关闭对应的fd时,会使相应的引用计数减一,只有减到0时,epoll才会真的删掉它,所以,比较安全的做法是: 37 | 先del掉它,再close它(如果不确定close是否真的关闭了这个文件。)。 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ubuntu 配置opencv: 52 | vim /etc/ld.so.conf 53 | 加一行include /usr/local/opencv/* 54 | /sbin/ldconfig –v -------------------------------------------------------------------------------- /old_version/old_version_0.1/index.html: -------------------------------------------------------------------------------- 1 | Hello World ! -------------------------------------------------------------------------------- /old_version/old_version_0.1/requestData.h: -------------------------------------------------------------------------------- 1 | #ifndef REQUESTDATA 2 | #define REQUESTDATA 3 | #include 4 | #include 5 | 6 | const int STATE_PARSE_URI = 1; 7 | const int STATE_PARSE_HEADERS = 2; 8 | const int STATE_RECV_BODY = 3; 9 | const int STATE_ANALYSIS = 4; 10 | const int STATE_FINISH = 5; 11 | 12 | const int MAX_BUFF = 4096; 13 | 14 | // 有请求出现但是读不到数据,可能是Request Aborted, 15 | // 或者来自网络的数据没有达到等原因, 16 | // 对这样的请求尝试超过一定的次数就抛弃 17 | const int AGAIN_MAX_TIMES = 200; 18 | 19 | const int PARSE_URI_AGAIN = -1; 20 | const int PARSE_URI_ERROR = -2; 21 | const int PARSE_URI_SUCCESS = 0; 22 | 23 | const int PARSE_HEADER_AGAIN = -1; 24 | const int PARSE_HEADER_ERROR = -2; 25 | const int PARSE_HEADER_SUCCESS = 0; 26 | 27 | const int ANALYSIS_ERROR = -2; 28 | const int ANALYSIS_SUCCESS = 0; 29 | 30 | const int METHOD_POST = 1; 31 | const int METHOD_GET = 2; 32 | const int HTTP_10 = 1; 33 | const int HTTP_11 = 2; 34 | 35 | const int EPOLL_WAIT_TIME = 500; 36 | 37 | class MimeType 38 | { 39 | private: 40 | static pthread_mutex_t lock; 41 | static std::unordered_map mime; 42 | MimeType(); 43 | MimeType(const MimeType &m); 44 | public: 45 | static std::string getMime(const std::string &suffix); 46 | }; 47 | 48 | enum HeadersState 49 | { 50 | h_start = 0, 51 | h_key, 52 | h_colon, 53 | h_spaces_after_colon, 54 | h_value, 55 | h_CR, 56 | h_LF, 57 | h_end_CR, 58 | h_end_LF 59 | }; 60 | 61 | struct mytimer; 62 | struct requestData; 63 | 64 | struct requestData 65 | { 66 | private: 67 | int againTimes; 68 | std::string path; 69 | int fd; 70 | int epollfd; 71 | // content的内容用完就清 72 | std::string content; 73 | int method; 74 | int HTTPversion; 75 | std::string file_name; 76 | int now_read_pos; 77 | int state; 78 | int h_state; 79 | bool isfinish; 80 | bool keep_alive; 81 | std::unordered_map headers; 82 | mytimer *timer; 83 | 84 | private: 85 | int parse_URI(); 86 | int parse_Headers(); 87 | int analysisRequest(); 88 | 89 | public: 90 | 91 | requestData(); 92 | requestData(int _epollfd, int _fd, std::string _path); 93 | ~requestData(); 94 | void addTimer(mytimer *mtimer); 95 | void reset(); 96 | void seperateTimer(); 97 | int getFd(); 98 | void setFd(int _fd); 99 | void handleRequest(); 100 | void handleError(int fd, int err_num, std::string short_msg); 101 | }; 102 | 103 | struct mytimer 104 | { 105 | bool deleted; 106 | size_t expired_time; 107 | requestData *request_data; 108 | 109 | mytimer(requestData *_request_data, int timeout); 110 | ~mytimer(); 111 | void update(int timeout); 112 | bool isvalid(); 113 | void clearReq(); 114 | void setDeleted(); 115 | bool isDeleted() const; 116 | size_t getExpTime() const; 117 | }; 118 | 119 | struct timerCmp 120 | { 121 | bool operator()(const mytimer *a, const mytimer *b) const; 122 | }; 123 | #endif -------------------------------------------------------------------------------- /old_version/old_version_0.1/threadpool.h: -------------------------------------------------------------------------------- 1 | #ifndef THREADPOOL 2 | #define THREADPOOL 3 | #include "requestData.h" 4 | #include 5 | 6 | const int THREADPOOL_INVALID = -1; 7 | const int THREADPOOL_LOCK_FAILURE = -2; 8 | const int THREADPOOL_QUEUE_FULL = -3; 9 | const int THREADPOOL_SHUTDOWN = -4; 10 | const int THREADPOOL_THREAD_FAILURE = -5; 11 | const int THREADPOOL_GRACEFUL = 1; 12 | 13 | const int MAX_THREADS = 1024; 14 | const int MAX_QUEUE = 65535; 15 | 16 | typedef enum 17 | { 18 | immediate_shutdown = 1, 19 | graceful_shutdown = 2 20 | } threadpool_shutdown_t; 21 | 22 | /** 23 | * @struct threadpool_task 24 | * @brief the work struct 25 | * 26 | * @var function Pointer to the function that will perform the task. 27 | * @var argument Argument to be passed to the function. 28 | */ 29 | 30 | typedef struct { 31 | void (*function)(void *); 32 | void *argument; 33 | } threadpool_task_t; 34 | 35 | /** 36 | * @struct threadpool 37 | * @brief The threadpool struct 38 | * 39 | * @var notify Condition variable to notify worker threads. 40 | * @var threads Array containing worker threads ID. 41 | * @var thread_count Number of threads 42 | * @var queue Array containing the task queue. 43 | * @var queue_size Size of the task queue. 44 | * @var head Index of the first element. 45 | * @var tail Index of the next element. 46 | * @var count Number of pending tasks 47 | * @var shutdown Flag indicating if the pool is shutting down 48 | * @var started Number of started threads 49 | */ 50 | struct threadpool_t 51 | { 52 | pthread_mutex_t lock; 53 | pthread_cond_t notify; 54 | pthread_t *threads; 55 | threadpool_task_t *queue; 56 | int thread_count; 57 | int queue_size; 58 | int head; 59 | int tail; 60 | int count; 61 | int shutdown; 62 | int started; 63 | }; 64 | 65 | threadpool_t *threadpool_create(int thread_count, int queue_size, int flags); 66 | int threadpool_add(threadpool_t *pool, void (*function)(void *), void *argument, int flags); 67 | int threadpool_destroy(threadpool_t *pool, int flags); 68 | int threadpool_free(threadpool_t *pool); 69 | static void *threadpool_thread(void *threadpool); 70 | 71 | #endif -------------------------------------------------------------------------------- /old_version/old_version_0.1/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | ssize_t readn(int fd, void *buff, size_t n) 9 | { 10 | size_t nleft = n; 11 | ssize_t nread = 0; 12 | ssize_t readSum = 0; 13 | char *ptr = (char*)buff; 14 | while (nleft > 0) 15 | { 16 | if ((nread = read(fd, ptr, nleft)) < 0) 17 | { 18 | if (errno == EINTR) 19 | nread = 0; 20 | else if (errno == EAGAIN) 21 | { 22 | return readSum; 23 | } 24 | else 25 | { 26 | return -1; 27 | } 28 | } 29 | else if (nread == 0) 30 | break; 31 | readSum += nread; 32 | nleft -= nread; 33 | ptr += nread; 34 | } 35 | return readSum; 36 | } 37 | 38 | ssize_t writen(int fd, void *buff, size_t n) 39 | { 40 | size_t nleft = n; 41 | ssize_t nwritten = 0; 42 | ssize_t writeSum = 0; 43 | char *ptr = (char*)buff; 44 | while (nleft > 0) 45 | { 46 | if ((nwritten = write(fd, ptr, nleft)) <= 0) 47 | { 48 | if (nwritten < 0) 49 | { 50 | if (errno == EINTR || errno == EAGAIN) 51 | { 52 | nwritten = 0; 53 | continue; 54 | } 55 | else 56 | return -1; 57 | } 58 | } 59 | writeSum += nwritten; 60 | nleft -= nwritten; 61 | ptr += nwritten; 62 | } 63 | return writeSum; 64 | } 65 | 66 | void handle_for_sigpipe() 67 | { 68 | struct sigaction sa; 69 | memset(&sa, '\0', sizeof(sa)); 70 | sa.sa_handler = SIG_IGN; 71 | sa.sa_flags = 0; 72 | if(sigaction(SIGPIPE, &sa, NULL)) 73 | return; 74 | } 75 | 76 | int setSocketNonBlocking(int fd) 77 | { 78 | int flag = fcntl(fd, F_GETFL, 0); 79 | if(flag == -1) 80 | return -1; 81 | 82 | flag |= O_NONBLOCK; 83 | if(fcntl(fd, F_SETFL, flag) == -1) 84 | return -1; 85 | return 0; 86 | } -------------------------------------------------------------------------------- /old_version/old_version_0.1/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL 2 | #define UTIL 3 | #include 4 | 5 | ssize_t readn(int fd, void *buff, size_t n); 6 | ssize_t writen(int fd, void *buff, size_t n); 7 | void handle_for_sigpipe(); 8 | int setSocketNonBlocking(int fd); 9 | 10 | #endif -------------------------------------------------------------------------------- /old_version/old_version_0.2/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE := $(wildcard *.cpp) 2 | OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE))) 3 | 4 | TARGET := myserver 5 | CC := g++ 6 | LIBS := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui 7 | INCLUDE:= -I./usr/local/include/opencv 8 | CFLAGS := -std=c++11 -g -Wall -O3 $(INCLUDE) 9 | CXXFLAGS:= $(CFLAGS) 10 | 11 | .PHONY : objs clean veryclean rebuild all 12 | all : $(TARGET) 13 | objs : $(OBJS) 14 | rebuild: veryclean all 15 | clean : 16 | rm -fr *.o 17 | veryclean : clean 18 | rm -rf $(TARGET) 19 | 20 | $(TARGET) : $(OBJS) 21 | $(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) -------------------------------------------------------------------------------- /old_version/old_version_0.2/Makefile1: -------------------------------------------------------------------------------- 1 | cc=g++ -std=c++11 2 | main: main.o requestData.o epoll.o threadpool.o 3 | $(cc) -o main main.o requestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml 4 | main.o: main.cpp 5 | $(cc) -I/usr/local/include/opencv -c main.cpp 6 | requestData.o: requestData.h requestData.cpp 7 | $(cc) -I/usr/local/include/opencv -c requestData.cpp 8 | epoll.o: epoll.h epoll.cpp 9 | $(cc) -I/usr/local/include/opencv -c epoll.cpp 10 | threadpool.o: threadpool.h threadpool.cpp 11 | $(cc) -I/usr/local/include/opencv -c threadpool.cpp 12 | .PHONY: clean 13 | clean: 14 | -rm main *.o 15 | 16 | 17 | 18 | 19 | .PHONY : everything objs clean veryclean rebuild 20 | everything : 21 | $(TARGET) 22 | all : 23 | $(TARGET) 24 | objs : 25 | $(OBJS) 26 | rebuild: 27 | veryclean everything 28 | clean : 29 | rm -fr *.o 30 | veryclean : 31 | clean rm -fr $(TARGET) -------------------------------------------------------------------------------- /old_version/old_version_0.2/epoll.cpp: -------------------------------------------------------------------------------- 1 | #include "epoll.h" 2 | #include 3 | #include 4 | #include "threadpool.h" 5 | 6 | struct epoll_event* events; 7 | 8 | int epoll_init() 9 | { 10 | int epoll_fd = epoll_create(LISTENQ + 1); 11 | if(epoll_fd == -1) 12 | return -1; 13 | //events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * MAXEVENTS); 14 | events = new epoll_event[MAXEVENTS]; 15 | return epoll_fd; 16 | } 17 | 18 | // 注册新描述符 19 | int epoll_add(int epoll_fd, int fd, void *request, __uint32_t events) 20 | { 21 | struct epoll_event event; 22 | event.data.ptr = request; 23 | event.events = events; 24 | //printf("add to epoll %d\n", fd); 25 | if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0) 26 | { 27 | perror("epoll_add error"); 28 | return -1; 29 | } 30 | return 0; 31 | } 32 | 33 | // 修改描述符状态 34 | int epoll_mod(int epoll_fd, int fd, void *request, __uint32_t events) 35 | { 36 | struct epoll_event event; 37 | event.data.ptr = request; 38 | event.events = events; 39 | if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0) 40 | { 41 | perror("epoll_mod error"); 42 | return -1; 43 | } 44 | return 0; 45 | } 46 | 47 | // 从epoll中删除描述符 48 | int epoll_del(int epoll_fd, int fd, void *request, __uint32_t events) 49 | { 50 | struct epoll_event event; 51 | event.data.ptr = request; 52 | event.events = events; 53 | if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event) < 0) 54 | { 55 | perror("epoll_del error"); 56 | return -1; 57 | } 58 | return 0; 59 | } 60 | 61 | // 返回活跃事件数 62 | int my_epoll_wait(int epoll_fd, struct epoll_event* events, int max_events, int timeout) 63 | { 64 | int ret_count = epoll_wait(epoll_fd, events, max_events, timeout); 65 | if (ret_count < 0) 66 | { 67 | perror("epoll wait error"); 68 | } 69 | return ret_count; 70 | } -------------------------------------------------------------------------------- /old_version/old_version_0.2/epoll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef EVENTPOLL 3 | #define EVENTPOLL 4 | #include "requestData.h" 5 | 6 | const int MAXEVENTS = 5000; 7 | const int LISTENQ = 1024; 8 | 9 | int epoll_init(); 10 | int epoll_add(int epoll_fd, int fd, void *request, __uint32_t events); 11 | int epoll_mod(int epoll_fd, int fd, void *request, __uint32_t events); 12 | int epoll_del(int epoll_fd, int fd, void *request, __uint32_t events); 13 | int my_epoll_wait(int epoll_fd, struct epoll_event *events, int max_events, int timeout); 14 | 15 | #endif -------------------------------------------------------------------------------- /old_version/old_version_0.2/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/old_version/old_version_0.2/favicon.ico -------------------------------------------------------------------------------- /old_version/old_version_0.2/improvement.txt: -------------------------------------------------------------------------------- 1 | 1. 指针可以改成shared_ptr,不需要delete。 2 | 2. 想法在某些地方写成单例模式。 3 | 3. readn 和 writen 改成写到不能写后或读到不能读后加入epoll等待,要记录写到哪或读到哪,维护这样一个状态。比较两者的性能差异。 4 | 4. 信号处理部分可以将 epoll_wait 替换为更安全的 epoll_pwait。 5 | 6 | 7 | 踩坑: 8 | 1. 对EPOLLONESHOT的误解,原以为当epoll_wait监听到相应的事件触发后,epoll会把与事件关联的fd从epoll描述符中禁止掉并且彻底删除,实际上并不会删除,man手册上的解释如下: 9 | EPOLLONESHOT (since Linux 2.6.2) 10 | Sets the one-shot behavior for the associated file descriptor. 11 | This means that after an event is pulled out with 12 | epoll_wait(2) the associated file descriptor is internally 13 | disabled and no other events will be reported by the epoll 14 | interface. The user must call epoll_ctl() with EPOLL_CTL_MOD 15 | to rearm the file descriptor with a new event mask. 16 | 另外: 17 | Linux Programmer's Manual 中第六个问题: 18 | Q6 Will closing a file descriptor cause it to be removed from all 19 | epoll sets automatically? 20 | 21 | A6 Yes, but be aware of the following point. A file descriptor is a 22 | reference to an open file description (see open(2)). Whenever a 23 | file descriptor is duplicated via dup(2), dup2(2), fcntl(2) 24 | F_DUPFD, or fork(2), a new file descriptor referring to the same 25 | open file description is created. An open file description con‐ 26 | tinues to exist until all file descriptors referring to it have 27 | been closed. A file descriptor is removed from an epoll set only 28 | after all the file descriptors referring to the underlying open 29 | file description have been closed (or before if the file descrip‐ 30 | tor is explicitly removed using epoll_ctl(2) EPOLL_CTL_DEL). 31 | This means that even after a file descriptor that is part of an 32 | epoll set has been closed, events may be reported for that file 33 | descriptor if other file descriptors referring to the same under‐ 34 | lying file description remain open. 35 | 36 | 当调用close()关闭对应的fd时,会使相应的引用计数减一,只有减到0时,epoll才会真的删掉它,所以,比较安全的做法是: 37 | 先del掉它,再close它(如果不确定close是否真的关闭了这个文件。)。 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ubuntu 配置opencv: 52 | vim /etc/ld.so.conf 53 | 加一行include /usr/local/opencv/* 54 | /sbin/ldconfig –v -------------------------------------------------------------------------------- /old_version/old_version_0.2/index.html: -------------------------------------------------------------------------------- 1 | Hello World ! -------------------------------------------------------------------------------- /old_version/old_version_0.2/requestData.h: -------------------------------------------------------------------------------- 1 | #ifndef REQUESTDATA 2 | #define REQUESTDATA 3 | #include 4 | #include 5 | 6 | const int STATE_PARSE_URI = 1; 7 | const int STATE_PARSE_HEADERS = 2; 8 | const int STATE_RECV_BODY = 3; 9 | const int STATE_ANALYSIS = 4; 10 | const int STATE_FINISH = 5; 11 | 12 | const int MAX_BUFF = 4096; 13 | 14 | // 有请求出现但是读不到数据,可能是Request Aborted, 15 | // 或者来自网络的数据没有达到等原因, 16 | // 对这样的请求尝试超过一定的次数就抛弃 17 | const int AGAIN_MAX_TIMES = 200; 18 | 19 | const int PARSE_URI_AGAIN = -1; 20 | const int PARSE_URI_ERROR = -2; 21 | const int PARSE_URI_SUCCESS = 0; 22 | 23 | const int PARSE_HEADER_AGAIN = -1; 24 | const int PARSE_HEADER_ERROR = -2; 25 | const int PARSE_HEADER_SUCCESS = 0; 26 | 27 | const int ANALYSIS_ERROR = -2; 28 | const int ANALYSIS_SUCCESS = 0; 29 | 30 | const int METHOD_POST = 1; 31 | const int METHOD_GET = 2; 32 | const int HTTP_10 = 1; 33 | const int HTTP_11 = 2; 34 | 35 | const int EPOLL_WAIT_TIME = 500; 36 | 37 | class MimeType 38 | { 39 | private: 40 | static pthread_mutex_t lock; 41 | static std::unordered_map mime; 42 | MimeType(); 43 | MimeType(const MimeType &m); 44 | public: 45 | static std::string getMime(const std::string &suffix); 46 | }; 47 | 48 | enum HeadersState 49 | { 50 | h_start = 0, 51 | h_key, 52 | h_colon, 53 | h_spaces_after_colon, 54 | h_value, 55 | h_CR, 56 | h_LF, 57 | h_end_CR, 58 | h_end_LF 59 | }; 60 | 61 | struct mytimer; 62 | struct requestData; 63 | 64 | struct requestData 65 | { 66 | private: 67 | int againTimes; 68 | std::string path; 69 | int fd; 70 | int epollfd; 71 | // content的内容用完就清 72 | std::string content; 73 | int method; 74 | int HTTPversion; 75 | std::string file_name; 76 | int now_read_pos; 77 | int state; 78 | int h_state; 79 | bool isfinish; 80 | bool keep_alive; 81 | std::unordered_map headers; 82 | mytimer *timer; 83 | 84 | private: 85 | int parse_URI(); 86 | int parse_Headers(); 87 | int analysisRequest(); 88 | 89 | public: 90 | 91 | requestData(); 92 | requestData(int _epollfd, int _fd, std::string _path); 93 | ~requestData(); 94 | void addTimer(mytimer *mtimer); 95 | void reset(); 96 | void seperateTimer(); 97 | int getFd(); 98 | void setFd(int _fd); 99 | void handleRequest(); 100 | void handleError(int fd, int err_num, std::string short_msg); 101 | }; 102 | 103 | struct mytimer 104 | { 105 | bool deleted; 106 | size_t expired_time; 107 | requestData *request_data; 108 | 109 | mytimer(requestData *_request_data, int timeout); 110 | ~mytimer(); 111 | void update(int timeout); 112 | bool isvalid(); 113 | void clearReq(); 114 | void setDeleted(); 115 | bool isDeleted() const; 116 | size_t getExpTime() const; 117 | }; 118 | 119 | struct timerCmp 120 | { 121 | bool operator()(const mytimer *a, const mytimer *b) const; 122 | }; 123 | 124 | 125 | class MutexLockGuard 126 | { 127 | public: 128 | explicit MutexLockGuard(); 129 | ~MutexLockGuard(); 130 | 131 | private: 132 | static pthread_mutex_t lock; 133 | 134 | private: 135 | MutexLockGuard(const MutexLockGuard&); 136 | MutexLockGuard& operator=(const MutexLockGuard&); 137 | }; 138 | #endif -------------------------------------------------------------------------------- /old_version/old_version_0.2/threadpool.h: -------------------------------------------------------------------------------- 1 | #ifndef THREADPOOL 2 | #define THREADPOOL 3 | #include "requestData.h" 4 | #include 5 | 6 | const int THREADPOOL_INVALID = -1; 7 | const int THREADPOOL_LOCK_FAILURE = -2; 8 | const int THREADPOOL_QUEUE_FULL = -3; 9 | const int THREADPOOL_SHUTDOWN = -4; 10 | const int THREADPOOL_THREAD_FAILURE = -5; 11 | const int THREADPOOL_GRACEFUL = 1; 12 | 13 | const int MAX_THREADS = 1024; 14 | const int MAX_QUEUE = 65535; 15 | 16 | typedef enum 17 | { 18 | immediate_shutdown = 1, 19 | graceful_shutdown = 2 20 | } threadpool_shutdown_t; 21 | 22 | /** 23 | * @struct threadpool_task 24 | * @brief the work struct 25 | * 26 | * @var function Pointer to the function that will perform the task. 27 | * @var argument Argument to be passed to the function. 28 | */ 29 | 30 | typedef struct { 31 | void (*function)(void *); 32 | void *argument; 33 | } threadpool_task_t; 34 | 35 | /** 36 | * @struct threadpool 37 | * @brief The threadpool struct 38 | * 39 | * @var notify Condition variable to notify worker threads. 40 | * @var threads Array containing worker threads ID. 41 | * @var thread_count Number of threads 42 | * @var queue Array containing the task queue. 43 | * @var queue_size Size of the task queue. 44 | * @var head Index of the first element. 45 | * @var tail Index of the next element. 46 | * @var count Number of pending tasks 47 | * @var shutdown Flag indicating if the pool is shutting down 48 | * @var started Number of started threads 49 | */ 50 | struct threadpool_t 51 | { 52 | pthread_mutex_t lock; 53 | pthread_cond_t notify; 54 | pthread_t *threads; 55 | threadpool_task_t *queue; 56 | int thread_count; 57 | int queue_size; 58 | int head; 59 | int tail; 60 | int count; 61 | int shutdown; 62 | int started; 63 | }; 64 | 65 | threadpool_t *threadpool_create(int thread_count, int queue_size, int flags); 66 | int threadpool_add(threadpool_t *pool, void (*function)(void *), void *argument, int flags); 67 | int threadpool_destroy(threadpool_t *pool, int flags); 68 | int threadpool_free(threadpool_t *pool); 69 | static void *threadpool_thread(void *threadpool); 70 | 71 | #endif -------------------------------------------------------------------------------- /old_version/old_version_0.2/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | ssize_t readn(int fd, void *buff, size_t n) 9 | { 10 | size_t nleft = n; 11 | ssize_t nread = 0; 12 | ssize_t readSum = 0; 13 | char *ptr = (char*)buff; 14 | while (nleft > 0) 15 | { 16 | if ((nread = read(fd, ptr, nleft)) < 0) 17 | { 18 | if (errno == EINTR) 19 | nread = 0; 20 | else if (errno == EAGAIN) 21 | { 22 | return readSum; 23 | } 24 | else 25 | { 26 | return -1; 27 | } 28 | } 29 | else if (nread == 0) 30 | break; 31 | readSum += nread; 32 | nleft -= nread; 33 | ptr += nread; 34 | } 35 | return readSum; 36 | } 37 | 38 | ssize_t writen(int fd, void *buff, size_t n) 39 | { 40 | size_t nleft = n; 41 | ssize_t nwritten = 0; 42 | ssize_t writeSum = 0; 43 | char *ptr = (char*)buff; 44 | while (nleft > 0) 45 | { 46 | if ((nwritten = write(fd, ptr, nleft)) <= 0) 47 | { 48 | if (nwritten < 0) 49 | { 50 | if (errno == EINTR || errno == EAGAIN) 51 | { 52 | nwritten = 0; 53 | continue; 54 | } 55 | else 56 | return -1; 57 | } 58 | } 59 | writeSum += nwritten; 60 | nleft -= nwritten; 61 | ptr += nwritten; 62 | } 63 | return writeSum; 64 | } 65 | 66 | void handle_for_sigpipe() 67 | { 68 | struct sigaction sa; 69 | memset(&sa, '\0', sizeof(sa)); 70 | sa.sa_handler = SIG_IGN; 71 | sa.sa_flags = 0; 72 | if(sigaction(SIGPIPE, &sa, NULL)) 73 | return; 74 | } 75 | 76 | int setSocketNonBlocking(int fd) 77 | { 78 | int flag = fcntl(fd, F_GETFL, 0); 79 | if(flag == -1) 80 | return -1; 81 | 82 | flag |= O_NONBLOCK; 83 | if(fcntl(fd, F_SETFL, flag) == -1) 84 | return -1; 85 | return 0; 86 | } -------------------------------------------------------------------------------- /old_version/old_version_0.2/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL 2 | #define UTIL 3 | #include 4 | 5 | ssize_t readn(int fd, void *buff, size_t n); 6 | ssize_t writen(int fd, void *buff, size_t n); 7 | void handle_for_sigpipe(); 8 | int setSocketNonBlocking(int fd); 9 | 10 | #endif -------------------------------------------------------------------------------- /old_version/old_version_0.3/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE := $(wildcard *.cpp) 2 | OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE))) 3 | 4 | TARGET := myserver 5 | CC := g++ 6 | LIBS := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui 7 | INCLUDE:= -I./usr/local/include/opencv 8 | CFLAGS := -std=c++11 -g -Wall -O3 $(INCLUDE) 9 | CXXFLAGS:= $(CFLAGS) 10 | 11 | .PHONY : objs clean veryclean rebuild all 12 | all : $(TARGET) 13 | objs : $(OBJS) 14 | rebuild: veryclean all 15 | clean : 16 | rm -fr *.o 17 | veryclean : clean 18 | rm -rf $(TARGET) 19 | 20 | $(TARGET) : $(OBJS) 21 | $(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) -------------------------------------------------------------------------------- /old_version/old_version_0.3/Makefile1: -------------------------------------------------------------------------------- 1 | cc=g++ -std=c++11 2 | main: main.o requestData.o epoll.o threadpool.o 3 | $(cc) -o main main.o requestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml 4 | main.o: main.cpp 5 | $(cc) -I/usr/local/include/opencv -c main.cpp 6 | requestData.o: requestData.h requestData.cpp 7 | $(cc) -I/usr/local/include/opencv -c requestData.cpp 8 | epoll.o: epoll.h epoll.cpp 9 | $(cc) -I/usr/local/include/opencv -c epoll.cpp 10 | threadpool.o: threadpool.h threadpool.cpp 11 | $(cc) -I/usr/local/include/opencv -c threadpool.cpp 12 | .PHONY: clean 13 | clean: 14 | -rm main *.o 15 | 16 | 17 | 18 | 19 | .PHONY : everything objs clean veryclean rebuild 20 | everything : 21 | $(TARGET) 22 | all : 23 | $(TARGET) 24 | objs : 25 | $(OBJS) 26 | rebuild: 27 | veryclean everything 28 | clean : 29 | rm -fr *.o 30 | veryclean : 31 | clean rm -fr $(TARGET) -------------------------------------------------------------------------------- /old_version/old_version_0.3/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | -------------------------------------------------------------------------------- /old_version/old_version_0.3/epoll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Epoll 9 | { 10 | private: 11 | static epoll_event *events; 12 | static std::unordered_map> fd2req; 13 | static int epoll_fd; 14 | static const std::string PATH; 15 | public: 16 | static int epoll_init(int maxevents, int listen_num); 17 | static int epoll_add(int fd, std::shared_ptr request, __uint32_t events); 18 | static int epoll_mod(int fd, std::shared_ptr request, __uint32_t events); 19 | static int epoll_del(int fd, __uint32_t events); 20 | static void my_epoll_wait(int listen_fd, int max_events, int timeout); 21 | static void acceptConnection(int listen_fd, int epoll_fd, const std::string path); 22 | static std::vector> getEventsRequest(int listen_fd, int events_num, const std::string path); 23 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.3/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/old_version/old_version_0.3/favicon.ico -------------------------------------------------------------------------------- /old_version/old_version_0.3/improvement.txt: -------------------------------------------------------------------------------- 1 | 1. 指针可以改成shared_ptr,不需要delete。 2 | 2. 想法在某些地方写成单例模式。 3 | 3. readn 和 writen 改成写到不能写后或读到不能读后加入epoll等待,要记录写到哪或读到哪,维护这样一个状态。比较两者的性能差异。 4 | 4. 信号处理部分可以将 epoll_wait 替换为更安全的 epoll_pwait。 5 | 6 | 7 | 踩坑: 8 | 1. 对EPOLLONESHOT的误解,原以为当epoll_wait监听到相应的事件触发后,epoll会把与事件关联的fd从epoll描述符中禁止掉并且彻底删除,实际上并不会删除,man手册上的解释如下: 9 | EPOLLONESHOT (since Linux 2.6.2) 10 | Sets the one-shot behavior for the associated file descriptor. 11 | This means that after an event is pulled out with 12 | epoll_wait(2) the associated file descriptor is internally 13 | disabled and no other events will be reported by the epoll 14 | interface. The user must call epoll_ctl() with EPOLL_CTL_MOD 15 | to rearm the file descriptor with a new event mask. 16 | 另外: 17 | Linux Programmer's Manual 中第六个问题: 18 | Q6 Will closing a file descriptor cause it to be removed from all 19 | epoll sets automatically? 20 | 21 | A6 Yes, but be aware of the following point. A file descriptor is a 22 | reference to an open file description (see open(2)). Whenever a 23 | file descriptor is duplicated via dup(2), dup2(2), fcntl(2) 24 | F_DUPFD, or fork(2), a new file descriptor referring to the same 25 | open file description is created. An open file description con‐ 26 | tinues to exist until all file descriptors referring to it have 27 | been closed. A file descriptor is removed from an epoll set only 28 | after all the file descriptors referring to the underlying open 29 | file description have been closed (or before if the file descrip‐ 30 | tor is explicitly removed using epoll_ctl(2) EPOLL_CTL_DEL). 31 | This means that even after a file descriptor that is part of an 32 | epoll set has been closed, events may be reported for that file 33 | descriptor if other file descriptors referring to the same under‐ 34 | lying file description remain open. 35 | 36 | 当调用close()关闭对应的fd时,会使相应的引用计数减一,只有减到0时,epoll才会真的删掉它,所以,比较安全的做法是: 37 | 先del掉它,再close它(如果不确定close是否真的关闭了这个文件。)。 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ubuntu 配置opencv: 52 | vim /etc/ld.so.conf 53 | 加一行include /usr/local/opencv/* 54 | /sbin/ldconfig –v -------------------------------------------------------------------------------- /old_version/old_version_0.3/index.html: -------------------------------------------------------------------------------- 1 | Hello World ! -------------------------------------------------------------------------------- /old_version/old_version_0.3/requestData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | const int STATE_PARSE_URI = 1; 7 | const int STATE_PARSE_HEADERS = 2; 8 | const int STATE_RECV_BODY = 3; 9 | const int STATE_ANALYSIS = 4; 10 | const int STATE_FINISH = 5; 11 | 12 | const int MAX_BUFF = 4096; 13 | 14 | // 有请求出现但是读不到数据,可能是Request Aborted, 15 | // 或者来自网络的数据没有达到等原因, 16 | // 对这样的请求尝试超过一定的次数就抛弃 17 | const int AGAIN_MAX_TIMES = 200; 18 | 19 | const int PARSE_URI_AGAIN = -1; 20 | const int PARSE_URI_ERROR = -2; 21 | const int PARSE_URI_SUCCESS = 0; 22 | 23 | const int PARSE_HEADER_AGAIN = -1; 24 | const int PARSE_HEADER_ERROR = -2; 25 | const int PARSE_HEADER_SUCCESS = 0; 26 | 27 | const int ANALYSIS_ERROR = -2; 28 | const int ANALYSIS_SUCCESS = 0; 29 | 30 | const int METHOD_POST = 1; 31 | const int METHOD_GET = 2; 32 | const int HTTP_10 = 1; 33 | const int HTTP_11 = 2; 34 | 35 | const int EPOLL_WAIT_TIME = 500; 36 | 37 | class MimeType 38 | { 39 | private: 40 | static pthread_mutex_t lock; 41 | static std::unordered_map mime; 42 | MimeType(); 43 | MimeType(const MimeType &m); 44 | public: 45 | static std::string getMime(const std::string &suffix); 46 | }; 47 | 48 | enum HeadersState 49 | { 50 | h_start = 0, 51 | h_key, 52 | h_colon, 53 | h_spaces_after_colon, 54 | h_value, 55 | h_CR, 56 | h_LF, 57 | h_end_CR, 58 | h_end_LF 59 | }; 60 | 61 | struct mytimer; 62 | class requestData; 63 | 64 | class requestData : public std::enable_shared_from_this 65 | { 66 | private: 67 | int againTimes; 68 | std::string path; 69 | int fd; 70 | int epollfd; 71 | // content的内容用完就清 72 | std::string content; 73 | int method; 74 | int HTTPversion; 75 | std::string file_name; 76 | int now_read_pos; 77 | int state; 78 | int h_state; 79 | bool isfinish; 80 | bool keep_alive; 81 | std::unordered_map headers; 82 | std::weak_ptr timer; 83 | 84 | private: 85 | int parse_URI(); 86 | int parse_Headers(); 87 | int analysisRequest(); 88 | 89 | public: 90 | 91 | requestData(); 92 | requestData(int _epollfd, int _fd, std::string _path); 93 | ~requestData(); 94 | void addTimer(std::shared_ptr mtimer); 95 | void reset(); 96 | void seperateTimer(); 97 | int getFd(); 98 | void setFd(int _fd); 99 | void handleRequest(); 100 | void handleError(int fd, int err_num, std::string short_msg); 101 | }; 102 | 103 | struct mytimer 104 | { 105 | bool deleted; 106 | size_t expired_time; 107 | std::shared_ptr request_data; 108 | 109 | mytimer(std::shared_ptr _request_data, int timeout); 110 | ~mytimer(); 111 | void update(int timeout); 112 | bool isvalid(); 113 | void clearReq(); 114 | void setDeleted(); 115 | bool isDeleted() const; 116 | size_t getExpTime() const; 117 | }; 118 | 119 | struct timerCmp 120 | { 121 | bool operator()(std::shared_ptr &a, std::shared_ptr &b) const; 122 | }; 123 | 124 | 125 | class MutexLockGuard 126 | { 127 | public: 128 | explicit MutexLockGuard(); 129 | ~MutexLockGuard(); 130 | 131 | private: 132 | static pthread_mutex_t lock; 133 | 134 | private: 135 | MutexLockGuard(const MutexLockGuard&); 136 | MutexLockGuard& operator=(const MutexLockGuard&); 137 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.3/threadpool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | const int THREADPOOL_INVALID = -1; 10 | const int THREADPOOL_LOCK_FAILURE = -2; 11 | const int THREADPOOL_QUEUE_FULL = -3; 12 | const int THREADPOOL_SHUTDOWN = -4; 13 | const int THREADPOOL_THREAD_FAILURE = -5; 14 | const int THREADPOOL_GRACEFUL = 1; 15 | 16 | const int MAX_THREADS = 1024; 17 | const int MAX_QUEUE = 65535; 18 | 19 | typedef enum 20 | { 21 | immediate_shutdown = 1, 22 | graceful_shutdown = 2 23 | } threadpool_shutdown_t; 24 | 25 | struct ThreadPoolTask 26 | { 27 | std::function)> fun; 28 | std::shared_ptr args; 29 | }; 30 | 31 | /** 32 | * @struct threadpool 33 | * @brief The threadpool struct 34 | * 35 | * @var notify Condition variable to notify worker threads. 36 | * @var threads Array containing worker threads ID. 37 | * @var thread_count Number of threads 38 | * @var queue Array containing the task queue. 39 | * @var queue_size Size of the task queue. 40 | * @var head Index of the first element. 41 | * @var tail Index of the next element. 42 | * @var count Number of pending tasks 43 | * @var shutdown Flag indicating if the pool is shutting down 44 | * @var started Number of started threads 45 | */ 46 | void myHandler(std::shared_ptr req); 47 | class ThreadPool 48 | { 49 | private: 50 | static pthread_mutex_t lock; 51 | static pthread_cond_t notify; 52 | static std::vector threads; 53 | static std::vector queue; 54 | static int thread_count; 55 | static int queue_size; 56 | static int head; 57 | // tail 指向尾节点的下一节点 58 | static int tail; 59 | static int count; 60 | static int shutdown; 61 | static int started; 62 | public: 63 | static int threadpool_create(int _thread_count, int _queue_size); 64 | static int threadpool_add(std::shared_ptr args, std::function)> fun = myHandler); 65 | static int threadpool_destroy(); 66 | static int threadpool_free(); 67 | static void *threadpool_thread(void *args); 68 | }; 69 | -------------------------------------------------------------------------------- /old_version/old_version_0.3/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | ssize_t readn(int fd, void *buff, size_t n) 9 | { 10 | size_t nleft = n; 11 | ssize_t nread = 0; 12 | ssize_t readSum = 0; 13 | char *ptr = (char*)buff; 14 | while (nleft > 0) 15 | { 16 | if ((nread = read(fd, ptr, nleft)) < 0) 17 | { 18 | if (errno == EINTR) 19 | nread = 0; 20 | else if (errno == EAGAIN) 21 | { 22 | return readSum; 23 | } 24 | else 25 | { 26 | return -1; 27 | } 28 | } 29 | else if (nread == 0) 30 | break; 31 | readSum += nread; 32 | nleft -= nread; 33 | ptr += nread; 34 | } 35 | return readSum; 36 | } 37 | 38 | ssize_t writen(int fd, void *buff, size_t n) 39 | { 40 | size_t nleft = n; 41 | ssize_t nwritten = 0; 42 | ssize_t writeSum = 0; 43 | char *ptr = (char*)buff; 44 | while (nleft > 0) 45 | { 46 | if ((nwritten = write(fd, ptr, nleft)) <= 0) 47 | { 48 | if (nwritten < 0) 49 | { 50 | if (errno == EINTR || errno == EAGAIN) 51 | { 52 | nwritten = 0; 53 | continue; 54 | } 55 | else 56 | return -1; 57 | } 58 | } 59 | writeSum += nwritten; 60 | nleft -= nwritten; 61 | ptr += nwritten; 62 | } 63 | return writeSum; 64 | } 65 | 66 | void handle_for_sigpipe() 67 | { 68 | struct sigaction sa; 69 | memset(&sa, '\0', sizeof(sa)); 70 | sa.sa_handler = SIG_IGN; 71 | sa.sa_flags = 0; 72 | if(sigaction(SIGPIPE, &sa, NULL)) 73 | return; 74 | } 75 | 76 | int setSocketNonBlocking(int fd) 77 | { 78 | int flag = fcntl(fd, F_GETFL, 0); 79 | if(flag == -1) 80 | return -1; 81 | 82 | flag |= O_NONBLOCK; 83 | if(fcntl(fd, F_SETFL, flag) == -1) 84 | return -1; 85 | return 0; 86 | } -------------------------------------------------------------------------------- /old_version/old_version_0.3/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | ssize_t readn(int fd, void *buff, size_t n); 5 | ssize_t writen(int fd, void *buff, size_t n); 6 | void handle_for_sigpipe(); 7 | int setSocketNonBlocking(int fd); -------------------------------------------------------------------------------- /old_version/old_version_0.4/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE := $(wildcard *.cpp) 2 | OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE))) 3 | 4 | TARGET := myserver 5 | CC := g++ 6 | LIBS := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui 7 | INCLUDE:= -I./usr/local/include/opencv 8 | CFLAGS := -std=c++11 -g -Wall -O3 $(INCLUDE) -D_PTHREADS 9 | CXXFLAGS:= $(CFLAGS) 10 | 11 | .PHONY : objs clean veryclean rebuild all 12 | all : $(TARGET) 13 | objs : $(OBJS) 14 | rebuild: veryclean all 15 | clean : 16 | rm -fr *.o 17 | veryclean : clean 18 | rm -rf $(TARGET) 19 | 20 | $(TARGET) : $(OBJS) 21 | $(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) -------------------------------------------------------------------------------- /old_version/old_version_0.4/Makefile1: -------------------------------------------------------------------------------- 1 | cc=g++ -std=c++11 2 | main: main.o RequestData.o epoll.o threadpool.o 3 | $(cc) -o main main.o RequestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml 4 | main.o: main.cpp 5 | $(cc) -I/usr/local/include/opencv -c main.cpp 6 | RequestData.o: requestData.h RequestData.cpp 7 | $(cc) -I/usr/local/include/opencv -c RequestData.cpp 8 | epoll.o: epoll.h epoll.cpp 9 | $(cc) -I/usr/local/include/opencv -c epoll.cpp 10 | threadpool.o: threadpool.h threadpool.cpp 11 | $(cc) -I/usr/local/include/opencv -c threadpool.cpp 12 | .PHONY: clean 13 | clean: 14 | -rm main *.o 15 | 16 | 17 | 18 | 19 | .PHONY : everything objs clean veryclean rebuild 20 | everything : 21 | $(TARGET) 22 | all : 23 | $(TARGET) 24 | objs : 25 | $(OBJS) 26 | rebuild: 27 | veryclean everything 28 | clean : 29 | rm -fr *.o 30 | veryclean : 31 | clean rm -fr $(TARGET) -------------------------------------------------------------------------------- /old_version/old_version_0.4/base/mutexLock.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nocopyable.hpp" 3 | #include 4 | #include 5 | 6 | class MutexLock: noncopyable 7 | { 8 | public: 9 | MutexLock() 10 | { 11 | pthread_mutex_init(&mutex, NULL); 12 | } 13 | ~MutexLock() 14 | { 15 | pthread_mutex_lock(&mutex); 16 | pthread_mutex_destroy(&mutex); 17 | } 18 | void lock() 19 | { 20 | pthread_mutex_lock(&mutex); 21 | } 22 | void unlock() 23 | { 24 | pthread_mutex_unlock(&mutex); 25 | } 26 | private: 27 | pthread_mutex_t mutex; 28 | }; 29 | 30 | 31 | class MutexLockGuard: noncopyable 32 | { 33 | public: 34 | explicit MutexLockGuard(MutexLock &_mutex): 35 | mutex(_mutex) 36 | { 37 | mutex.lock(); 38 | } 39 | ~MutexLockGuard() 40 | { 41 | mutex.unlock(); 42 | } 43 | private: 44 | MutexLock &mutex; 45 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.4/base/nocopyable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | class noncopyable 3 | { 4 | protected: 5 | noncopyable() {} 6 | ~noncopyable() {} 7 | private: 8 | noncopyable(const noncopyable&); 9 | const noncopyable& operator=(const noncopyable&); 10 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.4/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | -------------------------------------------------------------------------------- /old_version/old_version_0.4/epoll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | #include "timer.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Epoll 10 | { 11 | public: 12 | typedef std::shared_ptr SP_ReqData; 13 | private: 14 | static epoll_event *events; 15 | static std::unordered_map fd2req; 16 | static int epoll_fd; 17 | static const std::string PATH; 18 | 19 | static TimerManager timer_manager; 20 | public: 21 | static int epoll_init(int maxevents, int listen_num); 22 | static int epoll_add(int fd, SP_ReqData request, __uint32_t events); 23 | static int epoll_mod(int fd, SP_ReqData request, __uint32_t events); 24 | static int epoll_del(int fd, __uint32_t events = (EPOLLIN | EPOLLET | EPOLLONESHOT)); 25 | static void my_epoll_wait(int listen_fd, int max_events, int timeout); 26 | static void acceptConnection(int listen_fd, int epoll_fd, const std::string path); 27 | static std::vector getEventsRequest(int listen_fd, int events_num, const std::string path); 28 | 29 | static void add_timer(SP_ReqData request_data, int timeout); 30 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.4/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/old_version/old_version_0.4/favicon.ico -------------------------------------------------------------------------------- /old_version/old_version_0.4/improvement.txt: -------------------------------------------------------------------------------- 1 | 1. 指针可以改成shared_ptr,不需要delete。 2 | 2. 想法在某些地方写成单例模式。 3 | 3. readn 和 writen 改成写到不能写后或读到不能读后加入epoll等待,要记录写到哪或读到哪,维护这样一个状态。比较两者的性能差异。 4 | 4. 信号处理部分可以将 epoll_wait 替换为更安全的 epoll_pwait。 5 | 6 | 7 | 踩坑: 8 | 1. 对EPOLLONESHOT的误解,原以为当epoll_wait监听到相应的事件触发后,epoll会把与事件关联的fd从epoll描述符中禁止掉并且彻底删除,实际上并不会删除,man手册上的解释如下: 9 | EPOLLONESHOT (since Linux 2.6.2) 10 | Sets the one-shot behavior for the associated file descriptor. 11 | This means that after an event is pulled out with 12 | epoll_wait(2) the associated file descriptor is internally 13 | disabled and no other events will be reported by the epoll 14 | interface. The user must call epoll_ctl() with EPOLL_CTL_MOD 15 | to rearm the file descriptor with a new event mask. 16 | 另外: 17 | Linux Programmer's Manual 中第六个问题: 18 | Q6 Will closing a file descriptor cause it to be removed from all 19 | epoll sets automatically? 20 | 21 | A6 Yes, but be aware of the following point. A file descriptor is a 22 | reference to an open file description (see open(2)). Whenever a 23 | file descriptor is duplicated via dup(2), dup2(2), fcntl(2) 24 | F_DUPFD, or fork(2), a new file descriptor referring to the same 25 | open file description is created. An open file description con‐ 26 | tinues to exist until all file descriptors referring to it have 27 | been closed. A file descriptor is removed from an epoll set only 28 | after all the file descriptors referring to the underlying open 29 | file description have been closed (or before if the file descrip‐ 30 | tor is explicitly removed using epoll_ctl(2) EPOLL_CTL_DEL). 31 | This means that even after a file descriptor that is part of an 32 | epoll set has been closed, events may be reported for that file 33 | descriptor if other file descriptors referring to the same under‐ 34 | lying file description remain open. 35 | 36 | 当调用close()关闭对应的fd时,会使相应的引用计数减一,只有减到0时,epoll才会真的删掉它,所以,比较安全的做法是: 37 | 先del掉它,再close它(如果不确定close是否真的关闭了这个文件。)。 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ubuntu 配置opencv: 52 | vim /etc/ld.so.conf 53 | 加一行include /usr/local/opencv/* 54 | /sbin/ldconfig –v -------------------------------------------------------------------------------- /old_version/old_version_0.4/index.html: -------------------------------------------------------------------------------- 1 | Hello World ! -------------------------------------------------------------------------------- /old_version/old_version_0.4/main.cpp: -------------------------------------------------------------------------------- 1 | #include "requestData.h" 2 | #include "epoll.h" 3 | #include "threadpool.h" 4 | #include "util.h" 5 | #include "config.h" 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 | 19 | using namespace std; 20 | 21 | static const int MAXEVENTS = 5000; 22 | static const int LISTENQ = 1024; 23 | const int THREADPOOL_THREAD_NUM = 4; 24 | const int QUEUE_SIZE = 65535; 25 | 26 | const int PORT = 8888; 27 | const int ASK_STATIC_FILE = 1; 28 | const int ASK_IMAGE_STITCH = 2; 29 | 30 | const int TIMER_TIME_OUT = 500; 31 | 32 | void acceptConnection(int listen_fd, int epoll_fd, const string &path); 33 | 34 | int socket_bind_listen(int port) 35 | { 36 | // 检查port值,取正确区间范围 37 | if (port < 1024 || port > 65535) 38 | return -1; 39 | 40 | // 创建socket(IPv4 + TCP),返回监听描述符 41 | int listen_fd = 0; 42 | if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 43 | return -1; 44 | 45 | // 消除bind时"Address already in use"错误 46 | int optval = 1; 47 | if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) 48 | return -1; 49 | 50 | // 设置服务器IP和Port,和监听描述副绑定 51 | struct sockaddr_in server_addr; 52 | bzero((char*)&server_addr, sizeof(server_addr)); 53 | server_addr.sin_family = AF_INET; 54 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 55 | server_addr.sin_port = htons((unsigned short)port); 56 | if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) 57 | return -1; 58 | 59 | // 开始监听,最大等待队列长为LISTENQ 60 | if(listen(listen_fd, LISTENQ) == -1) 61 | return -1; 62 | 63 | // 无效监听描述符 64 | if(listen_fd == -1) 65 | { 66 | close(listen_fd); 67 | return -1; 68 | } 69 | return listen_fd; 70 | } 71 | 72 | 73 | int main() 74 | { 75 | #ifndef _PTHREADS 76 | printf("_PTHREADS is not defined !\n"); 77 | #endif 78 | handle_for_sigpipe(); 79 | if (Epoll::epoll_init(MAXEVENTS, LISTENQ) < 0) 80 | { 81 | perror("epoll init failed"); 82 | return 1; 83 | } 84 | if (ThreadPool::threadpool_create(THREADPOOL_THREAD_NUM, QUEUE_SIZE) < 0) 85 | { 86 | printf("Threadpool create failed\n"); 87 | return 1; 88 | } 89 | int listen_fd = socket_bind_listen(PORT); 90 | if (listen_fd < 0) 91 | { 92 | perror("socket bind failed"); 93 | return 1; 94 | } 95 | if (setSocketNonBlocking(listen_fd) < 0) 96 | { 97 | perror("set socket non block failed"); 98 | return 1; 99 | } 100 | shared_ptr request(new RequestData()); 101 | request->setFd(listen_fd); 102 | if (Epoll::epoll_add(listen_fd, request, EPOLLIN | EPOLLET) < 0) 103 | { 104 | perror("epoll add error"); 105 | return 1; 106 | } 107 | 108 | while (true) 109 | { 110 | //sleep(10); 111 | Epoll::my_epoll_wait(listen_fd, MAXEVENTS, -1); 112 | 113 | //ThreadPool::threadpool_destroy(); 114 | //break; 115 | } 116 | return 0; 117 | } -------------------------------------------------------------------------------- /old_version/old_version_0.4/requestData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "timer.h" 3 | #include 4 | #include 5 | #include 6 | 7 | const int STATE_PARSE_URI = 1; 8 | const int STATE_PARSE_HEADERS = 2; 9 | const int STATE_RECV_BODY = 3; 10 | const int STATE_ANALYSIS = 4; 11 | const int STATE_FINISH = 5; 12 | 13 | const int MAX_BUFF = 4096; 14 | 15 | // 有请求出现但是读不到数据,可能是Request Aborted, 16 | // 或者来自网络的数据没有达到等原因, 17 | // 对这样的请求尝试超过一定的次数就抛弃 18 | const int AGAIN_MAX_TIMES = 200; 19 | 20 | const int PARSE_URI_AGAIN = -1; 21 | const int PARSE_URI_ERROR = -2; 22 | const int PARSE_URI_SUCCESS = 0; 23 | 24 | const int PARSE_HEADER_AGAIN = -1; 25 | const int PARSE_HEADER_ERROR = -2; 26 | const int PARSE_HEADER_SUCCESS = 0; 27 | 28 | const int ANALYSIS_ERROR = -2; 29 | const int ANALYSIS_SUCCESS = 0; 30 | 31 | const int METHOD_POST = 1; 32 | const int METHOD_GET = 2; 33 | const int HTTP_10 = 1; 34 | const int HTTP_11 = 2; 35 | 36 | const int EPOLL_WAIT_TIME = 500; 37 | 38 | class MimeType 39 | { 40 | private: 41 | static void init(); 42 | static std::unordered_map mime; 43 | MimeType(); 44 | MimeType(const MimeType &m); 45 | 46 | public: 47 | static std::string getMime(const std::string &suffix); 48 | 49 | private: 50 | static pthread_once_t once_control; 51 | }; 52 | 53 | enum HeadersState 54 | { 55 | h_start = 0, 56 | h_key, 57 | h_colon, 58 | h_spaces_after_colon, 59 | h_value, 60 | h_CR, 61 | h_LF, 62 | h_end_CR, 63 | h_end_LF 64 | }; 65 | 66 | class TimerNode; 67 | 68 | class RequestData : public std::enable_shared_from_this 69 | { 70 | private: 71 | int againTimes; 72 | std::string path; 73 | int fd; 74 | int epollfd; 75 | // content的内容用完就清 76 | std::string content; 77 | int method; 78 | int HTTPversion; 79 | std::string file_name; 80 | int now_read_pos; 81 | int state; 82 | int h_state; 83 | bool isfinish; 84 | bool keep_alive; 85 | std::unordered_map headers; 86 | std::weak_ptr timer; 87 | 88 | private: 89 | int parse_URI(); 90 | int parse_Headers(); 91 | int analysisRequest(); 92 | 93 | public: 94 | 95 | RequestData(); 96 | RequestData(int _epollfd, int _fd, std::string _path); 97 | ~RequestData(); 98 | void linkTimer(std::shared_ptr mtimer); 99 | void reset(); 100 | void seperateTimer(); 101 | int getFd(); 102 | void setFd(int _fd); 103 | void handleRequest(); 104 | void handleError(int fd, int err_num, std::string short_msg); 105 | }; 106 | 107 | -------------------------------------------------------------------------------- /old_version/old_version_0.4/threadpool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | const int THREADPOOL_INVALID = -1; 10 | const int THREADPOOL_LOCK_FAILURE = -2; 11 | const int THREADPOOL_QUEUE_FULL = -3; 12 | const int THREADPOOL_SHUTDOWN = -4; 13 | const int THREADPOOL_THREAD_FAILURE = -5; 14 | const int THREADPOOL_GRACEFUL = 1; 15 | 16 | const int MAX_THREADS = 1024; 17 | const int MAX_QUEUE = 65535; 18 | 19 | typedef enum 20 | { 21 | immediate_shutdown = 1, 22 | graceful_shutdown = 2 23 | } ShutDownOption; 24 | 25 | struct ThreadPoolTask 26 | { 27 | std::function)> fun; 28 | std::shared_ptr args; 29 | }; 30 | 31 | /** 32 | * @struct threadpool 33 | * @brief The threadpool struct 34 | * 35 | * @var notify Condition variable to notify worker threads. 36 | * @var threads Array containing worker threads ID. 37 | * @var thread_count Number of threads 38 | * @var queue Array containing the task queue. 39 | * @var queue_size Size of the task queue. 40 | * @var head Index of the first element. 41 | * @var tail Index of the next element. 42 | * @var count Number of pending tasks 43 | * @var shutdown Flag indicating if the pool is shutting down 44 | * @var started Number of started threads 45 | */ 46 | void myHandler(std::shared_ptr req); 47 | class ThreadPool 48 | { 49 | private: 50 | static pthread_mutex_t lock; 51 | static pthread_cond_t notify; 52 | static std::vector threads; 53 | static std::vector queue; 54 | static int thread_count; 55 | static int queue_size; 56 | static int head; 57 | // tail 指向尾节点的下一节点 58 | static int tail; 59 | static int count; 60 | static int shutdown; 61 | static int started; 62 | public: 63 | static int threadpool_create(int _thread_count, int _queue_size); 64 | static int threadpool_add(std::shared_ptr args, std::function)> fun = myHandler); 65 | static int threadpool_destroy(ShutDownOption shutdown_option = graceful_shutdown); 66 | static int threadpool_free(); 67 | static void *threadpool_thread(void *args); 68 | }; 69 | -------------------------------------------------------------------------------- /old_version/old_version_0.4/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | #include "epoll.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | using namespace std; 12 | 13 | 14 | TimerNode::TimerNode(SP_ReqData _request_data, int timeout): 15 | deleted(false), 16 | request_data(_request_data) 17 | { 18 | cout << "TimerNode()" << endl; 19 | struct timeval now; 20 | gettimeofday(&now, NULL); 21 | // 以毫秒计 22 | expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout; 23 | } 24 | 25 | TimerNode::~TimerNode() 26 | { 27 | cout << "~TimerNode()" << endl; 28 | if (request_data) 29 | { 30 | Epoll::epoll_del(request_data->getFd()); 31 | } 32 | //request_data.reset(); 33 | // if (request_data) 34 | // { 35 | // cout << "request_data=" << request_data << endl; 36 | // delete request_data; 37 | // request_data = NULL; 38 | // } 39 | } 40 | 41 | void TimerNode::update(int timeout) 42 | { 43 | struct timeval now; 44 | gettimeofday(&now, NULL); 45 | expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout; 46 | } 47 | 48 | bool TimerNode::isvalid() 49 | { 50 | struct timeval now; 51 | gettimeofday(&now, NULL); 52 | size_t temp = ((now.tv_sec * 1000) + (now.tv_usec / 1000)); 53 | if (temp < expired_time) 54 | { 55 | return true; 56 | } 57 | else 58 | { 59 | this->setDeleted(); 60 | return false; 61 | } 62 | } 63 | 64 | void TimerNode::clearReq() 65 | { 66 | request_data.reset(); 67 | this->setDeleted(); 68 | } 69 | 70 | void TimerNode::setDeleted() 71 | { 72 | deleted = true; 73 | } 74 | 75 | bool TimerNode::isDeleted() const 76 | { 77 | return deleted; 78 | } 79 | 80 | size_t TimerNode::getExpTime() const 81 | { 82 | return expired_time; 83 | } 84 | 85 | TimerManager::TimerManager() 86 | { 87 | } 88 | 89 | TimerManager::~TimerManager() 90 | { 91 | } 92 | 93 | void TimerManager::addTimer(SP_ReqData request_data, int timeout) 94 | { 95 | SP_TimerNode new_node(new TimerNode(request_data, timeout)); 96 | { 97 | MutexLockGuard locker(lock); 98 | TimerNodeQueue.push(new_node); 99 | } 100 | request_data->linkTimer(new_node); 101 | } 102 | 103 | void TimerManager::addTimer(SP_TimerNode timer_node) 104 | { 105 | 106 | } 107 | 108 | /* 处理逻辑是这样的~ 109 | 因为(1) 优先队列不支持随机访问 110 | (2) 即使支持,随机删除某节点后破坏了堆的结构,需要重新更新堆结构。 111 | 所以对于被置为deleted的时间节点,会延迟到它(1)超时 或 (2)它前面的节点都被删除时,它才会被删除。 112 | 一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。 113 | 这样做有两个好处: 114 | (1) 第一个好处是不需要遍历优先队列,省时。 115 | (2) 第二个好处是给超时时间一个容忍的时间,就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除),如果监听的请求在超时后的下一次请求中又一次出现了, 116 | 就不用再重新申请RequestData节点了,这样可以继续重复利用前面的RequestData,减少了一次delete和一次new的时间。 117 | */ 118 | 119 | void TimerManager::handle_expired_event() 120 | { 121 | MutexLockGuard locker(lock); 122 | while (!TimerNodeQueue.empty()) 123 | { 124 | SP_TimerNode ptimer_now = TimerNodeQueue.top(); 125 | if (ptimer_now->isDeleted()) 126 | { 127 | TimerNodeQueue.pop(); 128 | //delete ptimer_now; 129 | } 130 | else if (ptimer_now->isvalid() == false) 131 | { 132 | TimerNodeQueue.pop(); 133 | //delete ptimer_now; 134 | } 135 | else 136 | { 137 | break; 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /old_version/old_version_0.4/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | #include "./base/nocopyable.hpp" 4 | #include "./base/mutexLock.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class RequestData; 11 | 12 | class TimerNode 13 | { 14 | typedef std::shared_ptr SP_ReqData; 15 | private: 16 | bool deleted; 17 | size_t expired_time; 18 | SP_ReqData request_data; 19 | public: 20 | TimerNode(SP_ReqData _request_data, int timeout); 21 | ~TimerNode(); 22 | void update(int timeout); 23 | bool isvalid(); 24 | void clearReq(); 25 | void setDeleted(); 26 | bool isDeleted() const; 27 | size_t getExpTime() const; 28 | }; 29 | 30 | struct timerCmp 31 | { 32 | bool operator()(std::shared_ptr &a, std::shared_ptr &b) const 33 | { 34 | return a->getExpTime() > b->getExpTime(); 35 | } 36 | }; 37 | 38 | class TimerManager 39 | { 40 | typedef std::shared_ptr SP_ReqData; 41 | typedef std::shared_ptr SP_TimerNode; 42 | private: 43 | std::priority_queue, timerCmp> TimerNodeQueue; 44 | MutexLock lock; 45 | public: 46 | TimerManager(); 47 | ~TimerManager(); 48 | void addTimer(SP_ReqData request_data, int timeout); 49 | void addTimer(SP_TimerNode timer_node); 50 | void handle_expired_event(); 51 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.4/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | ssize_t readn(int fd, void *buff, size_t n) 9 | { 10 | size_t nleft = n; 11 | ssize_t nread = 0; 12 | ssize_t readSum = 0; 13 | char *ptr = (char*)buff; 14 | while (nleft > 0) 15 | { 16 | if ((nread = read(fd, ptr, nleft)) < 0) 17 | { 18 | if (errno == EINTR) 19 | nread = 0; 20 | else if (errno == EAGAIN) 21 | { 22 | return readSum; 23 | } 24 | else 25 | { 26 | return -1; 27 | } 28 | } 29 | else if (nread == 0) 30 | break; 31 | readSum += nread; 32 | nleft -= nread; 33 | ptr += nread; 34 | } 35 | return readSum; 36 | } 37 | 38 | ssize_t writen(int fd, void *buff, size_t n) 39 | { 40 | size_t nleft = n; 41 | ssize_t nwritten = 0; 42 | ssize_t writeSum = 0; 43 | char *ptr = (char*)buff; 44 | while (nleft > 0) 45 | { 46 | if ((nwritten = write(fd, ptr, nleft)) <= 0) 47 | { 48 | if (nwritten < 0) 49 | { 50 | if (errno == EINTR || errno == EAGAIN) 51 | { 52 | nwritten = 0; 53 | continue; 54 | } 55 | else 56 | return -1; 57 | } 58 | } 59 | writeSum += nwritten; 60 | nleft -= nwritten; 61 | ptr += nwritten; 62 | } 63 | return writeSum; 64 | } 65 | 66 | void handle_for_sigpipe() 67 | { 68 | struct sigaction sa; 69 | memset(&sa, '\0', sizeof(sa)); 70 | sa.sa_handler = SIG_IGN; 71 | sa.sa_flags = 0; 72 | if(sigaction(SIGPIPE, &sa, NULL)) 73 | return; 74 | } 75 | 76 | int setSocketNonBlocking(int fd) 77 | { 78 | int flag = fcntl(fd, F_GETFL, 0); 79 | if(flag == -1) 80 | return -1; 81 | 82 | flag |= O_NONBLOCK; 83 | if(fcntl(fd, F_SETFL, flag) == -1) 84 | return -1; 85 | return 0; 86 | } -------------------------------------------------------------------------------- /old_version/old_version_0.4/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | ssize_t readn(int fd, void *buff, size_t n); 5 | ssize_t writen(int fd, void *buff, size_t n); 6 | void handle_for_sigpipe(); 7 | int setSocketNonBlocking(int fd); -------------------------------------------------------------------------------- /old_version/old_version_0.5/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE := $(wildcard *.cpp) 2 | OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE))) 3 | 4 | TARGET := myserver 5 | CC := g++ 6 | LIBS := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui 7 | INCLUDE:= -I./usr/local/include/opencv 8 | CFLAGS := -std=c++11 -g -Wall -O0 $(INCLUDE) -D_PTHREADS 9 | CXXFLAGS:= $(CFLAGS) 10 | 11 | .PHONY : objs clean veryclean rebuild all 12 | all : $(TARGET) 13 | objs : $(OBJS) 14 | rebuild: veryclean all 15 | clean : 16 | rm -fr *.o 17 | veryclean : clean 18 | rm -rf $(TARGET) 19 | 20 | $(TARGET) : $(OBJS) 21 | $(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) -------------------------------------------------------------------------------- /old_version/old_version_0.5/Makefile1: -------------------------------------------------------------------------------- 1 | cc=g++ -std=c++11 2 | main: main.o RequestData.o epoll.o threadpool.o 3 | $(cc) -o main main.o RequestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml 4 | main.o: main.cpp 5 | $(cc) -I/usr/local/include/opencv -c main.cpp 6 | RequestData.o: requestData.h RequestData.cpp 7 | $(cc) -I/usr/local/include/opencv -c RequestData.cpp 8 | epoll.o: epoll.h epoll.cpp 9 | $(cc) -I/usr/local/include/opencv -c epoll.cpp 10 | threadpool.o: threadpool.h threadpool.cpp 11 | $(cc) -I/usr/local/include/opencv -c threadpool.cpp 12 | .PHONY: clean 13 | clean: 14 | -rm main *.o 15 | 16 | 17 | 18 | 19 | .PHONY : everything objs clean veryclean rebuild 20 | everything : 21 | $(TARGET) 22 | all : 23 | $(TARGET) 24 | objs : 25 | $(OBJS) 26 | rebuild: 27 | veryclean everything 28 | clean : 29 | rm -fr *.o 30 | veryclean : 31 | clean rm -fr $(TARGET) -------------------------------------------------------------------------------- /old_version/old_version_0.5/base/condition.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nocopyable.hpp" 3 | #include "mutexLock.hpp" 4 | #include 5 | 6 | class Condition: nocopyable 7 | { 8 | public: 9 | explicit Condition(MutexLock &_mutex): 10 | mutex(_mutex) 11 | { 12 | pthread_cond_init(cond, NULL); 13 | } 14 | ~Condition() 15 | { 16 | pthread_cond_destroy(cond); 17 | } 18 | void wait() 19 | { 20 | pthread_cond_wait(&cond, mutex.get()); 21 | } 22 | void notify() 23 | { 24 | pthread_cond_signal(&cond); 25 | } 26 | void notifyAll() 27 | { 28 | pthread_cond_broadcast(&cond); 29 | } 30 | private: 31 | MutexLock &mutex; 32 | pthread_cond_t cond; 33 | } -------------------------------------------------------------------------------- /old_version/old_version_0.5/base/mutexLock.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nocopyable.hpp" 3 | #include 4 | #include 5 | 6 | class MutexLock: noncopyable 7 | { 8 | public: 9 | MutexLock() 10 | { 11 | pthread_mutex_init(&mutex, NULL); 12 | } 13 | ~MutexLock() 14 | { 15 | pthread_mutex_lock(&mutex); 16 | pthread_mutex_destroy(&mutex); 17 | } 18 | void lock() 19 | { 20 | pthread_mutex_lock(&mutex); 21 | } 22 | void unlock() 23 | { 24 | pthread_mutex_unlock(&mutex); 25 | } 26 | pthread_mutex_t *get() 27 | { 28 | return &mutex; 29 | } 30 | private: 31 | pthread_mutex_t mutex; 32 | 33 | // 友元类不受访问权限影响 34 | private: 35 | friend class Condition; 36 | }; 37 | 38 | 39 | class MutexLockGuard: noncopyable 40 | { 41 | public: 42 | explicit MutexLockGuard(MutexLock &_mutex): 43 | mutex(_mutex) 44 | { 45 | mutex.lock(); 46 | } 47 | ~MutexLockGuard() 48 | { 49 | mutex.unlock(); 50 | } 51 | private: 52 | MutexLock &mutex; 53 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.5/base/nocopyable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | class noncopyable 3 | { 4 | protected: 5 | noncopyable() {} 6 | ~noncopyable() {} 7 | private: 8 | noncopyable(const noncopyable&); 9 | const noncopyable& operator=(const noncopyable&); 10 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.5/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | -------------------------------------------------------------------------------- /old_version/old_version_0.5/epoll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | #include "timer.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Epoll 10 | { 11 | public: 12 | typedef std::shared_ptr SP_ReqData; 13 | private: 14 | static const int MAXFDS = 1000; 15 | static epoll_event *events; 16 | static SP_ReqData fd2req[MAXFDS]; 17 | static int epoll_fd; 18 | static const std::string PATH; 19 | 20 | static TimerManager timer_manager; 21 | public: 22 | static int epoll_init(int maxevents, int listen_num); 23 | static int epoll_add(int fd, SP_ReqData request, __uint32_t events); 24 | static int epoll_mod(int fd, SP_ReqData request, __uint32_t events); 25 | static int epoll_del(int fd, __uint32_t events = (EPOLLIN | EPOLLET | EPOLLONESHOT)); 26 | static void my_epoll_wait(int listen_fd, int max_events, int timeout); 27 | static void acceptConnection(int listen_fd, int epoll_fd, const std::string path); 28 | static std::vector getEventsRequest(int listen_fd, int events_num, const std::string path); 29 | 30 | static void add_timer(SP_ReqData request_data, int timeout); 31 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.5/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/old_version/old_version_0.5/favicon.ico -------------------------------------------------------------------------------- /old_version/old_version_0.5/index.html: -------------------------------------------------------------------------------- 1 | Hello World ! -------------------------------------------------------------------------------- /old_version/old_version_0.5/main.cpp: -------------------------------------------------------------------------------- 1 | #include "requestData.h" 2 | #include "epoll.h" 3 | #include "threadpool.h" 4 | #include "util.h" 5 | #include "config.h" 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 | 19 | using namespace std; 20 | 21 | static const int MAXEVENTS = 5000; 22 | static const int LISTENQ = 1024; 23 | const int THREADPOOL_THREAD_NUM = 4; 24 | const int QUEUE_SIZE = 65535; 25 | 26 | const int PORT = 8888; 27 | const int ASK_STATIC_FILE = 1; 28 | const int ASK_IMAGE_STITCH = 2; 29 | 30 | const int TIMER_TIME_OUT = 500; 31 | 32 | void acceptConnection(int listen_fd, int epoll_fd, const string &path); 33 | 34 | int socket_bind_listen(int port) 35 | { 36 | // 检查port值,取正确区间范围 37 | if (port < 1024 || port > 65535) 38 | return -1; 39 | 40 | // 创建socket(IPv4 + TCP),返回监听描述符 41 | int listen_fd = 0; 42 | if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 43 | return -1; 44 | 45 | // 消除bind时"Address already in use"错误 46 | int optval = 1; 47 | if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) 48 | return -1; 49 | 50 | // 设置服务器IP和Port,和监听描述副绑定 51 | struct sockaddr_in server_addr; 52 | bzero((char*)&server_addr, sizeof(server_addr)); 53 | server_addr.sin_family = AF_INET; 54 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 55 | server_addr.sin_port = htons((unsigned short)port); 56 | if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) 57 | return -1; 58 | 59 | // 开始监听,最大等待队列长为LISTENQ 60 | if(listen(listen_fd, LISTENQ) == -1) 61 | return -1; 62 | 63 | // 无效监听描述符 64 | if(listen_fd == -1) 65 | { 66 | close(listen_fd); 67 | return -1; 68 | } 69 | return listen_fd; 70 | } 71 | 72 | 73 | int main() 74 | { 75 | #ifndef _PTHREADS 76 | printf("_PTHREADS is not defined !\n"); 77 | #endif 78 | handle_for_sigpipe(); 79 | if (Epoll::epoll_init(MAXEVENTS, LISTENQ) < 0) 80 | { 81 | perror("epoll init failed"); 82 | return 1; 83 | } 84 | if (ThreadPool::threadpool_create(THREADPOOL_THREAD_NUM, QUEUE_SIZE) < 0) 85 | { 86 | printf("Threadpool create failed\n"); 87 | return 1; 88 | } 89 | int listen_fd = socket_bind_listen(PORT); 90 | if (listen_fd < 0) 91 | { 92 | perror("socket bind failed"); 93 | return 1; 94 | } 95 | if (setSocketNonBlocking(listen_fd) < 0) 96 | { 97 | perror("set socket non block failed"); 98 | return 1; 99 | } 100 | shared_ptr request(new RequestData()); 101 | request->setFd(listen_fd); 102 | if (Epoll::epoll_add(listen_fd, request, EPOLLIN | EPOLLET) < 0) 103 | { 104 | perror("epoll add error"); 105 | return 1; 106 | } 107 | 108 | while (true) 109 | { 110 | //sleep(10); 111 | Epoll::my_epoll_wait(listen_fd, MAXEVENTS, -1); 112 | 113 | //ThreadPool::threadpool_destroy(); 114 | //break; 115 | } 116 | return 0; 117 | } -------------------------------------------------------------------------------- /old_version/old_version_0.5/requestData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "timer.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | using namespace cv; 14 | 15 | const int STATE_PARSE_URI = 1; 16 | const int STATE_PARSE_HEADERS = 2; 17 | const int STATE_RECV_BODY = 3; 18 | const int STATE_ANALYSIS = 4; 19 | const int STATE_FINISH = 5; 20 | 21 | const int MAX_BUFF = 4096; 22 | 23 | // 有请求出现但是读不到数据,可能是Request Aborted, 24 | // 或者来自网络的数据没有达到等原因, 25 | // 对这样的请求尝试超过一定的次数就抛弃 26 | const int AGAIN_MAX_TIMES = 200; 27 | 28 | const int PARSE_URI_AGAIN = -1; 29 | const int PARSE_URI_ERROR = -2; 30 | const int PARSE_URI_SUCCESS = 0; 31 | 32 | const int PARSE_HEADER_AGAIN = -1; 33 | const int PARSE_HEADER_ERROR = -2; 34 | const int PARSE_HEADER_SUCCESS = 0; 35 | 36 | const int ANALYSIS_ERROR = -2; 37 | const int ANALYSIS_SUCCESS = 0; 38 | 39 | const int METHOD_POST = 1; 40 | const int METHOD_GET = 2; 41 | const int HTTP_10 = 1; 42 | const int HTTP_11 = 2; 43 | 44 | const int EPOLL_WAIT_TIME = 500; 45 | 46 | class MimeType 47 | { 48 | private: 49 | static void init(); 50 | static std::unordered_map mime; 51 | MimeType(); 52 | MimeType(const MimeType &m); 53 | 54 | public: 55 | static std::string getMime(const std::string &suffix); 56 | 57 | private: 58 | static pthread_once_t once_control; 59 | }; 60 | 61 | enum HeadersState 62 | { 63 | h_start = 0, 64 | h_key, 65 | h_colon, 66 | h_spaces_after_colon, 67 | h_value, 68 | h_CR, 69 | h_LF, 70 | h_end_CR, 71 | h_end_LF 72 | }; 73 | 74 | class TimerNode; 75 | 76 | class RequestData : public std::enable_shared_from_this 77 | { 78 | private: 79 | std::string path; 80 | int fd; 81 | int epollfd; 82 | 83 | std::string inBuffer; 84 | std::string outBuffer; 85 | __uint32_t events; 86 | bool error; 87 | 88 | int method; 89 | int HTTPversion; 90 | std::string file_name; 91 | int now_read_pos; 92 | int state; 93 | int h_state; 94 | bool isfinish; 95 | bool keep_alive; 96 | std::unordered_map headers; 97 | std::weak_ptr timer; 98 | 99 | bool isAbleRead; 100 | bool isAbleWrite; 101 | 102 | private: 103 | int parse_URI(); 104 | int parse_Headers(); 105 | int analysisRequest(); 106 | 107 | Mat stitch(Mat &src) 108 | { 109 | return src; 110 | } 111 | 112 | public: 113 | 114 | RequestData(); 115 | RequestData(int _epollfd, int _fd, std::string _path); 116 | ~RequestData(); 117 | void linkTimer(std::shared_ptr mtimer); 118 | void reset(); 119 | void seperateTimer(); 120 | int getFd(); 121 | void setFd(int _fd); 122 | void handleRead(); 123 | void handleWrite(); 124 | void handleError(int fd, int err_num, std::string short_msg); 125 | void handleConn(); 126 | 127 | void disableReadAndWrite(); 128 | 129 | void enableRead(); 130 | 131 | void enableWrite(); 132 | 133 | bool canRead(); 134 | 135 | bool canWrite(); 136 | }; 137 | 138 | -------------------------------------------------------------------------------- /old_version/old_version_0.5/threadpool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | //#include "condition.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const int THREADPOOL_INVALID = -1; 10 | const int THREADPOOL_LOCK_FAILURE = -2; 11 | const int THREADPOOL_QUEUE_FULL = -3; 12 | const int THREADPOOL_SHUTDOWN = -4; 13 | const int THREADPOOL_THREAD_FAILURE = -5; 14 | const int THREADPOOL_GRACEFUL = 1; 15 | 16 | const int MAX_THREADS = 1024; 17 | const int MAX_QUEUE = 65535; 18 | 19 | typedef enum 20 | { 21 | immediate_shutdown = 1, 22 | graceful_shutdown = 2 23 | } ShutDownOption; 24 | 25 | struct ThreadPoolTask 26 | { 27 | std::function)> fun; 28 | std::shared_ptr args; 29 | }; 30 | 31 | void myHandler(std::shared_ptr req); 32 | 33 | class ThreadPool 34 | { 35 | private: 36 | static pthread_mutex_t lock; 37 | static pthread_cond_t notify; 38 | 39 | static std::vector threads; 40 | static std::vector queue; 41 | static int thread_count; 42 | static int queue_size; 43 | static int head; 44 | // tail 指向尾节点的下一节点 45 | static int tail; 46 | static int count; 47 | static int shutdown; 48 | static int started; 49 | public: 50 | static int threadpool_create(int _thread_count, int _queue_size); 51 | static int threadpool_add(std::shared_ptr args, std::function)> fun = myHandler); 52 | static int threadpool_destroy(ShutDownOption shutdown_option = graceful_shutdown); 53 | static int threadpool_free(); 54 | static void *threadpool_thread(void *args); 55 | }; 56 | -------------------------------------------------------------------------------- /old_version/old_version_0.5/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | #include "epoll.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | using namespace std; 12 | 13 | 14 | TimerNode::TimerNode(SP_ReqData _request_data, int timeout): 15 | deleted(false), 16 | request_data(_request_data) 17 | { 18 | //cout << "TimerNode()" << endl; 19 | struct timeval now; 20 | gettimeofday(&now, NULL); 21 | // 以毫秒计 22 | expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout; 23 | } 24 | 25 | TimerNode::~TimerNode() 26 | { 27 | //cout << "~TimerNode()" << endl; 28 | if (request_data) 29 | { 30 | Epoll::epoll_del(request_data->getFd()); 31 | } 32 | //request_data.reset(); 33 | // if (request_data) 34 | // { 35 | // cout << "request_data=" << request_data << endl; 36 | // delete request_data; 37 | // request_data = NULL; 38 | // } 39 | } 40 | 41 | void TimerNode::update(int timeout) 42 | { 43 | struct timeval now; 44 | gettimeofday(&now, NULL); 45 | expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout; 46 | } 47 | 48 | bool TimerNode::isvalid() 49 | { 50 | struct timeval now; 51 | gettimeofday(&now, NULL); 52 | size_t temp = ((now.tv_sec * 1000) + (now.tv_usec / 1000)); 53 | if (temp < expired_time) 54 | { 55 | return true; 56 | } 57 | else 58 | { 59 | this->setDeleted(); 60 | return false; 61 | } 62 | } 63 | 64 | void TimerNode::clearReq() 65 | { 66 | request_data.reset(); 67 | this->setDeleted(); 68 | } 69 | 70 | void TimerNode::setDeleted() 71 | { 72 | deleted = true; 73 | } 74 | 75 | bool TimerNode::isDeleted() const 76 | { 77 | return deleted; 78 | } 79 | 80 | size_t TimerNode::getExpTime() const 81 | { 82 | return expired_time; 83 | } 84 | 85 | TimerManager::TimerManager() 86 | { 87 | } 88 | 89 | TimerManager::~TimerManager() 90 | { 91 | } 92 | 93 | void TimerManager::addTimer(SP_ReqData request_data, int timeout) 94 | { 95 | SP_TimerNode new_node(new TimerNode(request_data, timeout)); 96 | { 97 | MutexLockGuard locker(lock); 98 | TimerNodeQueue.push(new_node); 99 | } 100 | request_data->linkTimer(new_node); 101 | } 102 | 103 | void TimerManager::addTimer(SP_TimerNode timer_node) 104 | { 105 | 106 | } 107 | 108 | /* 处理逻辑是这样的~ 109 | 因为(1) 优先队列不支持随机访问 110 | (2) 即使支持,随机删除某节点后破坏了堆的结构,需要重新更新堆结构。 111 | 所以对于被置为deleted的时间节点,会延迟到它(1)超时 或 (2)它前面的节点都被删除时,它才会被删除。 112 | 一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。 113 | 这样做有两个好处: 114 | (1) 第一个好处是不需要遍历优先队列,省时。 115 | (2) 第二个好处是给超时时间一个容忍的时间,就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除),如果监听的请求在超时后的下一次请求中又一次出现了, 116 | 就不用再重新申请RequestData节点了,这样可以继续重复利用前面的RequestData,减少了一次delete和一次new的时间。 117 | */ 118 | 119 | void TimerManager::handle_expired_event() 120 | { 121 | MutexLockGuard locker(lock); 122 | while (!TimerNodeQueue.empty()) 123 | { 124 | SP_TimerNode ptimer_now = TimerNodeQueue.top(); 125 | if (ptimer_now->isDeleted()) 126 | { 127 | TimerNodeQueue.pop(); 128 | //delete ptimer_now; 129 | } 130 | else if (ptimer_now->isvalid() == false) 131 | { 132 | TimerNodeQueue.pop(); 133 | //delete ptimer_now; 134 | } 135 | else 136 | { 137 | break; 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /old_version/old_version_0.5/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | #include "./base/nocopyable.hpp" 4 | #include "./base/mutexLock.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class RequestData; 11 | 12 | class TimerNode 13 | { 14 | typedef std::shared_ptr SP_ReqData; 15 | private: 16 | bool deleted; 17 | size_t expired_time; 18 | SP_ReqData request_data; 19 | public: 20 | TimerNode(SP_ReqData _request_data, int timeout); 21 | ~TimerNode(); 22 | void update(int timeout); 23 | bool isvalid(); 24 | void clearReq(); 25 | void setDeleted(); 26 | bool isDeleted() const; 27 | size_t getExpTime() const; 28 | }; 29 | 30 | struct timerCmp 31 | { 32 | bool operator()(std::shared_ptr &a, std::shared_ptr &b) const 33 | { 34 | return a->getExpTime() > b->getExpTime(); 35 | } 36 | }; 37 | 38 | class TimerManager 39 | { 40 | typedef std::shared_ptr SP_ReqData; 41 | typedef std::shared_ptr SP_TimerNode; 42 | private: 43 | std::priority_queue, timerCmp> TimerNodeQueue; 44 | MutexLock lock; 45 | public: 46 | TimerManager(); 47 | ~TimerManager(); 48 | void addTimer(SP_ReqData request_data, int timeout); 49 | void addTimer(SP_TimerNode timer_node); 50 | void handle_expired_event(); 51 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.5/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | ssize_t readn(int fd, void *buff, size_t n); 6 | ssize_t readn(int fd, std::string &inBuffer); 7 | ssize_t writen(int fd, void *buff, size_t n); 8 | ssize_t writen(int fd, std::string &sbuff); 9 | void handle_for_sigpipe(); 10 | int setSocketNonBlocking(int fd); -------------------------------------------------------------------------------- /old_version/old_version_0.6/AsyncLogging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CountDownLatch.h" 4 | #include "MutexLock.h" 5 | #include "Thread.h" 6 | #include "LogStream.h" 7 | #include "noncopyable.h" 8 | #include 9 | #include 10 | #include 11 | 12 | // extern const int kSmallBuffer; 13 | // extern const int kLargeBuffer; 14 | 15 | // template class FixedBuffer; 16 | // template class FixedBuffer; 17 | 18 | class AsyncLogging : noncopyable 19 | { 20 | public: 21 | 22 | AsyncLogging(const std::string basename, int flushInterval = 2); 23 | 24 | ~AsyncLogging() 25 | { 26 | if (running_) 27 | { 28 | stop(); 29 | } 30 | } 31 | 32 | void append(const char* logline, int len); 33 | 34 | void start() 35 | { 36 | running_ = true; 37 | thread_.start(); 38 | latch_.wait(); 39 | } 40 | 41 | void stop() 42 | { 43 | running_ = false; 44 | cond_.notify(); 45 | thread_.join(); 46 | } 47 | 48 | private: 49 | 50 | // declare but not define, prevent compiler-synthesized functions 51 | AsyncLogging(const AsyncLogging&); // ptr_container 52 | void operator=(const AsyncLogging&); // ptr_container 53 | 54 | void threadFunc(); 55 | 56 | typedef FixedBuffer Buffer; 57 | typedef std::vector> BufferVector; 58 | typedef std::shared_ptr BufferPtr; 59 | 60 | const int flushInterval_; 61 | bool running_; 62 | std::string basename_; 63 | Thread thread_; 64 | MutexLock mutex_; 65 | Condition cond_; 66 | BufferPtr currentBuffer_; 67 | BufferPtr nextBuffer_; 68 | BufferVector buffers_; 69 | CountDownLatch latch_; 70 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/Condition.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "noncopyable.h" 3 | #include "MutexLock.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Condition: noncopyable 11 | { 12 | public: 13 | explicit Condition(MutexLock &_mutex): 14 | mutex(_mutex) 15 | { 16 | pthread_cond_init(&cond, NULL); 17 | } 18 | ~Condition() 19 | { 20 | pthread_cond_destroy(&cond); 21 | } 22 | void wait() 23 | { 24 | pthread_cond_wait(&cond, mutex.get()); 25 | } 26 | void notify() 27 | { 28 | pthread_cond_signal(&cond); 29 | } 30 | void notifyAll() 31 | { 32 | pthread_cond_broadcast(&cond); 33 | } 34 | bool waitForSeconds(int seconds) 35 | { 36 | struct timespec abstime; 37 | clock_gettime(CLOCK_REALTIME, &abstime); 38 | abstime.tv_sec += static_cast(seconds); 39 | return ETIMEDOUT == pthread_cond_timedwait(&cond, mutex.get(), &abstime); 40 | } 41 | private: 42 | MutexLock &mutex; 43 | pthread_cond_t cond; 44 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/CountDownLatch.cpp: -------------------------------------------------------------------------------- 1 | #include "CountDownLatch.h" 2 | 3 | 4 | CountDownLatch::CountDownLatch(int count) 5 | : mutex_(), 6 | condition_(mutex_), 7 | count_(count) 8 | { 9 | } 10 | 11 | void CountDownLatch::wait() 12 | { 13 | MutexLockGuard lock(mutex_); 14 | while (count_ > 0) 15 | { 16 | condition_.wait(); 17 | } 18 | } 19 | 20 | void CountDownLatch::countDown() 21 | { 22 | MutexLockGuard lock(mutex_); 23 | --count_; 24 | if (count_ == 0) 25 | { 26 | condition_.notifyAll(); 27 | } 28 | } 29 | 30 | int CountDownLatch::getCount() const 31 | { 32 | MutexLockGuard lock(mutex_); 33 | return count_; 34 | } -------------------------------------------------------------------------------- /old_version/old_version_0.6/CountDownLatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Condition.h" 4 | #include "MutexLock.h" 5 | 6 | #include "noncopyable.h" 7 | 8 | class CountDownLatch : noncopyable 9 | { 10 | public: 11 | explicit CountDownLatch(int count); 12 | void wait(); 13 | void countDown(); 14 | int getCount() const; 15 | 16 | private: 17 | mutable MutexLock mutex_; 18 | Condition condition_; 19 | int count_; 20 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/CurrentThread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /* 5 | namespace CurrendThread 6 | { 7 | // internal 8 | extern __thread int t_cachedTid; 9 | extern __thread char t_tidString[32]; 10 | extern __thread int t_tidStringLength; 11 | extern __thread const char* t_threadName; 12 | void cacheTid(); 13 | inline int tid() 14 | { 15 | if (__builtin_expect(t_cachedTid == 0, 0)) 16 | { 17 | cacheTid(); 18 | } 19 | return t_cachedTid; 20 | } 21 | 22 | inline const char* tidString() // for logging 23 | { 24 | return t_tidString; 25 | } 26 | 27 | inline int tidStringLength() // for logging 28 | { 29 | return t_tidStringLength; 30 | } 31 | 32 | inline const char* name() 33 | { 34 | return t_threadName; 35 | } 36 | } 37 | */ 38 | -------------------------------------------------------------------------------- /old_version/old_version_0.6/FileUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "FileUtil.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | AppendFile::AppendFile(string filename) 12 | : fp_(fopen(filename.c_str(), "ae")) 13 | { 14 | setbuffer(fp_, buffer_, sizeof buffer_); 15 | } 16 | 17 | AppendFile::~AppendFile() 18 | { 19 | fclose(fp_); 20 | } 21 | 22 | void AppendFile::append(const char* logline, const size_t len) 23 | { 24 | size_t n = write(logline, len); 25 | size_t remain = len - n; 26 | while (remain > 0) 27 | { 28 | size_t x = write(logline + n, remain); 29 | if (x == 0) 30 | { 31 | int err = ferror(fp_); 32 | if (err) 33 | { 34 | fprintf(stderr, "AppendFile::append() failed !\n"); 35 | } 36 | break; 37 | } 38 | n += x; 39 | remain = len - n; 40 | } 41 | } 42 | 43 | void AppendFile::flush() 44 | { 45 | fflush(fp_); 46 | } 47 | 48 | size_t AppendFile::write(const char* logline, size_t len) 49 | { 50 | // #undef fwrite_unlocked 51 | return ::fwrite_unlocked(logline, 1, len, fp_); 52 | } -------------------------------------------------------------------------------- /old_version/old_version_0.6/FileUtil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "noncopyable.h" 4 | #include 5 | 6 | class AppendFile : noncopyable 7 | { 8 | public: 9 | explicit AppendFile(std::string filename); 10 | ~AppendFile(); 11 | void append(const char* logline, const size_t len); 12 | void flush(); 13 | 14 | private: 15 | size_t write(const char* logline, size_t len); 16 | FILE* fp_; 17 | char buffer_[64*1024]; 18 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/LogFile.cpp: -------------------------------------------------------------------------------- 1 | #include "LogFile.h" 2 | 3 | #include "FileUtil.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | // "/logfile.log" 11 | LogFile::LogFile(const string& basename, int flushEveryN) 12 | : basename_(basename), 13 | flushEveryN_(flushEveryN), 14 | count_(0), 15 | mutex_(new MutexLock) 16 | { 17 | //assert(basename.find('/') >= 0); 18 | file_.reset(new AppendFile(basename)); 19 | } 20 | 21 | LogFile::~LogFile() 22 | { 23 | } 24 | 25 | void LogFile::append(const char* logline, int len) 26 | { 27 | MutexLockGuard lock(*mutex_); 28 | append_unlocked(logline, len); 29 | } 30 | 31 | void LogFile::flush() 32 | { 33 | MutexLockGuard lock(*mutex_); 34 | file_->flush(); 35 | } 36 | 37 | void LogFile::append_unlocked(const char* logline, int len) 38 | { 39 | file_->append(logline, len); 40 | ++count_; 41 | if (count_ >= flushEveryN_) 42 | { 43 | count_ = 0; 44 | file_->flush(); 45 | } 46 | } -------------------------------------------------------------------------------- /old_version/old_version_0.6/LogFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "FileUtil.h" 3 | #include "MutexLock.h" 4 | #include "noncopyable.h" 5 | #include 6 | #include 7 | 8 | class LogFile : noncopyable 9 | { 10 | public: 11 | LogFile(const std::string& basename, int flushEveryN = 1024); 12 | ~LogFile(); 13 | 14 | void append(const char* logline, int len); 15 | void flush(); 16 | bool rollFile(); 17 | 18 | private: 19 | void append_unlocked(const char* logline, int len); 20 | 21 | const std::string basename_; 22 | const int flushEveryN_; 23 | 24 | int count_; 25 | std::unique_ptr mutex_; 26 | std::unique_ptr file_; 27 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/LogStream.cpp: -------------------------------------------------------------------------------- 1 | #include "LogStream.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | const char digits[] = "9876543210123456789"; 12 | const char* zero = digits + 9; 13 | 14 | const char digitsHex[] = "0123456789ABCDEF"; 15 | 16 | // Efficient Integer to String Conversions, by Matthew Wilson. 17 | template 18 | size_t convert(char buf[], T value) 19 | { 20 | T i = value; 21 | char* p = buf; 22 | 23 | do 24 | { 25 | int lsd = static_cast(i % 10); 26 | i /= 10; 27 | *p++ = zero[lsd]; 28 | } while (i != 0); 29 | 30 | if (value < 0) 31 | { 32 | *p++ = '-'; 33 | } 34 | *p = '\0'; 35 | std::reverse(buf, p); 36 | 37 | return p - buf; 38 | } 39 | 40 | 41 | template class FixedBuffer; 42 | template class FixedBuffer; 43 | 44 | 45 | template 46 | void LogStream::formatInteger(T v) 47 | { 48 | if (buffer_.avail() >= kMaxNumericSize) 49 | { 50 | size_t len = convert(buffer_.current(), v); 51 | buffer_.add(len); 52 | } 53 | } 54 | 55 | LogStream& LogStream::operator<<(short v) 56 | { 57 | *this << static_cast(v); 58 | return *this; 59 | } 60 | 61 | LogStream& LogStream::operator<<(unsigned short v) 62 | { 63 | *this << static_cast(v); 64 | return *this; 65 | } 66 | 67 | LogStream& LogStream::operator<<(int v) 68 | { 69 | formatInteger(v); 70 | return *this; 71 | } 72 | 73 | LogStream& LogStream::operator<<(unsigned int v) 74 | { 75 | formatInteger(v); 76 | return *this; 77 | } 78 | 79 | LogStream& LogStream::operator<<(long v) 80 | { 81 | formatInteger(v); 82 | return *this; 83 | } 84 | 85 | LogStream& LogStream::operator<<(unsigned long v) 86 | { 87 | formatInteger(v); 88 | return *this; 89 | } 90 | 91 | LogStream& LogStream::operator<<(long long v) 92 | { 93 | formatInteger(v); 94 | return *this; 95 | } 96 | 97 | LogStream& LogStream::operator<<(unsigned long long v) 98 | { 99 | formatInteger(v); 100 | return *this; 101 | } 102 | 103 | // FIXME: replace this with Grisu3 by Florian Loitsch. 104 | LogStream& LogStream::operator<<(double v) 105 | { 106 | if (buffer_.avail() >= kMaxNumericSize) 107 | { 108 | int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12g", v); 109 | buffer_.add(len); 110 | } 111 | return *this; 112 | } -------------------------------------------------------------------------------- /old_version/old_version_0.6/LogStream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "noncopyable.h" 6 | //#include "AsyncLogging.h" 7 | 8 | class AsyncLogging; 9 | const int kSmallBuffer = 4000; 10 | const int kLargeBuffer = 4000*1000; 11 | 12 | template 13 | class FixedBuffer :noncopyable 14 | { 15 | public: 16 | FixedBuffer() 17 | : cur_(data_) 18 | { 19 | } 20 | 21 | ~FixedBuffer() 22 | { 23 | } 24 | 25 | void append(const char* buf, size_t len) 26 | { 27 | if (avail() > len) 28 | { 29 | memcpy(cur_, buf, len); 30 | cur_ += len; 31 | } 32 | } 33 | 34 | const char* data() const { return data_; } 35 | int length() const { return static_cast(cur_ - data_); } 36 | 37 | // write to data_ directly 38 | char* current() { return cur_; } 39 | int avail() const { return static_cast(end() - cur_); } 40 | void add(size_t len) { cur_ += len; } 41 | 42 | void reset() { cur_ = data_; } 43 | void bzero() { memset(data_, 0, sizeof data_); } 44 | 45 | 46 | private: 47 | const char* end() const { return data_ + sizeof data_; } 48 | // Must be outline function for cookies. 49 | 50 | char data_[SIZE]; 51 | char* cur_; 52 | }; 53 | 54 | 55 | 56 | class LogStream : noncopyable 57 | { 58 | typedef LogStream self; 59 | public: 60 | typedef FixedBuffer Buffer; 61 | 62 | self& operator<<(bool v) 63 | { 64 | buffer_.append(v ? "1" : "0", 1); 65 | return *this; 66 | } 67 | 68 | self& operator<<(short); 69 | self& operator<<(unsigned short); 70 | self& operator<<(int); 71 | self& operator<<(unsigned int); 72 | self& operator<<(long); 73 | self& operator<<(unsigned long); 74 | self& operator<<(long long); 75 | self& operator<<(unsigned long long); 76 | 77 | self& operator<<(const void*); 78 | 79 | self& operator<<(float v) 80 | { 81 | *this << static_cast(v); 82 | return *this; 83 | } 84 | self& operator<<(double); 85 | // self& operator<<(long double); 86 | 87 | self& operator<<(char v) 88 | { 89 | buffer_.append(&v, 1); 90 | return *this; 91 | } 92 | 93 | self& operator<<(const char* str) 94 | { 95 | if (str) 96 | { 97 | buffer_.append(str, strlen(str)); 98 | } 99 | else 100 | { 101 | buffer_.append("(null)", 6); 102 | } 103 | return *this; 104 | } 105 | 106 | self& operator<<(const unsigned char* str) 107 | { 108 | return operator<<(reinterpret_cast(str)); 109 | } 110 | 111 | self& operator<<(const std::string& v) 112 | { 113 | buffer_.append(v.c_str(), v.size()); 114 | return *this; 115 | } 116 | 117 | void append(const char* data, int len) { buffer_.append(data, len); } 118 | const Buffer& buffer() const { return buffer_; } 119 | void resetBuffer() { buffer_.reset(); } 120 | 121 | private: 122 | void staticCheck(); 123 | 124 | template 125 | void formatInteger(T); 126 | 127 | Buffer buffer_; 128 | 129 | static const int kMaxNumericSize = 32; 130 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/Logging.cpp: -------------------------------------------------------------------------------- 1 | #include "Logging.h" 2 | #include "CurrentThread.hpp" 3 | #include "Thread.h" 4 | #include 5 | 6 | #include "AsyncLogging.h" 7 | 8 | #include 9 | using namespace std; 10 | 11 | 12 | static pthread_once_t once_control_ = PTHREAD_ONCE_INIT; 13 | static AsyncLogging *AsyncLogger_; 14 | 15 | void once_init() 16 | { 17 | AsyncLogger_ = new AsyncLogging(std::string("/linya_web_server.log")); 18 | AsyncLogger_->start(); 19 | } 20 | 21 | void output(const char* msg, int len) 22 | { 23 | pthread_once(&once_control_, once_init); 24 | AsyncLogger_->append(msg, len); 25 | } 26 | 27 | Logger::Impl::Impl(const char *fileName, int line) 28 | : stream_(), 29 | basename_(fileName), 30 | line_(line) 31 | { 32 | //formatTime(); 33 | //CurrentThread::tid(); 34 | //stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength()); 35 | } 36 | 37 | void Logger::Impl::formatTime() 38 | { 39 | // int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch(); 40 | // time_t seconds = static_cast(microSecondsSinceEpoch / Timestamp::kMicroSecondsPerSecond); 41 | // int microseconds = static_cast(microSecondsSinceEpoch % Timestamp::kMicroSecondsPerSecond); 42 | // if (seconds != t_lastSecond) 43 | // { 44 | // t_lastSecond = seconds; 45 | // struct tm tm_time; 46 | // if (g_logTimeZone.valid()) 47 | // { 48 | // tm_time = g_logTimeZone.toLocalTime(seconds); 49 | // } 50 | // else 51 | // { 52 | // ::gmtime_r(&seconds, &tm_time); // FIXME TimeZone::fromUtcTime 53 | // } 54 | 55 | // int len = snprintf(t_time, sizeof(t_time), "%4d%02d%02d %02d:%02d:%02d", 56 | // tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, 57 | // tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec); 58 | // assert(len == 17); (void)len; 59 | // } 60 | 61 | // if (g_logTimeZone.valid()) 62 | // { 63 | // Fmt us(".%06d ", microseconds); 64 | // assert(us.length() == 8); 65 | // stream_ << T(t_time, 17) << T(us.data(), 8); 66 | // } 67 | // else 68 | // { 69 | // Fmt us(".%06dZ ", microseconds); 70 | // assert(us.length() == 9); 71 | // stream_ << T(t_time, 17) << T(us.data(), 9); 72 | // } 73 | } 74 | 75 | Logger::Logger(const char *fileName, int line) 76 | : impl_(fileName, line) 77 | { 78 | } 79 | 80 | Logger::~Logger() 81 | { 82 | impl_.stream_ << " - " << impl_.basename_ << ':' << impl_.line_ << '\n'; 83 | const LogStream::Buffer& buf(stream().buffer()); 84 | output(buf.data(), buf.length()); 85 | } -------------------------------------------------------------------------------- /old_version/old_version_0.6/Logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "LogStream.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class AsyncLogging; 9 | 10 | class Logger 11 | { 12 | public: 13 | ~Logger(); 14 | Logger(const char *fileName, int line); 15 | LogStream& stream() { return impl_.stream_; } 16 | 17 | private: 18 | class Impl 19 | { 20 | public: 21 | Impl(const char *fileName, int line); 22 | void formatTime(); 23 | 24 | LogStream stream_; 25 | int line_; 26 | std::string basename_; 27 | }; 28 | Impl impl_; 29 | }; 30 | 31 | #define LOG Logger(__FILE__, __LINE__).stream() -------------------------------------------------------------------------------- /old_version/old_version_0.6/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE := $(wildcard *.cpp) 2 | OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE))) 3 | 4 | TARGET := myserver 5 | CC := g++ 6 | LIBS := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui 7 | INCLUDE:= -I./usr/local/include/opencv 8 | CFLAGS := -std=c++11 -g -Wall -O0 $(INCLUDE) -D_PTHREADS 9 | CXXFLAGS:= $(CFLAGS) 10 | 11 | .PHONY : objs clean veryclean rebuild all 12 | all : $(TARGET) 13 | objs : $(OBJS) 14 | rebuild: veryclean all 15 | clean : 16 | rm -fr *.o 17 | veryclean : clean 18 | rm -rf $(TARGET) 19 | 20 | $(TARGET) : $(OBJS) 21 | $(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) -------------------------------------------------------------------------------- /old_version/old_version_0.6/Makefile1: -------------------------------------------------------------------------------- 1 | cc=g++ -std=c++11 2 | main: main.o RequestData.o epoll.o threadpool.o 3 | $(cc) -o main main.o RequestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml 4 | main.o: main.cpp 5 | $(cc) -I/usr/local/include/opencv -c main.cpp 6 | RequestData.o: requestData.h RequestData.cpp 7 | $(cc) -I/usr/local/include/opencv -c RequestData.cpp 8 | epoll.o: epoll.h epoll.cpp 9 | $(cc) -I/usr/local/include/opencv -c epoll.cpp 10 | threadpool.o: threadpool.h threadpool.cpp 11 | $(cc) -I/usr/local/include/opencv -c threadpool.cpp 12 | .PHONY: clean 13 | clean: 14 | -rm main *.o 15 | 16 | 17 | 18 | 19 | .PHONY : everything objs clean veryclean rebuild 20 | everything : 21 | $(TARGET) 22 | all : 23 | $(TARGET) 24 | objs : 25 | $(OBJS) 26 | rebuild: 27 | veryclean everything 28 | clean : 29 | rm -fr *.o 30 | veryclean : 31 | clean rm -fr $(TARGET) -------------------------------------------------------------------------------- /old_version/old_version_0.6/MutexLock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "noncopyable.h" 3 | #include 4 | #include 5 | 6 | class MutexLock: noncopyable 7 | { 8 | public: 9 | MutexLock() 10 | { 11 | pthread_mutex_init(&mutex, NULL); 12 | } 13 | ~MutexLock() 14 | { 15 | pthread_mutex_lock(&mutex); 16 | pthread_mutex_destroy(&mutex); 17 | } 18 | void lock() 19 | { 20 | pthread_mutex_lock(&mutex); 21 | } 22 | void unlock() 23 | { 24 | pthread_mutex_unlock(&mutex); 25 | } 26 | pthread_mutex_t *get() 27 | { 28 | return &mutex; 29 | } 30 | private: 31 | pthread_mutex_t mutex; 32 | 33 | // 友元类不受访问权限影响 34 | private: 35 | friend class Condition; 36 | }; 37 | 38 | 39 | class MutexLockGuard: noncopyable 40 | { 41 | public: 42 | explicit MutexLockGuard(MutexLock &_mutex): 43 | mutex(_mutex) 44 | { 45 | mutex.lock(); 46 | } 47 | ~MutexLockGuard() 48 | { 49 | mutex.unlock(); 50 | } 51 | private: 52 | MutexLock &mutex; 53 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/Thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CountDownLatch.h" 4 | #include 5 | #include "noncopyable.h" 6 | #include 7 | #include 8 | #include 9 | 10 | class Thread : noncopyable 11 | { 12 | public: 13 | typedef std::function ThreadFunc; 14 | 15 | explicit Thread(const ThreadFunc&, const std::string& name = std::string()); 16 | ~Thread(); 17 | 18 | void start(); 19 | int join(); 20 | 21 | bool started() const { return started_; } 22 | pid_t tid() const { return tid_; } 23 | const std::string& name() const { return name_; } 24 | 25 | 26 | private: 27 | void setDefaultName(); 28 | 29 | bool started_; 30 | bool joined_; 31 | pthread_t pthreadId_; 32 | pid_t tid_; 33 | ThreadFunc func_; 34 | std::string name_; 35 | CountDownLatch latch_; 36 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | -------------------------------------------------------------------------------- /old_version/old_version_0.6/epoll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | #include "timer.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Epoll 10 | { 11 | public: 12 | typedef std::shared_ptr SP_ReqData; 13 | private: 14 | static const int MAXFDS = 1000; 15 | static epoll_event *events; 16 | static SP_ReqData fd2req[MAXFDS]; 17 | static int epoll_fd; 18 | static const std::string PATH; 19 | 20 | static TimerManager timer_manager; 21 | public: 22 | static int epoll_init(int maxevents, int listen_num); 23 | static int epoll_add(int fd, SP_ReqData request, __uint32_t events); 24 | static int epoll_mod(int fd, SP_ReqData request, __uint32_t events); 25 | static int epoll_del(int fd, __uint32_t events = (EPOLLIN | EPOLLET | EPOLLONESHOT)); 26 | static void my_epoll_wait(int listen_fd, int max_events, int timeout); 27 | static void acceptConnection(int listen_fd, int epoll_fd, const std::string path); 28 | static std::vector getEventsRequest(int listen_fd, int events_num, const std::string path); 29 | 30 | static void add_timer(SP_ReqData request_data, int timeout); 31 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyacool/WebServer/7e18602f15c5c64e006091aa03e04b927c255983/old_version/old_version_0.6/favicon.ico -------------------------------------------------------------------------------- /old_version/old_version_0.6/index.html: -------------------------------------------------------------------------------- 1 | Hello World ! -------------------------------------------------------------------------------- /old_version/old_version_0.6/main.cpp: -------------------------------------------------------------------------------- 1 | #include "requestData.h" 2 | #include "epoll.h" 3 | #include "threadpool.h" 4 | #include "util.h" 5 | #include "config.h" 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 "Logging.h" 19 | 20 | using namespace std; 21 | 22 | static const int MAXEVENTS = 5000; 23 | static const int LISTENQ = 1024; 24 | const int THREADPOOL_THREAD_NUM = 4; 25 | const int QUEUE_SIZE = 65535; 26 | 27 | const int PORT = 8888; 28 | const int ASK_STATIC_FILE = 1; 29 | const int ASK_IMAGE_STITCH = 2; 30 | 31 | const int TIMER_TIME_OUT = 500; 32 | 33 | void acceptConnection(int listen_fd, int epoll_fd, const string &path); 34 | 35 | int socket_bind_listen(int port) 36 | { 37 | // 检查port值,取正确区间范围 38 | if (port < 1024 || port > 65535) 39 | return -1; 40 | 41 | // 创建socket(IPv4 + TCP),返回监听描述符 42 | int listen_fd = 0; 43 | if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 44 | return -1; 45 | 46 | // 消除bind时"Address already in use"错误 47 | int optval = 1; 48 | if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) 49 | return -1; 50 | 51 | // 设置服务器IP和Port,和监听描述副绑定 52 | struct sockaddr_in server_addr; 53 | bzero((char*)&server_addr, sizeof(server_addr)); 54 | server_addr.sin_family = AF_INET; 55 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 56 | server_addr.sin_port = htons((unsigned short)port); 57 | if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) 58 | return -1; 59 | 60 | // 开始监听,最大等待队列长为LISTENQ 61 | if(listen(listen_fd, LISTENQ) == -1) 62 | return -1; 63 | 64 | // 无效监听描述符 65 | if(listen_fd == -1) 66 | { 67 | close(listen_fd); 68 | return -1; 69 | } 70 | return listen_fd; 71 | } 72 | 73 | 74 | int main() 75 | { 76 | LOG << "yingyingying"; 77 | LOG << 654 << 3.2 << 0 << string("fg") << true; 78 | #ifndef _PTHREADS 79 | LOG << "_PTHREADS is not defined !"; 80 | #endif 81 | handle_for_sigpipe(); 82 | if (Epoll::epoll_init(MAXEVENTS, LISTENQ) < 0) 83 | { 84 | perror("epoll init failed"); 85 | return 1; 86 | } 87 | if (ThreadPool::threadpool_create(THREADPOOL_THREAD_NUM, QUEUE_SIZE) < 0) 88 | { 89 | printf("Threadpool create failed\n"); 90 | return 1; 91 | } 92 | int listen_fd = socket_bind_listen(PORT); 93 | if (listen_fd < 0) 94 | { 95 | perror("socket bind failed"); 96 | return 1; 97 | } 98 | if (setSocketNonBlocking(listen_fd) < 0) 99 | { 100 | perror("set socket non block failed"); 101 | return 1; 102 | } 103 | shared_ptr request(new RequestData()); 104 | request->setFd(listen_fd); 105 | if (Epoll::epoll_add(listen_fd, request, EPOLLIN | EPOLLET) < 0) 106 | { 107 | perror("epoll add error"); 108 | return 1; 109 | } 110 | 111 | while (true) 112 | { 113 | //sleep(10); 114 | Epoll::my_epoll_wait(listen_fd, MAXEVENTS, -1); 115 | 116 | //ThreadPool::threadpool_destroy(); 117 | //break; 118 | } 119 | return 0; 120 | } -------------------------------------------------------------------------------- /old_version/old_version_0.6/noncopyable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | class noncopyable 3 | { 4 | protected: 5 | noncopyable() {} 6 | ~noncopyable() {} 7 | private: 8 | noncopyable(const noncopyable&); 9 | const noncopyable& operator=(const noncopyable&); 10 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/requestData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "timer.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | using namespace cv; 14 | 15 | const int STATE_PARSE_URI = 1; 16 | const int STATE_PARSE_HEADERS = 2; 17 | const int STATE_RECV_BODY = 3; 18 | const int STATE_ANALYSIS = 4; 19 | const int STATE_FINISH = 5; 20 | 21 | const int MAX_BUFF = 4096; 22 | 23 | // 有请求出现但是读不到数据,可能是Request Aborted, 24 | // 或者来自网络的数据没有达到等原因, 25 | // 对这样的请求尝试超过一定的次数就抛弃 26 | const int AGAIN_MAX_TIMES = 200; 27 | 28 | const int PARSE_URI_AGAIN = -1; 29 | const int PARSE_URI_ERROR = -2; 30 | const int PARSE_URI_SUCCESS = 0; 31 | 32 | const int PARSE_HEADER_AGAIN = -1; 33 | const int PARSE_HEADER_ERROR = -2; 34 | const int PARSE_HEADER_SUCCESS = 0; 35 | 36 | const int ANALYSIS_ERROR = -2; 37 | const int ANALYSIS_SUCCESS = 0; 38 | 39 | const int METHOD_POST = 1; 40 | const int METHOD_GET = 2; 41 | const int HTTP_10 = 1; 42 | const int HTTP_11 = 2; 43 | 44 | const int EPOLL_WAIT_TIME = 500; 45 | 46 | class MimeType 47 | { 48 | private: 49 | static void init(); 50 | static std::unordered_map mime; 51 | MimeType(); 52 | MimeType(const MimeType &m); 53 | 54 | public: 55 | static std::string getMime(const std::string &suffix); 56 | 57 | private: 58 | static pthread_once_t once_control; 59 | }; 60 | 61 | enum HeadersState 62 | { 63 | h_start = 0, 64 | h_key, 65 | h_colon, 66 | h_spaces_after_colon, 67 | h_value, 68 | h_CR, 69 | h_LF, 70 | h_end_CR, 71 | h_end_LF 72 | }; 73 | 74 | class TimerNode; 75 | 76 | class RequestData : public std::enable_shared_from_this 77 | { 78 | private: 79 | std::string path; 80 | int fd; 81 | int epollfd; 82 | 83 | std::string inBuffer; 84 | std::string outBuffer; 85 | __uint32_t events; 86 | bool error; 87 | 88 | int method; 89 | int HTTPversion; 90 | std::string file_name; 91 | int now_read_pos; 92 | int state; 93 | int h_state; 94 | bool isfinish; 95 | bool keep_alive; 96 | std::unordered_map headers; 97 | std::weak_ptr timer; 98 | 99 | bool isAbleRead; 100 | bool isAbleWrite; 101 | 102 | private: 103 | int parse_URI(); 104 | int parse_Headers(); 105 | int analysisRequest(); 106 | 107 | Mat stitch(Mat &src) 108 | { 109 | return src; 110 | } 111 | 112 | public: 113 | 114 | RequestData(); 115 | RequestData(int _epollfd, int _fd, std::string _path); 116 | ~RequestData(); 117 | void linkTimer(std::shared_ptr mtimer); 118 | void reset(); 119 | void seperateTimer(); 120 | int getFd(); 121 | void setFd(int _fd); 122 | void handleRead(); 123 | void handleWrite(); 124 | void handleError(int fd, int err_num, std::string short_msg); 125 | void handleConn(); 126 | 127 | void disableReadAndWrite(); 128 | 129 | void enableRead(); 130 | 131 | void enableWrite(); 132 | 133 | bool canRead(); 134 | 135 | bool canWrite(); 136 | }; 137 | 138 | -------------------------------------------------------------------------------- /old_version/old_version_0.6/threadpool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | //#include "Condition.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const int THREADPOOL_INVALID = -1; 10 | const int THREADPOOL_LOCK_FAILURE = -2; 11 | const int THREADPOOL_QUEUE_FULL = -3; 12 | const int THREADPOOL_SHUTDOWN = -4; 13 | const int THREADPOOL_THREAD_FAILURE = -5; 14 | const int THREADPOOL_GRACEFUL = 1; 15 | 16 | const int MAX_THREADS = 1024; 17 | const int MAX_QUEUE = 65535; 18 | 19 | typedef enum 20 | { 21 | immediate_shutdown = 1, 22 | graceful_shutdown = 2 23 | } ShutDownOption; 24 | 25 | struct ThreadPoolTask 26 | { 27 | std::function)> fun; 28 | std::shared_ptr args; 29 | }; 30 | 31 | void myHandler(std::shared_ptr req); 32 | 33 | class ThreadPool 34 | { 35 | private: 36 | static pthread_mutex_t lock; 37 | static pthread_cond_t notify; 38 | 39 | static std::vector threads; 40 | static std::vector queue; 41 | static int thread_count; 42 | static int queue_size; 43 | static int head; 44 | // tail 指向尾节点的下一节点 45 | static int tail; 46 | static int count; 47 | static int shutdown; 48 | static int started; 49 | public: 50 | static int threadpool_create(int _thread_count, int _queue_size); 51 | static int threadpool_add(std::shared_ptr args, std::function)> fun = myHandler); 52 | static int threadpool_destroy(ShutDownOption shutdown_option = graceful_shutdown); 53 | static int threadpool_free(); 54 | static void *threadpool_thread(void *args); 55 | }; 56 | -------------------------------------------------------------------------------- /old_version/old_version_0.6/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | #include "epoll.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | using namespace std; 12 | 13 | 14 | TimerNode::TimerNode(SP_ReqData _request_data, int timeout): 15 | deleted(false), 16 | request_data(_request_data) 17 | { 18 | //cout << "TimerNode()" << endl; 19 | struct timeval now; 20 | gettimeofday(&now, NULL); 21 | // 以毫秒计 22 | expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout; 23 | } 24 | 25 | TimerNode::~TimerNode() 26 | { 27 | //cout << "~TimerNode()" << endl; 28 | if (request_data) 29 | { 30 | Epoll::epoll_del(request_data->getFd()); 31 | } 32 | //request_data.reset(); 33 | // if (request_data) 34 | // { 35 | // cout << "request_data=" << request_data << endl; 36 | // delete request_data; 37 | // request_data = NULL; 38 | // } 39 | } 40 | 41 | void TimerNode::update(int timeout) 42 | { 43 | struct timeval now; 44 | gettimeofday(&now, NULL); 45 | expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout; 46 | } 47 | 48 | bool TimerNode::isvalid() 49 | { 50 | struct timeval now; 51 | gettimeofday(&now, NULL); 52 | size_t temp = ((now.tv_sec * 1000) + (now.tv_usec / 1000)); 53 | if (temp < expired_time) 54 | { 55 | return true; 56 | } 57 | else 58 | { 59 | this->setDeleted(); 60 | return false; 61 | } 62 | } 63 | 64 | void TimerNode::clearReq() 65 | { 66 | request_data.reset(); 67 | this->setDeleted(); 68 | } 69 | 70 | void TimerNode::setDeleted() 71 | { 72 | deleted = true; 73 | } 74 | 75 | bool TimerNode::isDeleted() const 76 | { 77 | return deleted; 78 | } 79 | 80 | size_t TimerNode::getExpTime() const 81 | { 82 | return expired_time; 83 | } 84 | 85 | TimerManager::TimerManager() 86 | { 87 | } 88 | 89 | TimerManager::~TimerManager() 90 | { 91 | } 92 | 93 | void TimerManager::addTimer(SP_ReqData request_data, int timeout) 94 | { 95 | SP_TimerNode new_node(new TimerNode(request_data, timeout)); 96 | { 97 | MutexLockGuard locker(lock); 98 | TimerNodeQueue.push(new_node); 99 | } 100 | request_data->linkTimer(new_node); 101 | } 102 | 103 | void TimerManager::addTimer(SP_TimerNode timer_node) 104 | { 105 | 106 | } 107 | 108 | /* 处理逻辑是这样的~ 109 | 因为(1) 优先队列不支持随机访问 110 | (2) 即使支持,随机删除某节点后破坏了堆的结构,需要重新更新堆结构。 111 | 所以对于被置为deleted的时间节点,会延迟到它(1)超时 或 (2)它前面的节点都被删除时,它才会被删除。 112 | 一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。 113 | 这样做有两个好处: 114 | (1) 第一个好处是不需要遍历优先队列,省时。 115 | (2) 第二个好处是给超时时间一个容忍的时间,就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除),如果监听的请求在超时后的下一次请求中又一次出现了, 116 | 就不用再重新申请RequestData节点了,这样可以继续重复利用前面的RequestData,减少了一次delete和一次new的时间。 117 | */ 118 | 119 | void TimerManager::handle_expired_event() 120 | { 121 | MutexLockGuard locker(lock); 122 | while (!TimerNodeQueue.empty()) 123 | { 124 | SP_TimerNode ptimer_now = TimerNodeQueue.top(); 125 | if (ptimer_now->isDeleted()) 126 | { 127 | TimerNodeQueue.pop(); 128 | //delete ptimer_now; 129 | } 130 | else if (ptimer_now->isvalid() == false) 131 | { 132 | TimerNodeQueue.pop(); 133 | //delete ptimer_now; 134 | } 135 | else 136 | { 137 | break; 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /old_version/old_version_0.6/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "requestData.h" 3 | #include "noncopyable.h" 4 | #include "MutexLock.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class RequestData; 11 | 12 | class TimerNode 13 | { 14 | typedef std::shared_ptr SP_ReqData; 15 | private: 16 | bool deleted; 17 | size_t expired_time; 18 | SP_ReqData request_data; 19 | public: 20 | TimerNode(SP_ReqData _request_data, int timeout); 21 | ~TimerNode(); 22 | void update(int timeout); 23 | bool isvalid(); 24 | void clearReq(); 25 | void setDeleted(); 26 | bool isDeleted() const; 27 | size_t getExpTime() const; 28 | }; 29 | 30 | struct timerCmp 31 | { 32 | bool operator()(std::shared_ptr &a, std::shared_ptr &b) const 33 | { 34 | return a->getExpTime() > b->getExpTime(); 35 | } 36 | }; 37 | 38 | class TimerManager 39 | { 40 | typedef std::shared_ptr SP_ReqData; 41 | typedef std::shared_ptr SP_TimerNode; 42 | private: 43 | std::priority_queue, timerCmp> TimerNodeQueue; 44 | MutexLock lock; 45 | public: 46 | TimerManager(); 47 | ~TimerManager(); 48 | void addTimer(SP_ReqData request_data, int timeout); 49 | void addTimer(SP_TimerNode timer_node); 50 | void handle_expired_event(); 51 | }; -------------------------------------------------------------------------------- /old_version/old_version_0.6/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | ssize_t readn(int fd, void *buff, size_t n); 6 | ssize_t readn(int fd, std::string &inBuffer); 7 | ssize_t writen(int fd, void *buff, size_t n); 8 | ssize_t writen(int fd, std::string &sbuff); 9 | void handle_for_sigpipe(); 10 | int setSocketNonBlocking(int fd); -------------------------------------------------------------------------------- /并发模型.md: -------------------------------------------------------------------------------- 1 | # 并发模型 2 | 3 | 程序使用Reactor模型,并使用多线程提高并发度。为避免线程频繁创建和销毁带来的开销,使用线程池,在程序的开始创建固定数量的线程。使用epoll作为IO多路复用的实现方式。 4 | 5 | ## 线程 6 | 一般而言,多线程服务器中的线程可分为以下几类: 7 | 8 | * IO线程(负责网络IO) 9 | * 计算线程(负责复杂计算) 10 | * 第三方库所用线程 11 | 12 | 本程序中的Log线程属于第三种,其它线程属于IO线程,因为Web静态服务器计算量较小,所以没有分配计算线程,减少跨线程分配的开销,让IO线程兼顾计算任务。除Log线程外,每个线程一个事件循环,遵循One loop per thread。 13 | 14 | ## 并发模型 15 | 本程序使用的并发模型如下图所示: 16 | 17 | ![并发模型](https://github.com/linyacool/WebServer/blob/master/datum/model.png) 18 | 19 | MainReactor只有一个,负责响应client的连接请求,并建立连接,它使用一个NIO Selector。在建立连接后用Round Robin的方式分配给某个SubReactor,因为涉及到跨线程任务分配,需要加锁,这里的锁由某个特定线程中的loop创建,只会被该线程和主线程竞争。 20 | 21 | SubReactor可以有一个或者多个,每个subReactor都会在一个独立线程中运行,并且维护一个独立的NIO Selector。 22 | 23 | 当主线程把新连接分配给了某个SubReactor,该线程此时可能正阻塞在多路选择器(epoll)的等待中,怎么得知新连接的到来呢?这里使用了eventfd进行异步唤醒,线程会从epoll_wait中醒来,得到活跃事件,进行处理。 24 | 25 | 我学习了muduo库中的runInLoop和queueInLoop的设计方法,这两个方法主要用来执行用户的某个回调函数,queueInLoop是跨进程调用的精髓所在,具有极大的灵活性,我们只需要绑定好回调函数就可以了,我仿照muduo实现了这一点。 26 | 27 | ## epoll工作模式 28 | epoll的触发模式在这里我选择了ET模式,muduo使用的是LT,这两者IO处理上有很大的不同。ET模式要比LE复杂许多,它对用户提出了更高的要求,即每次读,必须读到不能再读(出现EAGAIN),每次写,写到不能再写(出现EAGAIN)。而LT则简单的多,可以选择也这样做,也可以为编程方便,比如每次只read一次(muduo就是这样做的,这样可以减少系统调用次数)。 29 | 30 | ## 定时器 31 | 32 | 每个SubReactor持有一个定时器,用于处理超时请求和长时间不活跃的连接。muduo中介绍了时间轮的实现和用stl里set的实现,这里我的实现直接使用了stl里的priority_queue,底层是小根堆,并采用惰性删除的方式,时间的到来不会唤醒线程,而是每次循环的最后进行检查,如果超时了再删,因为这里对超时的要求并不会很高,如果此时线程忙,那么检查时间队列的间隔也会短,如果不忙,也给了超时请求更长的等待时间。 33 | 34 | ## 核心结构 35 | 36 | 程序中的每一个类和结构体当然都必不可少,其中能体现并发模型和整体架构的,我认为是有两个: 37 | 38 | * Channel类:Channel是Reactor结构中的“事件”,它自始至终都属于一个EventLoop,负责一个文件描述符的IO事件,在Channel类中保存这IO事件的类型以及对应的回调函数,当IO事件发生时,最终会调用到Channel类中的回调函数。因此,程序中所有带有读写时间的对象都会和一个Channel关联,包括loop中的eventfd,listenfd,HttpData等。 39 | * EventLoop:One loop per thread意味着每个线程只能有一个EventLoop对象,EventLoop即是时间循环,每次从poller里拿活跃事件,并给到Channel里分发处理。EventLoop中的loop函数会在最底层(Thread)中被真正调用,开始无限的循环,直到某一轮的检查到退出状态后从底层一层一层的退出。 40 | 41 | ## Log 42 | Log的实现了学习了muduo,Log的实现分为前端和后端,前端往后端写,后端往磁盘写。为什么要这样区分前端和后端呢?因为只要涉及到IO,无论是网络IO还是磁盘IO,肯定是慢的,慢就会影响其它操作,必须让它快才行。 43 | 44 | 这里的Log前端是前面所述的IO线程,负责产生log,后端是Log线程,设计了多个缓冲区,负责收集前端产生的log,集中往磁盘写。这样,Log写到后端是没有障碍的,把慢的动作交给后端去做好了。 45 | 46 | 后端主要是由多个缓冲区构成的,集满了或者时间到了就向文件写一次。采用了muduo介绍了“双缓冲区”的思想,实际采用4个多的缓冲区(为什么说多呢?为什么4个可能不够用啊,要有备无患)。4个缓冲区分两组,每组的两个一个主要的,另一个防止第一个写满了没地方写,写满或者时间到了就和另外两个交换**指针**,然后把满的往文件里写。 47 | 48 | 与Log相关的类包括FileUtil、LogFile、AsyncLogging、LogStream、Logging。 49 | 其中前4个类每一个类都含有一个append函数,Log的设计也是主要围绕这个**append**函数展开的。 50 | 51 | * FileUtil是最底层的文件类,封装了Log文件的打开、写入并在类析构的时候关闭文件,底层使用了标准IO,该append函数直接向文件写。 52 | * LogFile进一步封装了FileUtil,并设置了一个循环次数,每过这么多次就flush一次。 53 | * AsyncLogging是核心,它负责启动一个log线程,专门用来将log写入LogFile,应用了“双缓冲技术”,其实有4个以上的缓冲区,但思想是一样的。AsyncLogging负责(定时到或被填满时)将缓冲区中的数据写入LogFile中。 54 | * LogStream主要用来格式化输出,重载了<<运算符,同时也有自己的一块缓冲区,这里缓冲区的存在是为了缓存一行,把多个<<的结果连成一块。 55 | * Logging是对外接口,Logging类内涵一个LogStream对象,主要是为了每次打log的时候在log之前和之后加上固定的格式化的信息,比如打log的行、文件名等信息。 -------------------------------------------------------------------------------- /测试及改进.md: -------------------------------------------------------------------------------- 1 | # 测试及改进 2 | 3 | ## 测试环境 4 | * OS:Ubuntu 14.04 5 | * 内存:8G 6 | * CPU:I7-4720HQ 7 | 8 | ## 测试方法 9 | * 理想的测试环境是两台计算机,带宽无限,现在的网卡虽然都是千兆网卡,但是普通家用的网线都是5类双绞线,最高100Mbps,在linux下用ethtool可以看到网卡的速度被限制为100Mbsp,无法更改为更高的,经测试很容易跑满带宽,因此退而选择本地环境。 10 | * 使用工具Webbench,开启1000客户端进程,时间为60s 11 | * 分别测试短连接和长连接的情况 12 | * 关闭所有的输出及Log 13 | * 为避免磁盘IO对测试结果的影响,测试响应为内存中的"Hello World"字符加上必要的HTTP头 14 | * 我的最终版本中很多方面借鉴了muduo的思路,muduo中也提供了一个简单的HTTP echo测试,因此我将与muduo进行一个小小的对比,我修改了muduo测试的代码,使其echo相同的内容,关闭muduo的所有输出及Log 15 | * 线程池开启4线程 16 | * 因为发送的内容很少,为避免发送可能的延迟,关闭Nagle算法 17 | 18 | 19 | ## 测试结果及分析 20 | 测试截图放在最后 21 | 22 | | 服务器 | 短连接QPS | 长连接QPS | 23 | | - | :-: | -: | 24 | | WebServer | 126798| 335338 | 25 | | Muduo | 88430 | 358302 | 26 | 27 | * 首先很明显的一点是长链接能处理的请求数是短连接的三四倍,因为没有了连接建立和断开的开销,不需要频繁accept和shutdown\close等系统调用,也不需要频繁建立和销毁对应的结构体。 28 | * 我的服务器在最后的版本中,没有改进输入输出Buffer,用了效率低下的string,muduo用的是设计良好的vector,我将在后续改进这一点。这也造成了在长连接的情况下,我的server逊于muduo。虽说边沿触发效率高一点,但是还是比不过在Buffer上性能的优化的。 29 | * 短链接的情况下,我的服务器要超过Muduo很多。原因在于:Muduo采用水平触发方式(Linux下用epoll),并且做法是每次Acceptor只accept一次就返回,面对突然的并发量,必然会因为频繁的epoll_wait耽误大量的时间,而我的做法是用while包裹accept,一直accept到不能再accept。当然,如果同时连接的请求很少,陈硕在书中也提到过,假如一次只有一个连接,那么我的方式就会多一次accpet才能跳出循环,但是这样的代价似乎微不足道啊,换来的效率却高了不少。 30 | * 空闲时,Server几乎不占CPU,短连接时,各线程的CPU负载比较均衡,长连接时,主线程负载0,线程池的线程负载接近100%,因为没有新的连接需要处理。各种情况均正常。 31 | * 没有严格的考证,测试时发现,HTTP的header解析的结果用map比用unordered_map快,网上的博客里有很多人做了测试,我在做实验的时候大致也发现了。主要是因为数据量太小,一个HTTP请求头才几个头部字段,建立unordered_map的成本要比map高,数据量小,复杂度根本体现不出来。 32 | 33 | 34 | 35 | ## 测试结果截图 36 | * WebServer短连接测试 37 | ![shortWeb](https://github.com/linyacool/WebServer/blob/master/datum/WebServer.png) 38 | * muduo短连接测试 39 | ![shortMuduo](https://github.com/linyacool/WebServer/blob/master/datum/muduo.png) 40 | * WebServer长连接测试 41 | ![keepWeb](https://github.com/linyacool/WebServer/blob/master/datum/WebServerk.png) 42 | * muduo长连接测试 43 | ![keepMuduo](https://github.com/linyacool/WebServer/blob/master/datum/muduok.png) 44 | * WebServer空闲负载 45 | ![idle](https://github.com/linyacool/WebServer/blob/master/datum/idle.png) 46 | * WebServer短连接CPU负载 47 | ![short](https://github.com/linyacool/WebServer/blob/master/datum/close.png) 48 | * WebServer长连接CPU负载 49 | ![keep](https://github.com/linyacool/WebServer/blob/master/datum/keepalive.png) 50 | 51 | -------------------------------------------------------------------------------- /版本历史.md: -------------------------------------------------------------------------------- 1 | # 版本历史 2 | 从0.1最初形成到一点一点改进到0.6,到最终看了muduo,痛下决心重写,最终的版本完全从头再来了,但有了前面的经验,写起来顺畅了不少,但花了比之前所有加起来还要长的时间 3 | ## 0.1 4 | 5 | 第一版是看了很多Github上别人写的服务器,以及博客上的一些总结,结合自己的理解写出来的。模型结构如下: 6 | 7 | * 使用了epoll边沿触发+EPOLLONESHOT+非阻塞IO 8 | * 使用了一个固定线程数的线程池 9 | * 实现了一个任务队列,由条件变量触发通知新任务的到来 10 | * 实现了一个小根堆的定时器及时剔除超时请求,使用了STL的优先队列来管理定时器 11 | * 解析了HTTP的get、post请求,支持长短连接 12 | * mime设计为单例模式 13 | * 线程的工作分配为: 14 | * 主线程负责等待epoll中的事件,并把到来的事件放进任务队列,在每次循环的结束剔除超时请求和被置为删除的时间结点 15 | * 工作线程阻塞在条件变量的等待中,新任务到来后,某一工作线程会被唤醒,执行具体的IO操作和计算任务,如果需要继续监听,会添加到epoll中 16 | 17 | * 锁的使用有两处: 18 | * 第一处是任务队列的添加和取操作,都需要加锁,并配合条件变量,跨越了多个线程。 19 | * 第二处是定时器结点的添加和删除,需要加锁,主线程和工作线程都要操作定时器队列。 20 | 21 | 22 | 23 | 第一版的服务器已经相对较完整了,该有的功能都已经具备了 24 | 25 | ## 0.2 26 | 27 | 在第一版的基础上,优化了代码结构,自己设计了RAII锁机制,使锁能够自动释放,并修复了一些小的bug 28 | 29 | ## 0.3 30 | 31 | * 几乎全部的裸指针被智能指针替代 32 | * 利用weak_ptr解决时间结点和HTTP类互指的问题 33 | * 任务队列的任务结构从函数指针+参数指针转换为C++11的function 34 | 35 | 这一版还是花了不少时间的,毕竟对象的生命周期不由自己控制了 36 | 37 | ## 0.4 38 | 39 | 这个时候买了陈硕的《Linux多线程服务端编程》书,看了一部分,从前面几章获得启发 40 | * 为不提供拷贝构造和赋值运算符的类添加了noncopyable基类 41 | * 重写了RAII机制的锁,学习muduo中的做法 42 | * 重写了单例模式,将双重加锁改为更简单而安全的pthread_once() 43 | 44 | ## 0.5 45 | 46 | * 修复了一些bug,稍微调整了类的结构 47 | * 封装了条件变量 48 | 49 | ## 0.6 50 | 51 | * 仿照muduo,写了4个缓冲区的异步Log日志,没有区分优先级,其它基本都具备了 52 | 53 | ## WebServer(重构) 54 | 55 | 不知道该给自己的服务器取什么名字好,随便叫个吧……最后一版被我改的面目全非,也是下了很大的决心。之前的版本无非修修补补,算是自我检讨的过程,但是闭门造车并不可取,于是我把陈硕的《Linux多线程服务端编程》看完了,书上虽然贴了部分源码,但我看的还是朦朦胧胧,很多地方不明白,花了几天时间把源码看了,不懂的地方再回过来看书,总算是弄明白了。看了大牛的代码,再看自己的……哎我重写总行了吧 56 | 57 | 顺便吐槽一下自己,之前在知乎上看到陈硕一直推销自己的书,我还觉得这人好功利,后来被师兄推荐,看了一下目录,觉得可以参考一下就买了。没想到书就一点一点这么看完了……还看了好几遍,源码也是看了好几遍…… 58 | 59 | 当然,这不是最终篇,可改进的地方还有很多,绝对不敢说看了几本书就敢说自己写的东西比大牛写的好,我还会继续改进自己的server的 60 | 61 | 最后版本的东西没有在这里介绍,写在了模型结构里,这里只想写一下自己的心路历程,记录一下小白成长之路~ -------------------------------------------------------------------------------- /连接的维护.md: -------------------------------------------------------------------------------- 1 | # 连接维护(针对非阻塞IO) 2 | 3 | ### 建立连接 4 | 5 | * 建立连接的过程 6 | 连接的建立比较简单,server端通过socket(),bind(),listen(),并使用epoll ET模式监听listenfd的读请求,当TCP连接完成3次握手后,会触发listenfd的读事件,应用程序调用accept(),会检查已完成的连接队列,如果队列里有连接,就返回这个连接,出错或连接为空时返回-1。此时,已经可以进行正常的读写操作了。 当然,因为是ET模式,accept()要一直循环到就绪连接为空。 7 | * 分析 8 | 之所以说建立连接的过程比较简单,是因为数据的通信已经由操作系统帮我们完成了,这里的通信是指3次握手的过程,这个过程不需要应用程序参与,当应用程序感知到连接时,此时该连接已经完成了3次握手的过程,accept就好了。另一个原因是一般情况下,连接的建立都是client发起的,server端被动建立连接就好了,也不会出现同时建立的情况。 9 | * 限制 10 | 假设server只监听一个端口,一个连接就是一个四元组(原ip,原port,对端ip, 对端port),那么理论上可以建立2^48个连接,可是,fd可没有这么多(操作系统限制、用户进程限制)。当连接满了,如果空等而不连接,那么就绪队列也满了后,会导致新连接无法建立。这里的做法我参考了muduo,准备一个空的文件描述符,accept()后直接close(),这样对端不会收到RST,至少可以知道服务器正在运行。 11 | 12 | ### 关闭连接 13 | 14 | 相对于连接的建立,关闭连接则复杂的多,远不是一个close()那么简单,关闭连接要优雅。 15 | 16 | ##### 什么时候关闭连接? 17 | 通常server和client都可以主动发Fin来关闭连接 18 | 19 | * 对于client(非Keep-Alive),发送完请求后就可以shutdown()写端,然后收到server发来的应答,最后close掉连接。也可以不shutdown()写,等读完直接close。对于Keep-Alive的情况,就要看client的心情了,收到消息后可以断,也可以不断,server应该保证不主动断开。 20 | 21 | * 对于server端,毫无疑问应该谨慎处理以上所有情况。具体说来: 22 | > * 出现各种关于连接的错误时,可以直接close()掉 23 | > * 短连接超时的请求,可以close(),也可以不关 24 | > * 长连接对方长时间没有请求(如果没有保活机制),可以close(),也可以不关 25 | > * client发出Fin,server会收到0字节,通常不能判断client是close了还是shutdown,这时server应当把消息发完,然后才可以close(),如果对方调用的是close,会收到RST,server能感知到,就可以立即close了 26 | > * 短连接正常结束,server可以close,也可以不close,大多数的实现是不close的(对HTTP1.1而言) 27 | 28 | 29 | ##### EPOLLIN触发但是read()返回0的情况 30 | 31 | 这种情况通常有两个原因: 32 | > * 对端已经关闭了连接,这时再写该fd会出错,此时应该关闭连接 33 | > * 对端只是shutdown()了写端,告诉server我已经写完了,但是还可以接收信息。server应该在写完所有的信息后再关闭连接。更优雅的做法是透明的传递这个行为,即server顺着关闭读端,然后发完数据后关闭。 34 | -------------------------------------------------------------------------------- /遇到的困难.md: -------------------------------------------------------------------------------- 1 | # 遇到的困难 2 | 3 | ## 1. 如何设计各个线程个任务 4 | 其实我觉的实现上的困难都不算真正的困难吧,毕竟都能写出来,无非是解决bug花的时间的长短。 5 | 我遇到的最大的问题是不太理解One loop per thread这句话吧,翻译出来不就是每个线程一个循环,我最开始写的也是一个线程一个循环啊,muduo的实现和我的有什么区别呢?还有怎么设计才能减少竞态? 6 | 7 | 带着这些问题我看了《Linux多线程服务端编程》,并看完了muduo的源码,这些问题自然而然就解决了 8 | 9 | 10 | ## 2. 异步Log几秒钟才写一次磁盘,要是coredump了,这段时间内产生的log我去哪找啊? 11 | 12 | 其实这个问题非常简单了,也没花多少时间去解决,但我觉的非常好玩。coredump了自然会保存在core文件里了,无非就是把它找出来的问题了,在这里记录一下。 13 | 14 | 当然这里不管coredump的原因是什么,我只想看丢失的log。所以模拟的话在某个地方abort()就行 15 | 16 | 多线程调试嘛,先看线程信息,info thread,找到我的异步打印线程,切换进去看bt调用栈,正常是阻塞在条件变量是wait条件中的,frame切换到threadFunc(这个函数是我的异步log里面的循环的函数名),剩下的就是print啦~不过,我的Buffer是用智能指针shared_ptr包裹的,直接->不行,gdb不识别,优化完.get()不让用,可能被inline掉了,只能直接从shared_ptr源码中找到_M_ptr成员来打印。 17 | 18 | ![gdb](https://github.com/linyacool/WebServer/blob/master/datum/gdb.png) -------------------------------------------------------------------------------- /项目目的.md: -------------------------------------------------------------------------------- 1 | # 项目目的 2 | --- 3 | ### 最初的想法 4 | 本项目是我在三星电子(中国)研发中心实习期间利用晚上和周末的时间完成的,实习期间我负责4K分辨率双鱼眼摄像头视频拼接的算法设计与实现,我希望能把其中的图像拼接部分的成果通过Web的方式展示出来,但因为涉及保密协议,不得不放弃这一想法。 5 | 6 | ### Web服务器能够很好的贯穿所学的知识 7 | 但是,Web服务器能够很好的贯穿之前所学的知识,之前看过的《C++ Primer》、《Effevtive C++》、《STL源码剖析》、《深度探索C++对象模型》、《TCP\IP详解卷1》、APUE、UNP,还包括了《后台开发核心技术与应用实践》等书,涵盖了 8 | 9 | * TCP、HTTP协议 10 | * 多进程多线程 11 | * IO 12 | * 锁 13 | * 通信 14 | * C++语法 15 | * 编程规范 16 | * Linux环境下各种工具的使用 17 | * 版本控制Git 18 | * Makefile和CMakeLists文件的编写 19 | * 自动化构建工具Travis CI的使用 20 | 21 | 最终的版本在很多方面学习了muduo网络库,在看完陈硕的《Linux多线程服务端编程》后,对照着书把muduo的源码读了几遍,并重构了自己的服务器,最终的很多想法借鉴了muduo的思想。 22 | --------------------------------------------------------------------------------