├── .gitignore ├── 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 ├── doc └── problems.md ├── multiProcess1 ├── README.md ├── base │ ├── Atomic.h │ ├── Signal.h │ ├── any.h │ └── noncopyable.h ├── doc │ ├── model.png │ ├── pressure_test.md │ ├── pressure_test_keep_alive.png │ └── pressure_test_short.png ├── http │ ├── HttpContext.cpp │ ├── HttpContext.h │ ├── HttpRequest.cpp │ ├── HttpRequest.h │ ├── HttpResponse.cpp │ ├── HttpResponse.h │ ├── HttpServer.cpp │ └── HttpServer.h ├── main.cpp ├── net │ ├── Acceptor.cpp │ ├── Acceptor.h │ ├── Buffer.cpp │ ├── Buffer.h │ ├── Channel.cpp │ ├── Channel.h │ ├── Epoll.cpp │ ├── Epoll.h │ ├── EventLoop.cpp │ ├── EventLoop.h │ ├── InternetAddress.cpp │ ├── InternetAddress.h │ ├── Process.cpp │ ├── Process.h │ ├── ProcessPool.cpp │ ├── ProcessPool.h │ ├── Socket.cpp │ ├── Socket.h │ ├── SocketPair.cpp │ ├── SocketPair.h │ ├── TcpConnection.cpp │ ├── TcpConnection.h │ ├── TcpServer.cpp │ ├── TcpServer.h │ ├── Timer.cpp │ ├── Timer.h │ ├── TimerId.h │ ├── TimerQueue.cpp │ ├── TimerQueue.h │ ├── status.cpp │ ├── status.h │ └── type.h └── web │ ├── favicon.ico │ └── index.html ├── multiProcess2 ├── README.md ├── base │ ├── Atomic.h │ ├── ProcessCondition.h │ ├── ProcessMutexLock.h │ ├── Signal.h │ ├── any.h │ ├── noncopyable.h │ └── utility.h ├── doc │ ├── pressure_test.md │ ├── pressure_test_keep_alive.png │ └── pressure_test_short.png ├── http │ ├── HttpContext.cpp │ ├── HttpContext.h │ ├── HttpRequest.cpp │ ├── HttpRequest.h │ ├── HttpResponse.cpp │ ├── HttpResponse.h │ ├── HttpServer.cpp │ └── HttpServer.h ├── main.cpp ├── net │ ├── Acceptor.cpp │ ├── Acceptor.h │ ├── Buffer.cpp │ ├── Buffer.h │ ├── Channel.cpp │ ├── Channel.h │ ├── Epoll.cpp │ ├── Epoll.h │ ├── EventLoop.cpp │ ├── EventLoop.h │ ├── InternetAddress.cpp │ ├── InternetAddress.h │ ├── ProcessPool.cpp │ ├── ProcessPool.h │ ├── Socket.cpp │ ├── Socket.h │ ├── TcpConnection.cpp │ ├── TcpConnection.h │ ├── TcpServer.cpp │ ├── TcpServer.h │ ├── Timer.cpp │ ├── Timer.h │ ├── TimerId.h │ ├── TimerQueue.cpp │ ├── TimerQueue.h │ ├── status.cpp │ ├── status.h │ └── type.h └── web │ ├── favicon.ico │ └── index.html └── multiThread ├── README.md ├── base ├── AsyncLogger.cpp ├── AsyncLogger.h ├── AsyncLogging.cpp ├── AsyncLogging.h ├── Atomic.h ├── BlockingQueue.h ├── BoundedBlockingQueue.h ├── Condition.h ├── CountDownLatch.cpp ├── CountDownLatch.h ├── Exception.cpp ├── Exception.h ├── FileUtil.cpp ├── FileUtil.h ├── LogFile.cpp ├── LogFile.h ├── LogStream.cpp ├── LogStream.h ├── Logger.cpp ├── Logger.h ├── MutexLock.h ├── ObjectPool.h ├── Singleton.h ├── SpinLock.h ├── Thread.cpp ├── Thread.h ├── ThreadLocal.h ├── ThreadPool.cpp ├── ThreadPool.h ├── ThreadPool_cpp11.cpp ├── ThreadPool_cpp11.h ├── any.h └── noncopyable.h ├── doc ├── model.png ├── pressure_test.md ├── pressure_test_keep_alive.png └── pressure_test_short.png ├── http ├── HttpContext.cpp ├── HttpContext.h ├── HttpRequest.cpp ├── HttpRequest.h ├── HttpResponse.cpp ├── HttpResponse.h ├── HttpServer.cpp └── HttpServer.h ├── main.cpp ├── net ├── Acceptor.cpp ├── Acceptor.h ├── Buffer.cpp ├── Buffer.h ├── CallBack.h ├── Channel.cpp ├── Channel.h ├── Connector.cpp ├── Connector.h ├── Epoll.cpp ├── Epoll.h ├── EventLoop.cpp ├── EventLoop.h ├── EventLoopThread.cpp ├── EventLoopThread.h ├── EventLoopThreadPool.cpp ├── EventLoopThreadPool.h ├── InternetAddress.cpp ├── InternetAddress.h ├── Socket.cpp ├── Socket.h ├── TcpClient.cpp ├── TcpClient.h ├── TcpConnection.cpp ├── TcpConnection.h ├── TcpServer.cpp ├── TcpServer.h ├── Timer.cpp ├── Timer.h ├── TimerId.h ├── TimerQueue.cpp └── TimerQueue.h └── web ├── favicon.ico └── index.html /.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 | # Clion 35 | cmake-build-*/ 36 | .idea/ 37 | 38 | # MAC 39 | .DS_Store 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tinyWS 2 | A C++ Tiny Web Server 3 | 4 | ## 版本 5 | 6 | - [多线程版本](./multiThread) 7 | - 多进程版本 8 | - [版本1](./multiProcess1):父进程负责 listen 和 accept 连接,子进程负责连接的读写处理。 9 | - [版本2](./multiProcess2):所有进程监听同一个 listen sockfd,accept 到新的连接后自己处理连接的读写。所有进程通过竞争设置了`PTHREAD_PROCESS_SHARED`属性的`mutex`来获取处理 listen sockfd 的机会,保证了同一时刻只有一个进程监听 listen sockfd 的 IO 事件(只有读事件),解决了***惊群问题***。 10 | 11 | ## 测试 12 | 13 | | 版本 | 短连接QPS | 长连接QPS | 14 | | ----------------------------------------------- | :-------- | :-------- | 15 | | [多线程](./multiThread/doc/pressure_test.md) | 8377 | 31325 | 16 | | [多进程1](./multiProcess1/doc/pressure_test.md) | 7650 | 30828 | 17 | | [多进程2](./multiProcess2/doc/pressure_test.md) | 7638 | 30608 | 18 | 19 | 可以看出,处理请求数方面,长连接比短连接能很多,大概多了 4 倍。 20 | 21 | -------------------------------------------------------------------------------- /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/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/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 createProccesses 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 createProccesses 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 | return -1; 62 | 63 | return sock; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /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/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/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 | -------------------------------------------------------------------------------- /doc/problems.md: -------------------------------------------------------------------------------- 1 | # 开发过程中遇到的问题 2 | 本文档记录了开发过程中遇到的问题以及 3 | 4 | ## 在头文件中定义了函数,导致链接错误:重复定义(multiple defination) 5 | 6 | 在头文件中定义了函数,而该头文件被多个文件引用,这样会导致链接错误:重复定义(multiple defination)。因为编译的时候,C++ 是采用独立编译,就是每个 cpp 单独编译成对应的目标文件,最后链接器再将多个目标链接成可执行程序。如果多个目标文件中定义了同一个函数,则在链接的时候,就会出现重复定义(multiple defination)的错误。 7 | 8 | 所以只在头文件中声明变量和函数,而不去定义它们。否则就会导致重复定义的错误。但是有几种情况是例外的: 9 | 10 | - 内联函数的定义 11 | - 类(class)的定义 12 | - const 和 static 变量 13 | 14 | 参考[注意头文件规则,避免链接错误:重复定义(multiple defination)](https://zybuluo.com/uuprince/note/81709)。 15 | 16 | -------------------------------------------------------------------------------- /multiProcess1/README.md: -------------------------------------------------------------------------------- 1 | # tinyWS 2 | A C++ Tiny Web Server 3 | 4 | ## 已完成 5 | 6 | - 完成基本的Tcp(被动连接)库; 7 | - 简易的HTTP服务器,可访问主页HTML和网站favicon图标。 8 | 9 | ## 技术 10 | 11 | - 主从 Reactor 模式: 12 | - 主 Reactor 负责监听连接,当有新的连接,accept 到新的 socket 后,使用 Round Robin 方法选择从 Reactor,将 socket 派发给从 Reactor; 13 | - 从 Reactor 负责管理时间描述符(timerfd 用于定时任务)、事件描述符(eventfd 用于唤醒 IO 线程)和 派发过来的 socket 文件描述符。 14 | - multiple Reactors + thread pool (one loop per thread + process pool); 15 | - EventLoop:使用 Epoll 水平触发的模式结合非阻塞 IO; 16 | - 进程池池: 17 | - 使用多进程能发挥多核的优势; 18 | - 进程池可以避免线程的频繁地创建和销毁的开销。 19 | - 使用智能指针等 RAII 机制,来为降低内存泄漏的可能性; 20 | 21 | ## 并发模型 22 | 23 | 并发模型为 multiple reactors + process pool (one loop per process + process pool); + 非阻塞 IO,新连接使用 Round Robin 策略派发。 24 | 25 | ![并发模型](doc/model.png) 26 | 27 | 28 | 29 | ## 参考 30 | 31 | - [陈硕老师的Blog](http://www.cppblog.com/solstice/) 32 | - [linyacool](https://github.com/linyacool)的[WebServer](https://github.com/linyacool/WebServer) 33 | - [uv-cpp](https://github.com/wlgq2/uv-cpp) 34 | - [开源HTTP解析器](https://www.cnblogs.com/arnoldlu/p/6497837.html) 35 | - [HTTP请求和响应格式](https://www.cnblogs.com/yaozhongxiao/archive/2013/03/02/2940252.html) 36 | - [写一个并发http服务器](https://zhuanlan.zhihu.com/p/23336565)(有代码) 37 | - [有什么适合提高 C/C++ 网络编程能力的开源项目推荐? - Variation的回答 - 知乎](https://www.zhihu.com/question/20124494/answer/733016078)(master/worker、proactor 等 IO 模型) 38 | - [Reactor事件驱动的两种设计实现:面向对象 VS 函数式编程](http://www.cnblogs.com/me115/p/5088914.html) 39 | - [网络编程中的关键问题总结](https://www.cnblogs.com/me115/p/5092091.html) 40 | - [muduo源码剖析 - cyhone的文章 - 知乎](https://zhuanlan.zhihu.com/p/85101271) 41 | - [Muduo 源码分析](https://youjiali1995.github.io/network/muduo/) -------------------------------------------------------------------------------- /multiProcess1/base/Atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_ATOMIC_H 2 | #define TINYWS_ATOMIC_H 3 | 4 | #include 5 | 6 | #include "noncopyable.h" 7 | 8 | namespace tinyWS_process1 { 9 | template 10 | class AtomicIntegerT : noncopyable { 11 | private: 12 | // volatile 关键字,参考: 13 | // https://zh.cppreference.com/w/cpp/language/cv 14 | // https://liam.page/2018/01/18/volatile-in-C-and-Cpp/ 15 | volatile T value_; 16 | 17 | public: 18 | AtomicIntegerT() : value_(0) {} 19 | 20 | T get() { 21 | // 可用 C++ 11 atomic 头文件:std::atomic_load 代替。 22 | // 参考 https://zh.cppreference.com/w/cpp/atomic/atomic_load 23 | return __atomic_load_n(&value_, __ATOMIC_SEQ_CST); 24 | } 25 | 26 | T getAndAdd(T x) { 27 | // 可用 C++ 11 atomic 头文件:std::atomic_fetch_add 代替。 28 | // 参考 https://zh.cppreference.com/w/cpp/atomic/atomic_fetch_add 29 | return __atomic_fetch_add(&value_, x, __ATOMIC_SEQ_CST); 30 | } 31 | 32 | T addAndGet(T x) { 33 | return getAndAdd(x) + x; 34 | } 35 | 36 | T incrementAndGet() { 37 | return addAndGet(1); 38 | } 39 | 40 | T decrementAndGet() { 41 | return addAndGet(-1); 42 | } 43 | 44 | void add(T x) { 45 | getAndAdd(x); 46 | } 47 | 48 | void increment() { 49 | incrementAndGet(); 50 | } 51 | 52 | void decrement() { 53 | decrementAndGet(); 54 | } 55 | 56 | T getAndSet(T newValue) { 57 | // 可用 C++ 11 atomic 头文件:std::atomic_exchange 代替。 58 | // 参考 https://zh.cppreference.com/w/cpp/atomic/atomic_exchange 59 | return __atomic_exchange_n(&value_, newValue, __ATOMIC_SEQ_CST); 60 | } 61 | }; 62 | 63 | using AtomicInt32 = AtomicIntegerT; 64 | using AtomicInt64 = AtomicIntegerT; 65 | } 66 | 67 | #endif //TINYWS_ATOMIC_H 68 | -------------------------------------------------------------------------------- /multiProcess1/base/Signal.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_SIGNAL_H 2 | #define TINYWS_SIGNAL_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace tinyWS_process1 { 11 | 12 | class Signal { 13 | public: 14 | typedef void (*SignalCallback)(int); 15 | 16 | friend class SignalManager; 17 | 18 | private: 19 | int signo_; 20 | std::string name_; 21 | std::string meaning_; 22 | SignalCallback signalCallback_; 23 | 24 | public: 25 | Signal(int signo, 26 | const std::string& name, 27 | const std::string& meaing, 28 | const SignalCallback& cb) 29 | : signo_(signo), 30 | name_(name), 31 | meaning_(meaing), 32 | signalCallback_(cb) { 33 | 34 | // std::cout << "class Signal constructor" << std::endl; 35 | } 36 | 37 | ~Signal() { 38 | // std::cout << "class Signal destructor" << std::endl; 39 | } 40 | 41 | int signo() const { 42 | return signo_; 43 | } 44 | 45 | std::string name() const { 46 | return name_; 47 | } 48 | 49 | std::string meaning() const { 50 | return meaning_; 51 | } 52 | 53 | bool isSame(int signal) const { 54 | return signo_ == signal; 55 | } 56 | }; 57 | 58 | class SignalManager { 59 | private: 60 | unsigned int num_; 61 | 62 | public: 63 | SignalManager() : num_(0) { 64 | 65 | } 66 | 67 | ~SignalManager() { 68 | // std::cout << "class SignalManager destructor" << std::endl; 69 | } 70 | 71 | void addSignal(const Signal& sign) { 72 | updateSignal(sign.signo_, sign.signalCallback_); 73 | ++num_; 74 | } 75 | 76 | void updateSignal(const Signal& sign) { 77 | updateSignal(sign.signo_, sign.signalCallback_); 78 | } 79 | 80 | void deleteSignal(const Signal& sign) { 81 | removeSignal(sign.signo_); 82 | --num_; 83 | } 84 | 85 | private: 86 | void updateSignal(int signo, sighandler_t handler) { 87 | struct sigaction sa{}; 88 | sa.sa_handler = handler; 89 | sa.sa_flags |= SA_RESTART; 90 | sigfillset(&sa.sa_mask); 91 | int result = sigaction(signo, &sa, nullptr); 92 | if (result == -1) { 93 | std::cerr << "update signal error: " << errno << std::endl; 94 | } 95 | } 96 | 97 | void removeSignal(int signo) { 98 | struct sigaction sa{}; 99 | sa.sa_handler = nullptr; 100 | sa.sa_flags |= SA_RESTART; 101 | sigfillset(&sa.sa_mask); 102 | int result = sigaction(signo, &sa, nullptr); 103 | if (result == -1) { 104 | std::cerr << "remove signal error: " << errno << std::endl; 105 | } 106 | } 107 | }; 108 | 109 | } 110 | 111 | #endif //TINYWS_SIGNAL_H 112 | -------------------------------------------------------------------------------- /multiProcess1/base/noncopyable.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_NONCOPYABLE_H 2 | #define TINYWS_NONCOPYABLE_H 3 | 4 | namespace tinyWS_process1 { 5 | // 不可拷贝类 6 | class noncopyable { 7 | protected: 8 | // 默认的构造函数和析构函数是 protected, 9 | // 不允许创建 noncopyable 实例,但允许子类创建实例 10 | // (即允许派生类构造和析构)。 11 | noncopyable() = default; 12 | 13 | ~noncopyable() = default; 14 | 15 | private: 16 | // 使用 delete 关键字禁止编译器自动产生复制构造函数和复制赋值操作符。 17 | noncopyable(const noncopyable&) = delete; 18 | 19 | noncopyable& operator=(const noncopyable&) = delete; 20 | }; 21 | } 22 | 23 | #endif //TINYWS_NONCOPYABLE_H 24 | -------------------------------------------------------------------------------- /multiProcess1/doc/model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiProcess1/doc/model.png -------------------------------------------------------------------------------- /multiProcess1/doc/pressure_test.md: -------------------------------------------------------------------------------- 1 | # 压力测试 2 | 3 | ## 测试环境 4 | 5 | ### 华为云虚拟主机 6 | 7 | - OS: Ubuntu 18.04 LTS 8 | - CPU: 1核 9 | - 内存:2M 10 | 11 | ## 测试方法 12 | 13 | - 为了避免带宽带来的影响,将服务器程序和 WebBench 程序都运行在同一台主机上。 14 | - 使用工具 Webbench,开启 1000 客户端进程,时间为 60s。 15 | - 分别测试短连接和长连接的情况。 16 | - 为避免磁盘 IO 对测试结果的影响,测试响应为内存中的"Hello World"字符加上必要的HTTP头。 17 | - 因为发送的内容很少,为避免发送可能的延迟,禁用 Nagle 算法。 18 | - 创建 4 个子进程。 19 | 20 | ## 测试结果 21 | 22 | | 短连接QPS | 长连接QPS | 23 | | --------- | --------- | 24 | | 7650 | 30828 | 25 | 26 | 处理请求数方面,长连接比短连接能很多,多了 4 倍。 27 | 28 | ## 测试结果截图 29 | 30 | - 短连接 31 | 32 | ![短连接](./pressure_test_short.png) 33 | 34 | - 长连接 35 | 36 | ![长连接](./pressure_test_keep_alive.png) 37 | 38 | ## 参考 39 | 40 | - [linyacool](https://github.com/linyacool)的[WebServer](https://github.com/linyacool/WebServer)中的[测试及改进]([https://github.com/linyacool/WebServer/blob/HEAD/%E6%B5%8B%E8%AF%95%E5%8F%8A%E6%94%B9%E8%BF%9B.md](https://github.com/linyacool/WebServer/blob/HEAD/测试及改进.md))。 41 | - [WebBench](https://github.com/linyacool/WebBench)来自[linyacool](https://github.com/linyacool)的[WebServer](https://github.com/linyacool/WebServer)。 -------------------------------------------------------------------------------- /multiProcess1/doc/pressure_test_keep_alive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiProcess1/doc/pressure_test_keep_alive.png -------------------------------------------------------------------------------- /multiProcess1/doc/pressure_test_short.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiProcess1/doc/pressure_test_short.png -------------------------------------------------------------------------------- /multiProcess1/http/HttpContext.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPCONTEXT_H 2 | #define TINYWS_HTTPCONTEXT_H 3 | 4 | #include "HttpRequest.h" 5 | #include "../net/type.h" 6 | 7 | namespace tinyWS_process1 { 8 | 9 | class Buffer; 10 | 11 | class HttpContext { 12 | public: 13 | // 解析状态 14 | enum HttpRequestParseState { 15 | kExpectRequestLine, // 解析行 16 | kExpectHeader, // 解析请求头 17 | kExpectBody, // 解析 Body 18 | kGotAll // 解析完成 19 | }; 20 | 21 | private: 22 | HttpRequestParseState state_; 23 | HttpRequest request_; 24 | 25 | public: 26 | HttpContext(); 27 | 28 | /** 29 | * 解析 Buffer 的数据,并将相应的信息添加到 HttpRequest 中,最后返回是否解析成功。 30 | * @param buffer 缓冲区 31 | * @param receiveTime 连接接收时间 32 | * @return 是否解析成功 33 | */ 34 | bool parseRequest(Buffer* buffer, TimeType receiveTime); 35 | 36 | /* 37 | * 是否解析完成 38 | */ 39 | bool gotAll() const; 40 | 41 | /** 42 | * 重置解析状态为 kExpectRequestLine,清空 HttpRequest 43 | */ 44 | void reset(); 45 | 46 | /** 47 | * 获取 HttpRequest 48 | * @return HttpRequest 49 | */ 50 | const HttpRequest& request() const; 51 | 52 | // 同上 53 | HttpRequest& request(); 54 | 55 | private: 56 | bool processRequestLine(const char* start, const char* end); 57 | 58 | }; 59 | 60 | } 61 | 62 | #endif //TINYWS_HTTPCONTEXT_H 63 | -------------------------------------------------------------------------------- /multiProcess1/http/HttpRequest.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPREQUEST_H 2 | #define TINYWS_HTTPREQUEST_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../net/type.h" 8 | 9 | namespace tinyWS_process1 { 10 | 11 | class HttpRequest { 12 | public: 13 | // 请求方法 14 | enum Method { 15 | kInvalid, kGet, kPost, kHead, kPut, kDelete 16 | }; 17 | 18 | private: 19 | Method method_; 20 | std::string path_; 21 | std::string query_; 22 | TimeType receiveTime_; 23 | std::map headers_; 24 | 25 | public: 26 | HttpRequest(); 27 | 28 | bool setMethod(const char* start, const char* end); 29 | 30 | Method method() const; 31 | 32 | const char* methodString() const; 33 | 34 | void setPath(const char* start, const char* end); 35 | 36 | const std::string& path() const; 37 | 38 | void setQuery(const char *start, const char *end); 39 | 40 | const std::string& query() const; 41 | 42 | void setReceiveTime(TimeType time); 43 | 44 | TimeType receiveTime() const; 45 | 46 | void addHeader(const char* start, const char* colon, const char* end); 47 | 48 | std::string getHeader(const std::string& field) const; 49 | 50 | const std::map& headers() const; 51 | 52 | void swap(HttpRequest& that); 53 | }; 54 | } 55 | 56 | 57 | #endif //TINYWS_HTTPREQUEST_H 58 | -------------------------------------------------------------------------------- /multiProcess1/http/HttpResponse.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpResponse.h" 2 | 3 | #include 4 | 5 | #include "../net/Buffer.h" 6 | 7 | using namespace tinyWS_process1; 8 | 9 | HttpResponse::HttpResponse(bool close) 10 | : statusCode_(kUnknown), 11 | closeConnection_(close){ 12 | 13 | } 14 | 15 | void HttpResponse::setStatusCode(HttpStatusCode code) { 16 | statusCode_ = code; 17 | } 18 | 19 | void HttpResponse::setStatusMessage(const std::string &message) { 20 | statusMessage_ = message; 21 | } 22 | 23 | void HttpResponse::setCloseConnection(bool on) { 24 | closeConnection_ = on; 25 | } 26 | 27 | bool HttpResponse::closeConnection() const { 28 | return closeConnection_; 29 | } 30 | 31 | void HttpResponse::setContentType(const std::string& contentType) { 32 | addHeader("Content-Type", contentType); 33 | } 34 | 35 | void HttpResponse::addHeader(const std::string& key, const std::string& value) { 36 | headers_[key] = value; 37 | } 38 | 39 | void HttpResponse::setBody(const std::string& body) { 40 | body_ = body; 41 | } 42 | 43 | void HttpResponse::appendToBuffer(Buffer* output) const { 44 | // 响应行:请求方法 路径 HTTP/版本 45 | char buf[32]; 46 | snprintf(buf, sizeof(buf), "HTTP/1.1 %d ", statusCode_); 47 | 48 | output->append(buf); 49 | output->append(statusMessage_); 50 | output->append("\r\n"); 51 | 52 | // 添加响应头 53 | if (closeConnection_) { 54 | // 关闭连接 55 | output->append("Connection: close\r\n"); 56 | } else { 57 | snprintf(buf, sizeof(buf), "Content-Length: %zd\r\n", body_.size()); 58 | output->append(buf); 59 | output->append("Connection: Keep-Alive\r\n"); 60 | } 61 | 62 | for (const auto &header : headers_) { 63 | output->append(header.first); 64 | output->append(": "); 65 | output->append(header.second); 66 | output->append("\r\n"); 67 | } 68 | 69 | output->append("\r\n"); 70 | output->append(body_); 71 | } 72 | -------------------------------------------------------------------------------- /multiProcess1/http/HttpResponse.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPRESPONSE_H 2 | #define TINYWS_HTTPRESPONSE_H 3 | 4 | #include 5 | #include 6 | 7 | namespace tinyWS_process1 { 8 | 9 | class Buffer; 10 | 11 | class HttpResponse { 12 | public: 13 | // 状态码 14 | enum HttpStatusCode { 15 | kUnknown, 16 | k200OK = 200, 17 | k301MovedPermanently = 301, 18 | k400BadRequest = 400, 19 | k404NotFound = 404 20 | }; 21 | 22 | private: 23 | std::map headers_; // 响应头映射 24 | HttpStatusCode statusCode_; // 状态码 25 | std::string statusMessage_; // 状态信息 26 | bool closeConnection_; // 是否将 Connection 字段设置为 close 27 | std::string body_; // Response Body 28 | 29 | public: 30 | /** 31 | * 构造函数 32 | * @param close 连接是否已关闭 33 | */ 34 | explicit HttpResponse(bool close); 35 | 36 | /** 37 | * 设置状态码 38 | * @param code 状态码 39 | */ 40 | void setStatusCode(HttpStatusCode code); 41 | 42 | /** 43 | * 设置状态信息 44 | * @param message 状态信息 45 | */ 46 | void setStatusMessage(const std::string& message); 47 | 48 | /** 49 | * 设置是否将 Connection 字段设置为 close 50 | * @param on true / false 51 | */ 52 | void setCloseConnection(bool on); 53 | 54 | /** 55 | * 获取是否已将 Connection 字段设置为 close 56 | * @return true / false 57 | */ 58 | bool closeConnection() const; 59 | 60 | /** 61 | * 设置 Content-Type 62 | * @param contentType Content-Type 字符串 63 | */ 64 | void setContentType(const std::string& contentType); 65 | 66 | /** 67 | * 添加响应头 68 | * @param key 69 | * @param value 70 | */ 71 | void addHeader(const std::string& key, const std::string& value); 72 | 73 | /** 74 | * 设置 Response Body 75 | * @param body Response Body 字符串 76 | */ 77 | void setBody(const std::string& body); 78 | 79 | /** 80 | * 将响应数据(包括响应头和 Body)添加到 Buffer 中 81 | * @param output 数据指针 82 | */ 83 | void appendToBuffer(Buffer* output) const; 84 | }; 85 | } 86 | 87 | 88 | #endif //TINYWS_HTTPRESPONSE_H 89 | -------------------------------------------------------------------------------- /multiProcess1/http/HttpServer.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPSERVER_H 2 | #define TINYWS_HTTPSERVER_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "../base/noncopyable.h" 9 | #include "../net/TcpServer.h" 10 | #include "../net/TcpConnection.h" 11 | #include "../net/Timer.h" 12 | #include "../net/type.h" 13 | 14 | namespace tinyWS_process1 { 15 | 16 | class Buffer; 17 | class HttpRequest; 18 | class HttpResponse; 19 | class TimerId; 20 | 21 | class HttpServer : noncopyable { 22 | public: 23 | // HTTP 请求到来时的回调函数的类型 24 | using HttpCallback = std::function; 25 | 26 | /** 27 | * 构造函数 28 | * @param loop 所属 EventLoop 29 | * @param listenAddress 监听地址 30 | * @param name TcpServer name 31 | */ 32 | HttpServer(const InternetAddress &listenAddress, const std::string &name); 33 | 34 | /** 35 | * 获取所属 EventLoop 36 | * @return EventLoop 37 | */ 38 | EventLoop* getLoop() const; 39 | 40 | /** 41 | * 设置 HTTP 请求到来时的回调函数 42 | * @param cb 43 | */ 44 | void setHttpCallback(const HttpCallback& cb); 45 | 46 | /** 47 | * 设置 IO 线程数 48 | * @param processNum 线程数 49 | */ 50 | void setProcessNum(int processNum); 51 | 52 | /** 53 | * 启动 TcpServer 54 | */ 55 | void start(); 56 | 57 | TimerId runAt(TimeType runTime, const Timer::TimerCallback& cb); 58 | 59 | TimerId runAfter(TimeType delay, const Timer::TimerCallback& cb); 60 | 61 | TimerId runEvery(TimeType interval, const Timer::TimerCallback& cb); 62 | 63 | private: 64 | TcpServer tcpServer_; // TcpServer 65 | HttpCallback httpCallback_; // HTTP 请求到来时的回调函数 66 | 67 | /** 68 | * 连接建立后,将 HttpContext 传给 TcpConnection。 69 | * @param connection TcpConnectionPtr 70 | */ 71 | void onConnection(const TcpConnectionPtr& connection); 72 | 73 | /** 74 | * 请求到来后,解析请求,响应请求。 75 | * @param connection 76 | * @param buffer 77 | * @param receiveTime 78 | */ 79 | void onMessage(const TcpConnectionPtr& connection, 80 | Buffer* buffer, 81 | TimeType receiveTime); 82 | /** 83 | * 当解析完一条请求信息后,响应请求。 84 | * @param connection TcpConnectionPtr 85 | * @param httpRequest 86 | */ 87 | void onRequest(const TcpConnectionPtr& connection, 88 | const HttpRequest& httpRequest); 89 | }; 90 | 91 | } 92 | 93 | #endif //TINYWS_HTTPSERVER_H 94 | -------------------------------------------------------------------------------- /multiProcess1/net/Acceptor.cpp: -------------------------------------------------------------------------------- 1 | #include "Acceptor.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "EventLoop.h" 11 | #include "InternetAddress.h" 12 | 13 | using namespace tinyWS_process1; 14 | 15 | Acceptor::Acceptor(EventLoop* loop, const InternetAddress& listenAddress) 16 | : loop_(loop), 17 | acceptSocket_(createNonblocking()), 18 | acceptChannel_(loop_, acceptSocket_.fd()), 19 | isListening_(false) { 20 | 21 | acceptSocket_.setReuseAddr(true); 22 | acceptSocket_.bindAddress(listenAddress); 23 | acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this)); 24 | } 25 | 26 | Acceptor::~Acceptor() { 27 | acceptChannel_.disableAll(); 28 | acceptChannel_.remove(); 29 | } 30 | 31 | void Acceptor::setNewConnectionCallback(const Acceptor::NewConnectionCallback& cb) { 32 | newConnectionCallback_ = cb; 33 | } 34 | 35 | void Acceptor::listen() { 36 | isListening_ = true; 37 | acceptSocket_.listen(); 38 | acceptChannel_.enableReading(); 39 | } 40 | 41 | bool Acceptor::isLIstening() const { 42 | return isListening_; 43 | } 44 | 45 | int Acceptor::getSockfd() const { 46 | return acceptSocket_.fd(); 47 | } 48 | 49 | int Acceptor::createNonblocking() { 50 | int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP); 51 | if (sockfd < 0) { 52 | // std::cout << "sockets::createNonblockingOrDie" << std::endl;; 53 | } 54 | } 55 | 56 | void Acceptor::handleRead() { 57 | InternetAddress peerAddress; 58 | while (auto sockfd = acceptSocket_.accept(&peerAddress)) { 59 | // std::cout << "sockfd: " << sockfd << "(" << getpid() << ")" << std::endl; 60 | if (sockfd < 0) { 61 | return; 62 | } 63 | 64 | Socket connectionSocket(sockfd); 65 | if (newConnectionCallback_) { 66 | newConnectionCallback_(std::move(connectionSocket), peerAddress); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /multiProcess1/net/Acceptor.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_ACCEPTOR_H 2 | #define TINYWS_ACCEPTOR_H 3 | 4 | #include 5 | 6 | #include "../base/noncopyable.h" 7 | #include "Channel.h" 8 | #include "Socket.h" 9 | 10 | namespace tinyWS_process1 { 11 | 12 | class InternetAddress; 13 | class EventLoop; 14 | 15 | // 内部类,供 TcpServer 使用,生命周期由 TcpServer 控制。 16 | // 用于 accept(2) 新 TCP 连接,并通过回调函数通知使用者。 17 | class Acceptor : noncopyable { 18 | public: 19 | using NewConnectionCallback = std::function; 20 | 21 | private: 22 | EventLoop* loop_; 23 | Socket acceptSocket_; 24 | Channel acceptChannel_; 25 | NewConnectionCallback newConnectionCallback_; 26 | bool isListening_; 27 | 28 | public: 29 | Acceptor(EventLoop* loop, const InternetAddress& listenAddress); 30 | 31 | ~Acceptor(); 32 | 33 | void setNewConnectionCallback(const NewConnectionCallback& cb); 34 | 35 | void listen(); 36 | 37 | bool isLIstening() const; 38 | 39 | int getSockfd() const; 40 | 41 | static int createNonblocking(); 42 | 43 | private: 44 | void handleRead(); 45 | }; 46 | } 47 | 48 | 49 | #endif //TINYWS_ACCEPTOR_H 50 | -------------------------------------------------------------------------------- /multiProcess1/net/Channel.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_CHANNEL_H 2 | #define TINYWS_CHANNEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../base/noncopyable.h" 9 | #include "type.h" 10 | 11 | namespace tinyWS_process1 { 12 | class EventLoop; 13 | 14 | class Channel : noncopyable { 15 | public: 16 | using EventCallback = std::function; // 事件回调函数 17 | using ReadEventCallback = std::function; // 读事件回调函数 18 | 19 | private: 20 | static const int kNoneEvent; // 无事件 21 | static const int kReadEvent; // 读事件 22 | static const int kWriteEvent; // 写事件 23 | 24 | EventLoop* loop_; 25 | int fd_; 26 | int events_; 27 | int revents_; 28 | 29 | int statusInEpoll_; 30 | 31 | ReadEventCallback readCallback_; // 读事件回调函数 32 | EventCallback writeCallback_; // 写事件回调函数 33 | EventCallback closeCallback_; // 关闭事件回调函数 34 | EventCallback errorCallback_; // 异常事件回调函数 35 | 36 | // 详情见《Linux多线程服务端编程》P274 37 | // 如果用户在 onClose() 中析构 Channel 对象,这会造成要种的后果。 38 | // 即 Channel::handleEvent() 执行到一半,其所属的 Channel 独享本身被销毁了。 39 | // 这时程序立即 core dump 就是最好的结果了。 40 | // 解决方法:提供接口 Channel::tie(),用于延长某些对象(可以是 Channel,也可以是其 owner 对象)的生命期, 41 | // 使之长过 Channel::handleEvent() 函数。 42 | // 这也是 TcpConnection 使用 shared_ptr 管理对象生命期的原因之一。 43 | bool eventHandling_; // 是否在处理事件,用于调试阶段保证 Channel 不会在处理事件过程中析构 44 | bool addedToLoop_; // 是否添加到 EventLoop 中 45 | std::weak_ptr tie_; // 绑定对象 46 | bool tied_; // 是否绑定了对象 47 | 48 | public: 49 | explicit Channel(EventLoop* loop, int fdArg); 50 | 51 | ~Channel(); 52 | 53 | void tie(const std::shared_ptr& obj); 54 | 55 | void handleEvent(TimeType receiveTime); 56 | 57 | void setReadCallback(const ReadEventCallback& cb); 58 | 59 | void setWriteCallack(const EventCallback& cb); 60 | 61 | void setCloseCallback(const EventCallback& cb); 62 | 63 | void setErrorCallback(const EventCallback& cb); 64 | 65 | int fd() const; 66 | 67 | int getEvents() const; 68 | 69 | int setRevents(int revt); 70 | 71 | bool isNoneEvent() const; 72 | 73 | void enableReading(); 74 | 75 | void enableWriting(); 76 | 77 | void disableWriting(); 78 | 79 | void disableAll(); 80 | 81 | bool isWriting() const; 82 | 83 | int getStatusInEpoll(); 84 | 85 | void setStatusInEpoll(int statusInEpoll); 86 | 87 | EventLoop* ownerLoop(); 88 | 89 | void remove(); 90 | 91 | // for debug 92 | std::string reventsToString() const; 93 | 94 | std::string eventsToString() const; 95 | 96 | private: 97 | void handleEventWithGuard(TimeType receiveTime); 98 | 99 | void update(); 100 | 101 | std::string eventsToString(int fd, int event) const; 102 | }; 103 | } 104 | 105 | 106 | #endif //TINYWS_CHANNEL_H 107 | -------------------------------------------------------------------------------- /multiProcess1/net/Epoll.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_EPOLL_H 2 | #define TINYWS_EPOLL_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../base/noncopyable.h" 11 | #include "Timer.h" 12 | 13 | namespace tinyWS_process1 { 14 | class Channel; 15 | class EventLoop; 16 | 17 | // Epoll 的职责: 18 | // 1 管理(增删改) Channel 列表:有关心事件的 Channel 和 无关心事件的 Channel; 19 | // 2 Epoll::poll(),监听文件描述符(有关心事件的 Channel 对应的文件描述符)。 20 | // 当有事件发生时,将"活跃"的 Channel 填充到 activeChannel 中, 21 | // 供 EventLoop 处理相应的事件,即调用 Channel::handleEvent()。 22 | // 23 | // Epoll 是 EventLoop 对象的间接成员, 24 | // 只供 owner EventLoop 在 IO 线程调用,因此无须加锁。 25 | // 其生命周期与 EVentLoop 一样长。 26 | // 27 | // Epoll 采用的是 level trigger。 28 | class Epoll : noncopyable { 29 | public: 30 | using ChannelList = std::vector; // Channel 列表类型 31 | 32 | private: 33 | using EventList = std::vector; // epoll 事件列表类型 34 | using ChannelMap = std::map; // <文件描述符, Channel> 映射 35 | 36 | static const int kInitEventListSize = 16; // 事件数组(EventList)的默认大小 37 | 38 | EventLoop* loop_; 39 | int epollfd_; 40 | EventList events_; 41 | ChannelMap channels_; 42 | 43 | public: 44 | explicit Epoll(EventLoop* loop); 45 | 46 | ~Epoll(); 47 | 48 | TimeType poll(int timeoutMS, ChannelList* activeChannels); 49 | 50 | void updateChannel(Channel* channel); 51 | 52 | void removeChannel(Channel* channel); 53 | 54 | bool hasChannel(Channel* channel); 55 | 56 | private: 57 | void fillActiveChannels(int eventNums, ChannelList *activeChannels) const; 58 | 59 | void update(int operation, Channel* channel); 60 | 61 | std::string operationToString(int operation) const; 62 | }; 63 | } 64 | 65 | #endif //TINYWS_EPOLL_H 66 | -------------------------------------------------------------------------------- /multiProcess1/net/EventLoop.cpp: -------------------------------------------------------------------------------- 1 | #include "EventLoop.h" 2 | 3 | #include 4 | 5 | #include "Channel.h" 6 | #include "Epoll.h" 7 | #include "TimerQueue.h" 8 | #include "TimerId.h" 9 | #include "status.h" 10 | 11 | using namespace tinyWS_process1; 12 | 13 | const int kEpollTimeMs = 10000; 14 | 15 | EventLoop::EventLoop() 16 | : running_(false), 17 | epoll_(new Epoll(this)), 18 | pid_(getpid()), 19 | timerQueue_(new TimerQueue(this)) { 20 | 21 | // std::cout << "EventLoop created " 22 | // << this << " in process " 23 | // << pid_ << std::endl; 24 | } 25 | 26 | EventLoop::~EventLoop() { 27 | 28 | } 29 | 30 | void EventLoop::loop() { 31 | running_ = true; 32 | 33 | // std::cout << "EventLoop " << this << " create looping" << std::endl; 34 | 35 | while (running_) { 36 | activeChannels_.clear(); 37 | auto receiveTime = epoll_->poll(kEpollTimeMs, &activeChannels_); 38 | 39 | // stop this loop if get signal SIGINT SIGTERM SIGKILL SIGQUIT SIGCHLD(parent process) 40 | if (status_quit_softly == 1 || status_terminate == 1 || status_child_quit == 1) { 41 | // std::cout << "process(" << getpid() << ") quit this eventloop" << std::endl; 42 | running_ = false; 43 | break; 44 | } 45 | 46 | for (auto channel : activeChannels_) { 47 | channel->handleEvent(receiveTime); 48 | } 49 | } 50 | } 51 | 52 | void EventLoop::quit() { 53 | running_ = false; 54 | } 55 | 56 | TimerId EventLoop::runAt(TimeType runTime, const Timer::TimerCallback& cb) { 57 | return timerQueue_->addTimer(cb, runTime, 0); 58 | } 59 | 60 | TimerId EventLoop::runAfter(TimeType delay, const Timer::TimerCallback& cb) { 61 | return runAt(Timer::now() + delay, cb); 62 | } 63 | 64 | TimerId EventLoop::runEvery(TimeType interval, const Timer::TimerCallback& cb) { 65 | // std::cout << timerQueue_.get() << std::endl; 66 | return timerQueue_->addTimer(cb, Timer::now(), interval); 67 | } 68 | 69 | void EventLoop::cancel(const TimerId& timerId) { 70 | timerQueue_->cancel(timerId); 71 | } 72 | 73 | void EventLoop::updateChannel(Channel *channel) { 74 | epoll_->updateChannel(channel); 75 | } 76 | 77 | void EventLoop::removeChannel(Channel *channel) { 78 | epoll_->removeChannel(channel); 79 | } 80 | 81 | 82 | void EventLoop::printActiveChannels() const { 83 | for (auto channel : activeChannels_) { 84 | // std::cout << "{" << channel->reventsToString() << "} " << std::endl; 85 | } 86 | } -------------------------------------------------------------------------------- /multiProcess1/net/EventLoop.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_EVENTLOOP_H 2 | #define TINYWS_EVENTLOOP_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "Timer.h" 10 | #include "../base/noncopyable.h" 11 | 12 | namespace tinyWS_process1 { 13 | class Channel; 14 | class Epoll; 15 | class TimerQueue; 16 | class TimerId; 17 | 18 | class EventLoop : noncopyable { 19 | private: 20 | using ChannelList = std::vector; 21 | 22 | bool running_; 23 | pid_t pid_; 24 | std::unique_ptr epoll_; 25 | std::unique_ptr timerQueue_; 26 | ChannelList activeChannels_; 27 | 28 | public: 29 | EventLoop(); 30 | 31 | ~EventLoop(); 32 | 33 | void loop(); 34 | 35 | void quit(); 36 | 37 | TimerId runAt(TimeType runTime, const Timer::TimerCallback& cb); 38 | 39 | TimerId runAfter(TimeType delay, const Timer::TimerCallback& cb); 40 | 41 | TimerId runEvery(TimeType interval, const Timer::TimerCallback& cb); 42 | 43 | void cancel(const TimerId& timerId); 44 | 45 | void updateChannel(Channel* channel); 46 | 47 | void removeChannel(Channel* channel); 48 | 49 | private: 50 | void printActiveChannels() const; // for DEBUG 51 | }; 52 | } 53 | 54 | 55 | #endif //TINYWS_EVENTLOOP_H 56 | -------------------------------------------------------------------------------- /multiProcess1/net/InternetAddress.cpp: -------------------------------------------------------------------------------- 1 | #include "InternetAddress.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace tinyWS_process1; 9 | 10 | 11 | // // Structure describing an Internet socket address. 12 | // struct sockaddr_in { 13 | // sa_family_t sin_family; /* address family: AF_INET */ 14 | // uint16_t sin_port; /* port in network byte order */ 15 | // struct in_addr sin_addr; /* internet address */ 16 | // }; 17 | 18 | // // Internet address. 19 | // typedef uint32_t in_addr_t; 20 | // struct in_addr { 21 | // in_addr_t s_addr; /* address in network byte order */ 22 | // }; 23 | 24 | InternetAddress::InternetAddress(uint16_t port) : address_{} { 25 | address_.sin_family = AF_INET; 26 | address_.sin_addr.s_addr = INADDR_ANY; 27 | address_.sin_port = htobe16(port); 28 | } 29 | 30 | InternetAddress::InternetAddress(const std::string& ip, uint16_t port) : address_{} { 31 | address_.sin_family = AF_INET; 32 | address_.sin_port = htobe16(port); 33 | if (inet_pton(AF_INET, ip.c_str(), &address_.sin_addr) <= 0) { 34 | // std::cout << "InternetAddress::InternetAddress(const std::string &ip, uint16_t port)" 35 | // << std::endl; 36 | } 37 | } 38 | 39 | InternetAddress::InternetAddress(const sockaddr_in& address) : address_(address) {} 40 | 41 | std::string InternetAddress::toIP() const { 42 | const int size = 32; 43 | char buf[size]; 44 | inet_ntop(AF_INET, &address_.sin_addr, buf, static_cast(size)); 45 | 46 | return buf; 47 | } 48 | 49 | std::string InternetAddress::toIPPort() const { 50 | const int size = 32; 51 | char buf[size]; 52 | 53 | char host[INET_ADDRSTRLEN] = "INVALID"; 54 | inet_ntop(AF_INET, &address_.sin_addr, host, static_cast(strlen(host))); 55 | 56 | uint16_t port = be16toh(address_.sin_port); 57 | snprintf(buf, size, "%s:%u", host, port); 58 | 59 | return buf; 60 | } 61 | 62 | const sockaddr_in& InternetAddress::getSockAddrInternet() const { 63 | return address_; 64 | } 65 | 66 | void InternetAddress::setSockAddrInternet(const sockaddr_in& address) { 67 | address_ = address; 68 | } 69 | 70 | uint32_t InternetAddress::ipNetEnd() const { 71 | return address_.sin_addr.s_addr; 72 | } 73 | 74 | uint16_t InternetAddress::portNetEnd() const { 75 | return address_.sin_port; 76 | } 77 | 78 | sockaddr_in InternetAddress::getLocalAddress(int sockfd) { 79 | sockaddr_in localAddress{}; 80 | socklen_t addressLen = sizeof(localAddress); 81 | if (getsockname(sockfd, reinterpret_cast(&localAddress), &addressLen) < 0) { 82 | std::cout << "InternetAddress::getPeerAddress" << std::endl; 83 | } 84 | 85 | return localAddress; 86 | } 87 | 88 | sockaddr_in InternetAddress::getPeerAddress(int sockfd) { 89 | sockaddr_in peerAddress{}; 90 | socklen_t addressLen = sizeof(peerAddress); 91 | if (getpeername(sockfd, reinterpret_cast(&peerAddress), &addressLen) < 0) { 92 | std::cout << "InternetAddress::getPeerAddress" << std::endl; 93 | } 94 | 95 | return peerAddress; 96 | } 97 | -------------------------------------------------------------------------------- /multiProcess1/net/InternetAddress.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_INTERNETADDRESS_H 2 | #define TINYWS_INTERNETADDRESS_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | namespace tinyWS_process1 { 9 | 10 | class InternetAddress { 11 | private: 12 | sockaddr_in address_; 13 | 14 | public: 15 | explicit InternetAddress(uint16_t port = 0); 16 | 17 | InternetAddress(const std::string& ip, uint16_t port); 18 | 19 | explicit InternetAddress(const sockaddr_in& address); 20 | 21 | std::string toIP() const; 22 | 23 | std::string toIPPort() const; 24 | 25 | const sockaddr_in& getSockAddrInternet() const; 26 | 27 | void setSockAddrInternet(const sockaddr_in &address); 28 | 29 | uint32_t ipNetEnd() const; 30 | 31 | uint16_t portNetEnd() const; 32 | 33 | static sockaddr_in getLocalAddress(int sockfd); 34 | 35 | static sockaddr_in getPeerAddress(int sockfd); 36 | }; 37 | } 38 | 39 | 40 | #endif //TINYWS_INTERNETADDRESS_H 41 | -------------------------------------------------------------------------------- /multiProcess1/net/Process.h: -------------------------------------------------------------------------------- 1 | #ifndef tinyWS_process1_H 2 | #define tinyWS_process1_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../base/noncopyable.h" 9 | #include "SocketPair.h" 10 | #include "../base/Signal.h" 11 | 12 | namespace tinyWS_process1 { 13 | 14 | class EventLoop; 15 | class Socket; 16 | 17 | class Process : public noncopyable { 18 | public: 19 | using ProcessFunction = std::function; 20 | using ChildConnectionCallback = std::function; 21 | 22 | private: 23 | EventLoop* loop_; 24 | bool running_; 25 | pid_t pid_; 26 | SocketPair pipe_; 27 | SignalManager signalManager_; 28 | 29 | ChildConnectionCallback childConnectionCallback_; 30 | 31 | public: 32 | explicit Process(int fds[2]); 33 | 34 | ~Process(); 35 | 36 | void start(); 37 | 38 | bool started() const; 39 | 40 | void setAsChild(int port); 41 | 42 | void setSignalHandlers(); 43 | 44 | void setChildConnectionCallback(const ChildConnectionCallback& cb); 45 | 46 | pid_t getPid() const; 47 | 48 | private: 49 | void newConnection(int sockfd); 50 | 51 | static void childSignalHandler(int signo); 52 | }; 53 | } 54 | 55 | 56 | #endif //tinyWS_process1_H 57 | -------------------------------------------------------------------------------- /multiProcess1/net/ProcessPool.h: -------------------------------------------------------------------------------- 1 | #ifndef tinyWS_process1POOL_H 2 | #define tinyWS_process1POOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "Process.h" 12 | #include "../base/Signal.h" 13 | 14 | namespace tinyWS_process1 { 15 | 16 | class EventLoop; 17 | class SocketPair; 18 | class Socket; 19 | 20 | class ProcessPool { 21 | public: 22 | using ForkCallback = std::function; 23 | 24 | private: 25 | EventLoop* baseLoop_; // 父进程事件循环 26 | std::vector> pipes_; 27 | std::vector pids_; 28 | int processNum_; 29 | 30 | // std::string name_; 31 | bool running_; 32 | int next_; 33 | 34 | SignalManager signalManager_; 35 | 36 | ForkCallback forkFunction_; 37 | Process::ChildConnectionCallback childConnectionCallback_; 38 | 39 | public: 40 | explicit ProcessPool(EventLoop* loop); 41 | 42 | ~ProcessPool(); 43 | 44 | void setProcessNum(int processNum); 45 | 46 | void start(); 47 | 48 | void killAll(); 49 | 50 | void killSoftly(); 51 | 52 | void sendToChild(Socket socket); 53 | 54 | void setForkFunction(const ForkCallback& cb); 55 | 56 | void setSignalHandlers(); 57 | 58 | void setChildConnectionCallback(const Process::ChildConnectionCallback& cb); 59 | 60 | void newChildConnection(EventLoop* loop, Socket socket); 61 | 62 | private: 63 | void createChildAndSetParent(int processNum); 64 | 65 | pid_t createChildProcess(int fds[2]); 66 | 67 | void addChildInfoToParent(pid_t childPid, int fds[2]); 68 | 69 | void parentStart(); 70 | 71 | void clearDeadChild(); 72 | 73 | static void parentSignalHandler(int signo); 74 | }; 75 | } 76 | 77 | 78 | #endif //tinyWS_process1POOL_H 79 | -------------------------------------------------------------------------------- /multiProcess1/net/Socket.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_SOCKET_H 2 | #define TINYWS_SOCKET_H 3 | 4 | #include "../base/noncopyable.h" 5 | 6 | namespace tinyWS_process1 { 7 | 8 | class InternetAddress; 9 | 10 | class Socket : noncopyable { 11 | private: 12 | int sockfd_; 13 | 14 | public: 15 | explicit Socket(int sockfd); 16 | 17 | ~Socket(); 18 | 19 | Socket(Socket&& socket) noexcept; 20 | 21 | Socket& operator=(Socket&& rhs) noexcept; 22 | 23 | int fd() const; 24 | 25 | void setNoneFd(); 26 | 27 | bool isValid() const; 28 | 29 | void bindAddress(const InternetAddress& localAddress); 30 | 31 | void listen(); 32 | 33 | int accept(InternetAddress* peerAddress); 34 | 35 | void shutdownWrite(); 36 | 37 | void setTcpNoDelay(bool on); 38 | 39 | void setReuseAddr(bool on); 40 | 41 | void setKeepAlive(bool on); 42 | 43 | int getSocketError(); 44 | }; 45 | 46 | } 47 | 48 | #endif //TINYWS_SOCKET_H 49 | -------------------------------------------------------------------------------- /multiProcess1/net/SocketPair.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_SOCKETPAIR_H 2 | #define TINYWS_SOCKETPAIR_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../base/noncopyable.h" 9 | #include "type.h" 10 | #include "Socket.h" 11 | 12 | namespace tinyWS_process1 { 13 | 14 | class EventLoop; 15 | class TcpConnection; 16 | class Channel; 17 | class Socket; 18 | class InternetAddress; 19 | 20 | class SocketPair : noncopyable { 21 | public: 22 | using ReceiveFdCallback = std::function; 23 | using CloseCallback = std::function; 24 | 25 | private: 26 | EventLoop* loop_; 27 | int fds_[2]; 28 | std::unique_ptr connection_; 29 | std::unique_ptr pipeChannel_; 30 | 31 | bool isParent_; 32 | 33 | ReceiveFdCallback receiveFdCallback_; 34 | CloseCallback closeCallback_; 35 | 36 | public: 37 | SocketPair(EventLoop* loop, int fds[2]); 38 | 39 | ~SocketPair(); 40 | // 41 | // SocketPair(SocketPair&& other) noexcept; 42 | // 43 | // SocketPair& operator=(SocketPair&& other) noexcept; 44 | 45 | void setParentSocket(); 46 | 47 | void setChildSocket(); 48 | 49 | // void clear(); 50 | 51 | void sendFdToChild(Socket socket); 52 | 53 | void sendFdToParent(Socket socket); 54 | 55 | void setReceiveFdCallback(const ReceiveFdCallback& cb); 56 | 57 | void setCloseCallback(const CloseCallback& cb); 58 | 59 | private: 60 | void sendFd(Socket socket); 61 | 62 | int receiveFd(); 63 | 64 | void handleRead(); 65 | }; 66 | 67 | } 68 | 69 | #endif //TINYWS_SOCKETPAIR_H 70 | -------------------------------------------------------------------------------- /multiProcess1/net/TcpServer.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TCPSERVER_H 2 | #define TINYWS_TCPSERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../base/noncopyable.h" 10 | #include "TcpConnection.h" 11 | #include "Timer.h" 12 | #include "type.h" 13 | 14 | namespace tinyWS_process1 { 15 | 16 | class EventLoop; 17 | class Acceptor; 18 | class ProcessPool; 19 | class InternetAddress; 20 | class TimerId; 21 | 22 | // TcpServer 的功能:管理 Acceptor 获得的 TcpConnection。 23 | // TcpServer 是供用户直接使用的,生命周期由用户控制。 24 | // 用户只需要设置好 callback,再调用 createProccesses() 即可。 25 | // 26 | // 尽量让依赖是单向的。 27 | // TcpServer 用到 Acceptor,但 Acceptor 并不知道 TcpServer 的存在。 28 | // TcpServer 创建 TcpConnection,但 TcpConnection 并不知道 TcpServer 的存在 29 | class TcpServer { 30 | public: 31 | using ProcessInitCallback = std::function; 32 | 33 | private: 34 | using ConnectionMap = std::map; 35 | 36 | EventLoop* loop_; 37 | const std::string name_; 38 | std::unique_ptr acceptor_; 39 | std::unique_ptr processPool_; 40 | 41 | int nextConnectionId_; 42 | bool started_; 43 | ConnectionMap connectionMap_; 44 | 45 | 46 | ConnectionCallback connectionCallback_; // 连接建立的回调函数 47 | MessageCallback messageCallback_; // 消息到来的回调函数 48 | ProcessInitCallback threadInitCallback_; // 线程初始化的回调函数 49 | 50 | public: 51 | TcpServer(const InternetAddress &address, const std::string &name); 52 | 53 | ~TcpServer(); 54 | 55 | EventLoop* getLoop() const; 56 | 57 | void setProcessNum(int processNum); 58 | 59 | void start(); 60 | 61 | /** 62 | * 设置连接建立的回调函数 63 | * @param cb 回调函数 64 | */ 65 | void setConnectionCallback(const ConnectionCallback &cb); 66 | 67 | /** 68 | * 设置消息到来的回调函数 69 | * @param cb 回调函数 70 | */ 71 | void setMessageCallback(const MessageCallback &cb); 72 | 73 | TimerId runAt(TimeType runTime, const Timer::TimerCallback& cb); 74 | 75 | TimerId runAfter(TimeType delay, const Timer::TimerCallback& cb); 76 | 77 | TimerId runEvery(TimeType interval, const Timer::TimerCallback& cb); 78 | 79 | private: 80 | void newConnectionInParent(Socket socket, const InternetAddress& peerAddress); 81 | 82 | void newConnectionInChild(EventLoop* loop, Socket socket); 83 | 84 | void removeConnection(const TcpConnectionPtr& connection); 85 | 86 | void clearInSubProcess(bool isParent); 87 | }; 88 | } 89 | 90 | 91 | #endif //TINYWS_TCPSERVER_H 92 | -------------------------------------------------------------------------------- /multiProcess1/net/Timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Timer.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace tinyWS_process1; 8 | 9 | AtomicInt64 Timer::s_numCreated_; 10 | 11 | Timer::Timer(const Timer::TimerCallback &cb, TimeType timeout, TimeType interval) 12 | : timerCallback_(cb), 13 | expiredTime_(timeout), 14 | interval_(interval), 15 | repeat_(interval_ > 0), 16 | sequence_(s_numCreated_.incrementAndGet()) { 17 | 18 | } 19 | 20 | void Timer::run() const { 21 | timerCallback_(); 22 | } 23 | 24 | TimeType Timer::getExpiredTime() { 25 | return expiredTime_; 26 | } 27 | 28 | void Timer::updateExpiredTime(TimeType timeout) { 29 | expiredTime_ = timeout; 30 | } 31 | 32 | bool Timer::repeat() const { 33 | return repeat_; 34 | } 35 | 36 | int64_t Timer::getSequence() const { 37 | return sequence_; 38 | } 39 | 40 | bool Timer::isValid() const { 41 | return expiredTime_ >= Timer::now(); 42 | } 43 | 44 | TimeType Timer::invalid() const { 45 | return 0; 46 | } 47 | 48 | void Timer::restart(TimeType now) { 49 | if (repeat_) { 50 | expiredTime_ = now + interval_; 51 | } else { 52 | expiredTime_ = invalid(); 53 | } 54 | } 55 | 56 | TimeType Timer::now() { 57 | // struct timeval { 58 | // time_t tv_sec; // seconds since Jan. 1, 1970 59 | // suseconds_t tv_usec; // and microseconds 60 | // }; 61 | std::shared_ptr tv(std::make_shared()); 62 | 63 | // SUSv4 指定 gettimeofday() 函数现已弃用。 64 | // gettimeofday() 不是系统调用,是在用户态实现的,没有上下文切换和陷入内核的开销。 65 | // 精度为1纳秒。 66 | gettimeofday(tv.get(), nullptr); 67 | 68 | return static_cast(tv->tv_sec * Timer::kMicroSecondsPerSecond + tv->tv_usec); 69 | } 70 | 71 | int64_t Timer::createNum() { 72 | return s_numCreated_.get(); 73 | } -------------------------------------------------------------------------------- /multiProcess1/net/Timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TIMER_H 2 | #define TINYWS_TIMER_H 3 | 4 | #include 5 | 6 | #include "../base/noncopyable.h" 7 | #include "type.h" 8 | #include "../base/Atomic.h" 9 | 10 | namespace tinyWS_process1 { 11 | 12 | class Timer : noncopyable { 13 | public: 14 | using TimerCallback = std::function; 15 | 16 | const static TimeType kMicroSecondsPerSecond = 1000 * 1000; // 一秒有 1000 * 1000 微秒 17 | 18 | private: 19 | const TimerCallback timerCallback_; 20 | TimeType expiredTime_; 21 | const TimeType interval_; 22 | const bool repeat_; 23 | const int64_t sequence_; 24 | 25 | static AtomicInt64 s_numCreated_; 26 | 27 | public: 28 | Timer(const TimerCallback& cb, TimeType timeout, TimeType interval = 0); 29 | 30 | void run() const; 31 | 32 | TimeType getExpiredTime(); 33 | 34 | void updateExpiredTime(TimeType timeout); 35 | 36 | bool repeat() const; 37 | 38 | int64_t getSequence() const; 39 | 40 | bool isValid() const; 41 | 42 | TimeType invalid() const; 43 | 44 | void restart(TimeType now); 45 | 46 | static TimeType now(); 47 | 48 | static int64_t createNum(); 49 | }; 50 | } 51 | 52 | 53 | #endif //TINYWS_TIMER_H 54 | -------------------------------------------------------------------------------- /multiProcess1/net/TimerId.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TIMERID_H 2 | #define TINYWS_TIMERID_H 3 | 4 | #include 5 | 6 | #include "Timer.h" 7 | 8 | namespace tinyWS_process1 { 9 | class TimerId{ 10 | public: 11 | friend class TimerQueue; 12 | 13 | private: 14 | std::weak_ptr timer_; 15 | int64_t sequence_; 16 | 17 | public: 18 | TimerId() : sequence_(0) {} 19 | 20 | explicit TimerId(const std::weak_ptr& timer) 21 | : timer_(timer), 22 | sequence_(timer.lock() ? timer.lock()->getSequence() : 0) { 23 | 24 | } 25 | }; 26 | } 27 | 28 | #endif //TINYWS_TIMERID_H 29 | -------------------------------------------------------------------------------- /multiProcess1/net/TimerQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TIMERQUEUE_H 2 | #define TINYWS_TIMERQUEUE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Channel.h" 10 | #include "../base/noncopyable.h" 11 | #include "Timer.h" 12 | 13 | namespace tinyWS_process1 { 14 | 15 | class EventLoop; 16 | class TimerId; 17 | 18 | class TimerQueue : noncopyable { 19 | private: 20 | using Entry = std::pair>; 21 | using TimerList = std::set; 22 | 23 | using ActiveTimer = std::pair, TimeType>; 24 | using ActiveTimerSet = std::set; 25 | 26 | EventLoop *loop_; 27 | 28 | const int timerfd_; 29 | Channel timerfdChannel_; 30 | TimerList timers_; 31 | 32 | ActiveTimerSet activeTimers_; 33 | bool callingExpiredTimers_; 34 | ActiveTimerSet cancelTimers_; 35 | 36 | public: 37 | explicit TimerQueue(EventLoop *loop); 38 | 39 | ~TimerQueue(); 40 | 41 | TimerId addTimer(const Timer::TimerCallback& cb, TimeType timeout, TimeType interval); 42 | 43 | void cancel(const TimerId& timerId); 44 | 45 | private: 46 | void handleRead(); 47 | 48 | std::vector getExpired(TimeType now); 49 | 50 | void reset(const std::vector& expired, TimeType now); 51 | 52 | bool insert(const std::shared_ptr& timer); 53 | }; 54 | } 55 | 56 | 57 | #endif //TINYWS_TIMERQUEUE_H 58 | -------------------------------------------------------------------------------- /multiProcess1/net/status.cpp: -------------------------------------------------------------------------------- 1 | #include "status.h" 2 | 3 | int tinyWS_process1::status_quit_softly = 0; //QUIT 4 | int tinyWS_process1::status_terminate = 0; //TERM,INT 5 | int tinyWS_process1::status_exiting = 0; 6 | int tinyWS_process1::status_restart = 0; 7 | int tinyWS_process1::status_reconfigure = 0; //HUP,reboot 8 | int tinyWS_process1::status_child_quit = 0; //CHLD 9 | -------------------------------------------------------------------------------- /multiProcess1/net/status.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_STATUS_H 2 | #define TINYWS_STATUS_H 3 | 4 | namespace tinyWS_process1 { 5 | extern int status_quit_softly; //QUIT 6 | extern int status_terminate; //TERM,INT 7 | extern int status_exiting; 8 | extern int status_restart; 9 | extern int status_reconfigure; //HUP,reboot 10 | extern int status_child_quit; //CHLD 11 | } 12 | 13 | #endif //TINYWS_STATUS_H 14 | -------------------------------------------------------------------------------- /multiProcess1/net/type.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TYPE_H 2 | #define TINYWS_TYPE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tinyWS_process1 { 9 | // 定时器中的时间类型 10 | using TimeType = int64_t; 11 | 12 | // TcpConnection 相关的类型 13 | class TcpConnection; 14 | class Buffer; 15 | 16 | // TcpConnection 对象的智能指针类型 17 | using TcpConnectionPtr = std::shared_ptr; 18 | 19 | // 连接建立的回调函数的类型 20 | using ConnectionCallback = std::function; 21 | 22 | // 消息到来的回调函数的类型 23 | using MessageCallback = std::function; 24 | 25 | // 连接断开的的回调函数的类型 26 | using CloseCallback = std::function; 27 | 28 | // 写完成的回调函数的类型 29 | using WriteCompleteCallback = std::function; 30 | } 31 | 32 | #endif //TINYWS_TYPE_H 33 | -------------------------------------------------------------------------------- /multiProcess1/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiProcess1/web/favicon.ico -------------------------------------------------------------------------------- /multiProcess1/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tinyWS 5 | 6 | 7 | 8 | 9 | Hello 10 | 11 | 12 |

这是我的HTML

13 |

Hello World!Chen Shuaihao's NetServer

14 | 15 | 16 | -------------------------------------------------------------------------------- /multiProcess2/README.md: -------------------------------------------------------------------------------- 1 | # tinyWS 2 | A C++ Tiny Web Server 3 | 4 | ## 已完成 5 | 6 | - 完成基本的Tcp(被动连接)库; 7 | - 简易的HTTP服务器,可访问主页HTML和网站favicon图标。 8 | 9 | ## 技术 10 | 11 | - EventLoop:使用 Epoll 水平触发的模式结合非阻塞 IO; 12 | - 使用智能指针等 RAII 机制,来为降低内存泄漏的可能性; 13 | 14 | ## 并发模型 15 | 16 | 所有进程监听同一个 listen sockfd,accept 到新的连接后自己处理连接的读写。所有进程通过竞争设置了`PTHREAD_PROCESS_SHARED`属性的`mutex`来获取处理 listen sockfd 的机会,保证了同一时刻只有一个进程监听 listen sockfd 的 IO 事件(只有读事件),解决了***惊群问题***。 17 | 18 | 19 | 20 | 21 | 22 | ## 参考 23 | 24 | - [陈硕老师的Blog](http://www.cppblog.com/solstice/) 25 | - [linyacool](https://github.com/linyacool)的[WebServer](https://github.com/linyacool/WebServer) 26 | - [uv-cpp](https://github.com/wlgq2/uv-cpp) 27 | - [开源HTTP解析器](https://www.cnblogs.com/arnoldlu/p/6497837.html) 28 | - [HTTP请求和响应格式](https://www.cnblogs.com/yaozhongxiao/archive/2013/03/02/2940252.html) 29 | - [写一个并发http服务器](https://zhuanlan.zhihu.com/p/23336565)(有代码) 30 | - [有什么适合提高 C/C++ 网络编程能力的开源项目推荐? - Variation的回答 - 知乎](https://www.zhihu.com/question/20124494/answer/733016078)(master/worker、proactor 等 IO 模型) 31 | - [Reactor事件驱动的两种设计实现:面向对象 VS 函数式编程](http://www.cnblogs.com/me115/p/5088914.html) 32 | - [网络编程中的关键问题总结](https://www.cnblogs.com/me115/p/5092091.html) 33 | - [muduo源码剖析 - cyhone的文章 - 知乎](https://zhuanlan.zhihu.com/p/85101271) 34 | - [Muduo 源码分析](https://youjiali1995.github.io/network/muduo/) -------------------------------------------------------------------------------- /multiProcess2/base/Atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_ATOMIC_H 2 | #define TINYWS_ATOMIC_H 3 | 4 | #include 5 | 6 | #include "noncopyable.h" 7 | 8 | namespace tinyWS_process2 { 9 | template 10 | class AtomicIntegerT : noncopyable { 11 | private: 12 | // volatile 关键字,参考: 13 | // https://zh.cppreference.com/w/cpp/language/cv 14 | // https://liam.page/2018/01/18/volatile-in-C-and-Cpp/ 15 | volatile T value_; 16 | 17 | public: 18 | AtomicIntegerT() : value_(0) {} 19 | 20 | T get() { 21 | // 可用 C++ 11 atomic 头文件:std::atomic_load 代替。 22 | // 参考 https://zh.cppreference.com/w/cpp/atomic/atomic_load 23 | return __atomic_load_n(&value_, __ATOMIC_SEQ_CST); 24 | } 25 | 26 | T getAndAdd(T x) { 27 | // 可用 C++ 11 atomic 头文件:std::atomic_fetch_add 代替。 28 | // 参考 https://zh.cppreference.com/w/cpp/atomic/atomic_fetch_add 29 | return __atomic_fetch_add(&value_, x, __ATOMIC_SEQ_CST); 30 | } 31 | 32 | T addAndGet(T x) { 33 | return getAndAdd(x) + x; 34 | } 35 | 36 | T incrementAndGet() { 37 | return addAndGet(1); 38 | } 39 | 40 | T decrementAndGet() { 41 | return addAndGet(-1); 42 | } 43 | 44 | void add(T x) { 45 | getAndAdd(x); 46 | } 47 | 48 | void increment() { 49 | incrementAndGet(); 50 | } 51 | 52 | void decrement() { 53 | decrementAndGet(); 54 | } 55 | 56 | T getAndSet(T newValue) { 57 | // 可用 C++ 11 atomic 头文件:std::atomic_exchange 代替。 58 | // 参考 https://zh.cppreference.com/w/cpp/atomic/atomic_exchange 59 | return __atomic_exchange_n(&value_, newValue, __ATOMIC_SEQ_CST); 60 | } 61 | }; 62 | 63 | using AtomicInt32 = AtomicIntegerT; 64 | using AtomicInt64 = AtomicIntegerT; 65 | } 66 | 67 | #endif //TINYWS_ATOMIC_H 68 | -------------------------------------------------------------------------------- /multiProcess2/base/ProcessCondition.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_PROCESSCONDITION_H 2 | #define TINYWS_PROCESSCONDITION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "noncopyable.h" 11 | #include "ProcessMutexLock.h" 12 | 13 | namespace tinyWS_process2 { 14 | 15 | class ProcessCondition : noncopyable { 16 | private: 17 | ProcessMutexLock &mutex_; 18 | pthread_cond_t* cond_; 19 | 20 | public: 21 | explicit ProcessCondition(ProcessMutexLock& mutex) 22 | : mutex_(mutex), 23 | cond_{} { 24 | 25 | // 读设备 /dev/zero 时,该设备是 0 字节的无限资源。 26 | // 它可以接受写向它的任何数据,但又忽略这些数据。 27 | int fd = open("/dev/zero", O_RDWR, 0); 28 | 29 | // PROT_READ | PROT_WRITE:设置映射区域可读可写。 30 | // MAP_SHARED:父进程在调用 mmap 函数时指定了 MAP_SHARED 标志,则这些进程可共享此内存区域。从而达到了共享内存的目的。 31 | cond_ = static_cast(mmap(nullptr, 32 | sizeof(pthread_cond_t), 33 | PROT_READ | PROT_WRITE, 34 | MAP_SHARED, fd, 35 | 0)); 36 | close(fd); // 已经映射完成,可以关闭文件描述符。 37 | 38 | pthread_condattr_t condattr{}; 39 | pthread_condattr_init(&condattr); 40 | // PTHREAD_PROCESS_SHARED:允许在不同进程之间共享条件变量。 41 | pthread_condattr_setpshared(&condattr, PTHREAD_PROCESS_SHARED); 42 | assert(pthread_cond_init(cond_, nullptr) == 0); 43 | } 44 | 45 | ~ProcessCondition() { 46 | // 销毁条件变量,释放映射的内存区域 47 | pthread_cond_destroy(cond_); 48 | munmap(cond_, sizeof(pthread_cond_t)); 49 | } 50 | 51 | void wait() { 52 | pthread_cond_wait(cond_, mutex_.getPthreadMutexPtr()); 53 | } 54 | 55 | /** 56 | * 等待规定时间 57 | * @param second 等待的时间 58 | * @return 如果超时,则返回true;否则,返回false 59 | */ 60 | bool waitForSecond(int second) { 61 | struct timespec timeout{}; 62 | clock_getres(CLOCK_REALTIME, &timeout); 63 | timeout.tv_sec += second; 64 | return pthread_cond_timedwait(cond_, mutex_.getPthreadMutexPtr(), &timeout) == ETIMEDOUT; 65 | } 66 | 67 | void notify() { 68 | pthread_cond_signal(cond_); 69 | } 70 | 71 | void notifyAll() { 72 | pthread_cond_broadcast(cond_); 73 | } 74 | }; 75 | 76 | } 77 | 78 | #endif //TINYWS_PROCESSCONDITION_H 79 | -------------------------------------------------------------------------------- /multiProcess2/base/Signal.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_SIGNAL_H 2 | #define TINYWS_SIGNAL_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace tinyWS_process2 { 11 | 12 | class Signal { 13 | public: 14 | typedef void (*SignalCallback)(int); 15 | 16 | friend class SignalManager; 17 | 18 | private: 19 | int signo_; 20 | std::string name_; 21 | std::string meaning_; 22 | SignalCallback signalCallback_; 23 | 24 | public: 25 | Signal(int signo, 26 | const std::string& name, 27 | const std::string& meaing, 28 | const SignalCallback& cb) 29 | : signo_(signo), 30 | name_(name), 31 | meaning_(meaing), 32 | signalCallback_(cb) { 33 | 34 | // std::cout << "class Signal constructor" << std::endl; 35 | } 36 | 37 | ~Signal() { 38 | // std::cout << "class Signal destructor" << std::endl; 39 | } 40 | 41 | int signo() const { 42 | return signo_; 43 | } 44 | 45 | std::string name() const { 46 | return name_; 47 | } 48 | 49 | std::string meaning() const { 50 | return meaning_; 51 | } 52 | 53 | bool isSame(int signal) const { 54 | return signo_ == signal; 55 | } 56 | }; 57 | 58 | class SignalManager { 59 | private: 60 | unsigned int num_; 61 | 62 | public: 63 | SignalManager() : num_(0) { 64 | 65 | } 66 | 67 | ~SignalManager() { 68 | // std::cout << "class SignalManager destructor" << std::endl; 69 | } 70 | 71 | void addSignal(const Signal& sign) { 72 | updateSignal(sign.signo_, sign.signalCallback_); 73 | ++num_; 74 | } 75 | 76 | void updateSignal(const Signal& sign) { 77 | updateSignal(sign.signo_, sign.signalCallback_); 78 | } 79 | 80 | void deleteSignal(const Signal& sign) { 81 | removeSignal(sign.signo_); 82 | --num_; 83 | } 84 | 85 | private: 86 | void updateSignal(int signo, sighandler_t handler) { 87 | struct sigaction sa{}; 88 | sa.sa_handler = handler; 89 | sa.sa_flags |= SA_RESTART; 90 | sigfillset(&sa.sa_mask); 91 | int result = sigaction(signo, &sa, nullptr); 92 | if (result == -1) { 93 | std::cerr << "update signal error: " << errno << std::endl; 94 | } 95 | } 96 | 97 | void removeSignal(int signo) { 98 | struct sigaction sa{}; 99 | sa.sa_handler = nullptr; 100 | sa.sa_flags |= SA_RESTART; 101 | sigfillset(&sa.sa_mask); 102 | int result = sigaction(signo, &sa, nullptr); 103 | if (result == -1) { 104 | std::cerr << "remove signal error: " << errno << std::endl; 105 | } 106 | } 107 | }; 108 | 109 | } 110 | 111 | #endif //TINYWS_SIGNAL_H 112 | -------------------------------------------------------------------------------- /multiProcess2/base/noncopyable.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_NONCOPYABLE_H 2 | #define TINYWS_NONCOPYABLE_H 3 | 4 | namespace tinyWS_process2 { 5 | // 不可拷贝类 6 | class noncopyable { 7 | protected: 8 | // 默认的构造函数和析构函数是 protected, 9 | // 不允许创建 noncopyable 实例,但允许子类创建实例 10 | // (即允许派生类构造和析构)。 11 | noncopyable() = default; 12 | 13 | ~noncopyable() = default; 14 | 15 | private: 16 | // 使用 delete 关键字禁止编译器自动产生复制构造函数和复制赋值操作符。 17 | noncopyable(const noncopyable&) = delete; 18 | 19 | noncopyable& operator=(const noncopyable&) = delete; 20 | }; 21 | } 22 | 23 | #endif //TINYWS_NONCOPYABLE_H 24 | -------------------------------------------------------------------------------- /multiProcess2/base/utility.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_UTILITY_H 2 | #define TINYWS_UTILITY_H 3 | 4 | #include 5 | #include 6 | 7 | namespace tinyWS_process2 { 8 | 9 | // FIXME 不能用,会报错 10 | template 11 | std::unique_ptr make_unique(Arg&&... arg) { 12 | return std::unique_ptr(std::forward(arg)...); 13 | } 14 | } 15 | 16 | #endif //TINYWS_UTILITY_H 17 | -------------------------------------------------------------------------------- /multiProcess2/doc/pressure_test.md: -------------------------------------------------------------------------------- 1 | # 压力测试 2 | 3 | ## 测试环境 4 | 5 | ### 华为云虚拟主机 6 | 7 | - OS: Ubuntu 18.04 LTS 8 | - CPU: 1核 9 | - 内存:2M 10 | 11 | ## 测试方法 12 | 13 | - 为了避免带宽带来的影响,将服务器程序和 WebBench 程序都运行在同一台主机上。 14 | - 使用工具 Webbench,开启 1000 客户端进程,时间为 60s。 15 | - 分别测试短连接和长连接的情况。 16 | - 为避免磁盘 IO 对测试结果的影响,测试响应为内存中的"Hello World"字符加上必要的HTTP头。 17 | - 创建 4 个子进程。 18 | 19 | ## 测试结果 20 | 21 | | 短连接QPS | 长连接QPS | 22 | | --------- | --------- | 23 | | 7638 | 30608 | 24 | 25 | 处理请求数方面,长连接比短连接能很多,多了 4 倍。 26 | 27 | ## 测试结果截图 28 | 29 | - 短连接 30 | 31 | ![短连接](./pressure_test_short.png) 32 | 33 | - 长连接 34 | 35 | ![长连接](./pressure_test_keep_alive.png) 36 | 37 | ## 参考 38 | 39 | - [linyacool](https://github.com/linyacool)的[WebServer](https://github.com/linyacool/WebServer)中的[测试及改进]([https://github.com/linyacool/WebServer/blob/HEAD/%E6%B5%8B%E8%AF%95%E5%8F%8A%E6%94%B9%E8%BF%9B.md](https://github.com/linyacool/WebServer/blob/HEAD/测试及改进.md))。 40 | - [WebBench](https://github.com/linyacool/WebBench)来自[linyacool](https://github.com/linyacool)的[WebServer](https://github.com/linyacool/WebServer)。 -------------------------------------------------------------------------------- /multiProcess2/doc/pressure_test_keep_alive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiProcess2/doc/pressure_test_keep_alive.png -------------------------------------------------------------------------------- /multiProcess2/doc/pressure_test_short.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiProcess2/doc/pressure_test_short.png -------------------------------------------------------------------------------- /multiProcess2/http/HttpContext.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPCONTEXT_H 2 | #define TINYWS_HTTPCONTEXT_H 3 | 4 | #include "HttpRequest.h" 5 | #include "../net/type.h" 6 | 7 | namespace tinyWS_process2 { 8 | 9 | class Buffer; 10 | 11 | class HttpContext { 12 | public: 13 | // 解析状态 14 | enum HttpRequestParseState { 15 | kExpectRequestLine, // 解析行 16 | kExpectHeader, // 解析请求头 17 | kExpectBody, // 解析 Body 18 | kGotAll // 解析完成 19 | }; 20 | 21 | private: 22 | HttpRequestParseState state_; 23 | HttpRequest request_; 24 | 25 | public: 26 | HttpContext(); 27 | 28 | /** 29 | * 解析 Buffer 的数据,并将相应的信息添加到 HttpRequest 中,最后返回是否解析成功。 30 | * @param buffer 缓冲区 31 | * @param receiveTime 连接接收时间 32 | * @return 是否解析成功 33 | */ 34 | bool parseRequest(Buffer* buffer, TimeType receiveTime); 35 | 36 | /* 37 | * 是否解析完成 38 | */ 39 | bool gotAll() const; 40 | 41 | /** 42 | * 重置解析状态为 kExpectRequestLine,清空 HttpRequest 43 | */ 44 | void reset(); 45 | 46 | /** 47 | * 获取 HttpRequest 48 | * @return HttpRequest 49 | */ 50 | const HttpRequest& request() const; 51 | 52 | // 同上 53 | HttpRequest& request(); 54 | 55 | private: 56 | bool processRequestLine(const char* start, const char* end); 57 | 58 | }; 59 | 60 | } 61 | 62 | #endif //TINYWS_HTTPCONTEXT_H 63 | -------------------------------------------------------------------------------- /multiProcess2/http/HttpRequest.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPREQUEST_H 2 | #define TINYWS_HTTPREQUEST_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../net/type.h" 8 | 9 | namespace tinyWS_process2 { 10 | 11 | class HttpRequest { 12 | public: 13 | // 请求方法 14 | enum Method { 15 | kInvalid, kGet, kPost, kHead, kPut, kDelete 16 | }; 17 | 18 | private: 19 | Method method_; 20 | std::string path_; 21 | std::string query_; 22 | TimeType receiveTime_; 23 | std::map headers_; 24 | 25 | public: 26 | HttpRequest(); 27 | 28 | bool setMethod(const char* start, const char* end); 29 | 30 | Method method() const; 31 | 32 | const char* methodString() const; 33 | 34 | void setPath(const char* start, const char* end); 35 | 36 | const std::string& path() const; 37 | 38 | void setQuery(const char *start, const char *end); 39 | 40 | const std::string& query() const; 41 | 42 | void setReceiveTime(TimeType time); 43 | 44 | TimeType receiveTime() const; 45 | 46 | void addHeader(const char* start, const char* colon, const char* end); 47 | 48 | std::string getHeader(const std::string& field) const; 49 | 50 | const std::map& headers() const; 51 | 52 | void swap(HttpRequest& that); 53 | }; 54 | } 55 | 56 | 57 | #endif //TINYWS_HTTPREQUEST_H 58 | -------------------------------------------------------------------------------- /multiProcess2/http/HttpResponse.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpResponse.h" 2 | 3 | #include 4 | 5 | #include "../net/Buffer.h" 6 | 7 | using namespace tinyWS_process2; 8 | 9 | HttpResponse::HttpResponse(bool close) 10 | : statusCode_(kUnknown), 11 | closeConnection_(close){ 12 | 13 | } 14 | 15 | void HttpResponse::setStatusCode(HttpStatusCode code) { 16 | statusCode_ = code; 17 | } 18 | 19 | void HttpResponse::setStatusMessage(const std::string &message) { 20 | statusMessage_ = message; 21 | } 22 | 23 | void HttpResponse::setCloseConnection(bool on) { 24 | closeConnection_ = on; 25 | } 26 | 27 | bool HttpResponse::closeConnection() const { 28 | return closeConnection_; 29 | } 30 | 31 | void HttpResponse::setContentType(const std::string& contentType) { 32 | addHeader("Content-Type", contentType); 33 | } 34 | 35 | void HttpResponse::addHeader(const std::string& key, const std::string& value) { 36 | headers_[key] = value; 37 | } 38 | 39 | void HttpResponse::setBody(const std::string& body) { 40 | body_ = body; 41 | } 42 | 43 | void HttpResponse::appendToBuffer(Buffer* output) const { 44 | // 响应行:请求方法 路径 HTTP/版本 45 | char buf[32]; 46 | snprintf(buf, sizeof(buf), "HTTP/1.1 %d ", statusCode_); 47 | 48 | output->append(buf); 49 | output->append(statusMessage_); 50 | output->append("\r\n"); 51 | 52 | // 添加响应头 53 | if (closeConnection_) { 54 | // 关闭连接 55 | output->append("Connection: close\r\n"); 56 | } else { 57 | snprintf(buf, sizeof(buf), "Content-Length: %zd\r\n", body_.size()); 58 | output->append(buf); 59 | output->append("Connection: Keep-Alive\r\n"); 60 | } 61 | 62 | for (const auto &header : headers_) { 63 | output->append(header.first); 64 | output->append(": "); 65 | output->append(header.second); 66 | output->append("\r\n"); 67 | } 68 | 69 | output->append("\r\n"); 70 | output->append(body_); 71 | } 72 | -------------------------------------------------------------------------------- /multiProcess2/http/HttpResponse.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPRESPONSE_H 2 | #define TINYWS_HTTPRESPONSE_H 3 | 4 | #include 5 | #include 6 | 7 | namespace tinyWS_process2 { 8 | 9 | class Buffer; 10 | 11 | class HttpResponse { 12 | public: 13 | // 状态码 14 | enum HttpStatusCode { 15 | kUnknown, 16 | k200OK = 200, 17 | k301MovedPermanently = 301, 18 | k400BadRequest = 400, 19 | k404NotFound = 404 20 | }; 21 | 22 | private: 23 | std::map headers_; // 响应头映射 24 | HttpStatusCode statusCode_; // 状态码 25 | std::string statusMessage_; // 状态信息 26 | bool closeConnection_; // 是否将 Connection 字段设置为 close 27 | std::string body_; // Response Body 28 | 29 | public: 30 | /** 31 | * 构造函数 32 | * @param close 连接是否已关闭 33 | */ 34 | explicit HttpResponse(bool close); 35 | 36 | /** 37 | * 设置状态码 38 | * @param code 状态码 39 | */ 40 | void setStatusCode(HttpStatusCode code); 41 | 42 | /** 43 | * 设置状态信息 44 | * @param message 状态信息 45 | */ 46 | void setStatusMessage(const std::string& message); 47 | 48 | /** 49 | * 设置是否将 Connection 字段设置为 close 50 | * @param on true / false 51 | */ 52 | void setCloseConnection(bool on); 53 | 54 | /** 55 | * 获取是否已将 Connection 字段设置为 close 56 | * @return true / false 57 | */ 58 | bool closeConnection() const; 59 | 60 | /** 61 | * 设置 Content-Type 62 | * @param contentType Content-Type 字符串 63 | */ 64 | void setContentType(const std::string& contentType); 65 | 66 | /** 67 | * 添加响应头 68 | * @param key 69 | * @param value 70 | */ 71 | void addHeader(const std::string& key, const std::string& value); 72 | 73 | /** 74 | * 设置 Response Body 75 | * @param body Response Body 字符串 76 | */ 77 | void setBody(const std::string& body); 78 | 79 | /** 80 | * 将响应数据(包括响应头和 Body)添加到 Buffer 中 81 | * @param output 数据指针 82 | */ 83 | void appendToBuffer(Buffer* output) const; 84 | }; 85 | } 86 | 87 | 88 | #endif //TINYWS_HTTPRESPONSE_H 89 | -------------------------------------------------------------------------------- /multiProcess2/http/HttpServer.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPSERVER_H 2 | #define TINYWS_HTTPSERVER_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "../base/noncopyable.h" 9 | #include "../net/TcpServer.h" 10 | #include "../net/TcpConnection.h" 11 | #include "../net/Timer.h" 12 | #include "../net/type.h" 13 | 14 | namespace tinyWS_process2 { 15 | 16 | class Buffer; 17 | class HttpRequest; 18 | class HttpResponse; 19 | class TimerId; 20 | 21 | class HttpServer : noncopyable { 22 | public: 23 | // HTTP 请求到来时的回调函数的类型 24 | using HttpCallback = std::function; 25 | 26 | /** 27 | * 构造函数 28 | * @param loop 所属 EventLoop 29 | * @param listenAddress 监听地址 30 | * @param name TcpServer name 31 | */ 32 | HttpServer(const InternetAddress &listenAddress, const std::string &name); 33 | 34 | /** 35 | * 获取所属 EventLoop 36 | * @return EventLoop 37 | */ 38 | EventLoop* getLoop() const; 39 | 40 | /** 41 | * 设置 HTTP 请求到来时的回调函数 42 | * @param cb 43 | */ 44 | void setHttpCallback(const HttpCallback& cb); 45 | 46 | /** 47 | * 启动 TcpServer 48 | */ 49 | void start(); 50 | 51 | TimerId runAt(TimeType runTime, const Timer::TimerCallback& cb); 52 | 53 | TimerId runAfter(TimeType delay, const Timer::TimerCallback& cb); 54 | 55 | TimerId runEvery(TimeType interval, const Timer::TimerCallback& cb); 56 | 57 | private: 58 | TcpServer tcpServer_; // TcpServer 59 | HttpCallback httpCallback_; // HTTP 请求到来时的回调函数 60 | 61 | /** 62 | * 连接建立后,将 HttpContext 传给 TcpConnection。 63 | * @param connection TcpConnectionPtr 64 | */ 65 | void onConnection(const TcpConnectionPtr& connection); 66 | 67 | /** 68 | * 请求到来后,解析请求,响应请求。 69 | * @param connection 70 | * @param buffer 71 | * @param receiveTime 72 | */ 73 | void onMessage(const TcpConnectionPtr& connection, 74 | Buffer* buffer, 75 | TimeType receiveTime); 76 | /** 77 | * 当解析完一条请求信息后,响应请求。 78 | * @param connection TcpConnectionPtr 79 | * @param httpRequest 80 | */ 81 | void onRequest(const TcpConnectionPtr& connection, 82 | const HttpRequest& httpRequest); 83 | }; 84 | 85 | } 86 | 87 | #endif //TINYWS_HTTPSERVER_H 88 | -------------------------------------------------------------------------------- /multiProcess2/net/Acceptor.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_ACCEPTOR_H 2 | #define TINYWS_ACCEPTOR_H 3 | 4 | #include 5 | 6 | #include "../base/noncopyable.h" 7 | #include "Channel.h" 8 | #include "Socket.h" 9 | 10 | namespace tinyWS_process2 { 11 | 12 | class InternetAddress; 13 | class EventLoop; 14 | 15 | // 内部类,供 TcpServer 使用,生命周期由 TcpServer 控制。 16 | // 用于 accept(2) 新 TCP 连接,并通过回调函数通知使用者。 17 | class Acceptor : noncopyable { 18 | public: 19 | using NewConnectionCallback = std::function; 20 | 21 | private: 22 | EventLoop* loop_; 23 | Socket acceptSocket_; 24 | Channel acceptChannel_; 25 | NewConnectionCallback newConnectionCallback_; 26 | bool isListening_; 27 | 28 | public: 29 | Acceptor(EventLoop* loop, const InternetAddress& listenAddress); 30 | 31 | Acceptor(EventLoop* loop, Socket socket, const InternetAddress& listenAddress); 32 | 33 | ~Acceptor(); 34 | 35 | void resetLoop(EventLoop* loop); 36 | 37 | void setNewConnectionCallback(const NewConnectionCallback& cb); 38 | 39 | void listen(); 40 | 41 | void listenInEpoll(); 42 | 43 | void unlistenInEpoll(); 44 | 45 | bool isLIstening() const; 46 | 47 | int getSockfd() const; 48 | 49 | static int createNonblocking(); 50 | 51 | private: 52 | void handleRead(); 53 | }; 54 | } 55 | 56 | 57 | #endif //TINYWS_ACCEPTOR_H 58 | -------------------------------------------------------------------------------- /multiProcess2/net/Epoll.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_EPOLL_H 2 | #define TINYWS_EPOLL_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../base/noncopyable.h" 11 | #include "Timer.h" 12 | 13 | namespace tinyWS_process2 { 14 | class Channel; 15 | class EventLoop; 16 | 17 | // Epoll 的职责: 18 | // 1 管理(增删改) Channel 列表:有关心事件的 Channel 和 无关心事件的 Channel; 19 | // 2 Epoll::poll(),监听文件描述符(有关心事件的 Channel 对应的文件描述符)。 20 | // 当有事件发生时,将"活跃"的 Channel 填充到 activeChannel 中, 21 | // 供 EventLoop 处理相应的事件,即调用 Channel::handleEvent()。 22 | // 23 | // Epoll 是 EventLoop 对象的间接成员, 24 | // 只供 owner EventLoop 在 IO 线程调用,因此无须加锁。 25 | // 其生命周期与 EVentLoop 一样长。 26 | // 27 | // Epoll 采用的是 level trigger。 28 | class Epoll : noncopyable { 29 | public: 30 | using ChannelList = std::vector; // Channel 列表类型 31 | 32 | private: 33 | using EventList = std::vector; // epoll 事件列表类型 34 | using ChannelMap = std::map; // <文件描述符, Channel> 映射 35 | 36 | static const int kInitEventListSize = 16; // 事件数组(EventList)的默认大小 37 | 38 | EventLoop* loop_; 39 | int epollfd_; 40 | EventList events_; 41 | ChannelMap channels_; 42 | 43 | public: 44 | explicit Epoll(EventLoop* loop); 45 | 46 | ~Epoll(); 47 | 48 | TimeType poll(int timeoutMS, ChannelList* activeChannels); 49 | 50 | void updateChannel(Channel* channel); 51 | 52 | void removeChannel(Channel* channel); 53 | 54 | bool hasChannel(Channel* channel); 55 | 56 | private: 57 | void fillActiveChannels(int eventNums, ChannelList *activeChannels) const; 58 | 59 | void update(int operation, Channel* channel); 60 | 61 | std::string operationToString(int operation) const; 62 | }; 63 | } 64 | 65 | #endif //TINYWS_EPOLL_H 66 | -------------------------------------------------------------------------------- /multiProcess2/net/EventLoop.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_EVENTLOOP_H 2 | #define TINYWS_EVENTLOOP_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "Timer.h" 10 | #include "../base/noncopyable.h" 11 | 12 | namespace tinyWS_process2 { 13 | class Channel; 14 | class Epoll; 15 | class TimerQueue; 16 | class TimerId; 17 | 18 | class EventLoop : noncopyable { 19 | public: 20 | using BeforeEachLoopFunction = std::function; 21 | using AfterEachAcceptFunction = std::function; 22 | 23 | private: 24 | using ChannelList = std::vector; 25 | 26 | bool running_; 27 | pid_t pid_; 28 | std::unique_ptr epoll_; 29 | std::unique_ptr timerQueue_; 30 | ChannelList activeChannels_; 31 | 32 | int listenSockfd_; 33 | 34 | BeforeEachLoopFunction beforeEachLoopFunction_; 35 | AfterEachAcceptFunction afterEachAcceptFunction_; 36 | 37 | public: 38 | EventLoop(); 39 | 40 | ~EventLoop(); 41 | 42 | void loop(); 43 | 44 | void quit(); 45 | 46 | TimerId runAt(TimeType runTime, const Timer::TimerCallback& cb); 47 | 48 | TimerId runAfter(TimeType delay, const Timer::TimerCallback& cb); 49 | 50 | TimerId runEvery(TimeType interval, const Timer::TimerCallback& cb); 51 | 52 | void cancel(const TimerId& timerId); 53 | 54 | void updateChannel(Channel* channel); 55 | 56 | void removeChannel(Channel* channel); 57 | 58 | void setListenSockfd(int sockfd); 59 | 60 | void setBeforeEachLoopFunction(const BeforeEachLoopFunction& cb); 61 | 62 | void setAfterEachLoopFunction(const AfterEachAcceptFunction& cb); 63 | 64 | private: 65 | void printActiveChannels() const; // for DEBUG 66 | }; 67 | } 68 | 69 | 70 | #endif //TINYWS_EVENTLOOP_H 71 | -------------------------------------------------------------------------------- /multiProcess2/net/InternetAddress.cpp: -------------------------------------------------------------------------------- 1 | #include "InternetAddress.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace tinyWS_process2; 9 | 10 | 11 | // // Structure describing an Internet socket address. 12 | // struct sockaddr_in { 13 | // sa_family_t sin_family; /* address family: AF_INET */ 14 | // uint16_t sin_port; /* port in network byte order */ 15 | // struct in_addr sin_addr; /* internet address */ 16 | // }; 17 | 18 | // // Internet address. 19 | // typedef uint32_t in_addr_t; 20 | // struct in_addr { 21 | // in_addr_t s_addr; /* address in network byte order */ 22 | // }; 23 | 24 | InternetAddress::InternetAddress(uint16_t port) : address_{} { 25 | address_.sin_family = AF_INET; 26 | address_.sin_addr.s_addr = INADDR_ANY; 27 | address_.sin_port = htobe16(port); 28 | } 29 | 30 | InternetAddress::InternetAddress(const std::string& ip, uint16_t port) : address_{} { 31 | address_.sin_family = AF_INET; 32 | address_.sin_port = htobe16(port); 33 | if (inet_pton(AF_INET, ip.c_str(), &address_.sin_addr) <= 0) { 34 | // std::cout << "InternetAddress::InternetAddress(const std::string &ip, uint16_t port)" 35 | // << std::endl; 36 | } 37 | } 38 | 39 | InternetAddress::InternetAddress(const sockaddr_in& address) : address_(address) {} 40 | 41 | std::string InternetAddress::toIP() const { 42 | const int size = 32; 43 | char buf[size]; 44 | inet_ntop(AF_INET, &address_.sin_addr, buf, static_cast(size)); 45 | 46 | return buf; 47 | } 48 | 49 | std::string InternetAddress::toIPPort() const { 50 | const int size = 32; 51 | char buf[size]; 52 | 53 | char host[INET_ADDRSTRLEN] = "INVALID"; 54 | inet_ntop(AF_INET, &address_.sin_addr, host, static_cast(strlen(host))); 55 | 56 | uint16_t port = be16toh(address_.sin_port); 57 | snprintf(buf, size, "%s:%u", host, port); 58 | 59 | return buf; 60 | } 61 | 62 | const sockaddr_in& InternetAddress::getSockAddrInternet() const { 63 | return address_; 64 | } 65 | 66 | void InternetAddress::setSockAddrInternet(const sockaddr_in& address) { 67 | address_ = address; 68 | } 69 | 70 | uint32_t InternetAddress::ipNetEnd() const { 71 | return address_.sin_addr.s_addr; 72 | } 73 | 74 | uint16_t InternetAddress::portNetEnd() const { 75 | return address_.sin_port; 76 | } 77 | 78 | sockaddr_in InternetAddress::getLocalAddress(int sockfd) { 79 | sockaddr_in localAddress{}; 80 | socklen_t addressLen = sizeof(localAddress); 81 | if (getsockname(sockfd, reinterpret_cast(&localAddress), &addressLen) < 0) { 82 | // std::cout << "InternetAddress::getPeerAddress" << std::endl; 83 | } 84 | 85 | return localAddress; 86 | } 87 | 88 | sockaddr_in InternetAddress::getPeerAddress(int sockfd) { 89 | sockaddr_in peerAddress{}; 90 | socklen_t addressLen = sizeof(peerAddress); 91 | if (getpeername(sockfd, reinterpret_cast(&peerAddress), &addressLen) < 0) { 92 | // std::cout << "InternetAddress::getPeerAddress" << std::endl; 93 | } 94 | 95 | return peerAddress; 96 | } 97 | -------------------------------------------------------------------------------- /multiProcess2/net/InternetAddress.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_INTERNETADDRESS_H 2 | #define TINYWS_INTERNETADDRESS_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | namespace tinyWS_process2 { 9 | 10 | class InternetAddress { 11 | private: 12 | sockaddr_in address_; 13 | 14 | public: 15 | explicit InternetAddress(uint16_t port = 0); 16 | 17 | InternetAddress(const std::string& ip, uint16_t port); 18 | 19 | explicit InternetAddress(const sockaddr_in& address); 20 | 21 | std::string toIP() const; 22 | 23 | std::string toIPPort() const; 24 | 25 | const sockaddr_in& getSockAddrInternet() const; 26 | 27 | void setSockAddrInternet(const sockaddr_in &address); 28 | 29 | uint32_t ipNetEnd() const; 30 | 31 | uint16_t portNetEnd() const; 32 | 33 | static sockaddr_in getLocalAddress(int sockfd); 34 | 35 | static sockaddr_in getPeerAddress(int sockfd); 36 | }; 37 | } 38 | 39 | 40 | #endif //TINYWS_INTERNETADDRESS_H 41 | -------------------------------------------------------------------------------- /multiProcess2/net/ProcessPool.h: -------------------------------------------------------------------------------- 1 | #ifndef tinyWS_process2POOL_H 2 | #define tinyWS_process2POOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../base/Signal.h" 12 | 13 | namespace tinyWS_process2 { 14 | 15 | class EventLoop; 16 | class Socket; 17 | 18 | class ProcessPool { 19 | public: 20 | using ForkCallback = std::function; 21 | 22 | private: 23 | EventLoop* baseLoop_; // 父进程事件循环 24 | std::vector pids_; 25 | int processNum_; 26 | 27 | // std::string name_; 28 | bool running_; 29 | 30 | SignalManager signalManager_; 31 | 32 | ForkCallback forkFunction_; 33 | 34 | public: 35 | explicit ProcessPool(EventLoop* loop); 36 | 37 | explicit ProcessPool(int processNum); 38 | 39 | ~ProcessPool(); 40 | 41 | void start(); 42 | 43 | void killAll(); 44 | 45 | void setForkFunction(const ForkCallback& cb); 46 | 47 | void setParentSignalHandlers(); 48 | 49 | void setChildSignalHandlers(); 50 | 51 | pid_t createNewChildProcess(); 52 | private: 53 | void createChildProcess(int processNum); 54 | 55 | 56 | void parentStart(); 57 | 58 | void clearDeadChild(); 59 | 60 | static void parentSignalHandler(int signo); 61 | 62 | static void childSignalHandler(int signo); 63 | }; 64 | } 65 | 66 | 67 | #endif //tinyWS_process2POOL_H 68 | -------------------------------------------------------------------------------- /multiProcess2/net/Socket.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_SOCKET_H 2 | #define TINYWS_SOCKET_H 3 | 4 | #include "../base/noncopyable.h" 5 | 6 | namespace tinyWS_process2 { 7 | 8 | class InternetAddress; 9 | 10 | class Socket : noncopyable { 11 | private: 12 | int sockfd_; 13 | 14 | public: 15 | explicit Socket(int sockfd); 16 | 17 | ~Socket(); 18 | 19 | Socket(Socket&& socket) noexcept; 20 | 21 | Socket& operator=(Socket&& rhs) noexcept; 22 | 23 | int fd() const; 24 | 25 | void setNoneFd(); 26 | 27 | bool isValid() const; 28 | 29 | void bindAddress(const InternetAddress& localAddress); 30 | 31 | void listen(); 32 | 33 | int accept(InternetAddress* peerAddress); 34 | 35 | void shutdownWrite(); 36 | 37 | void setTcpNoDelay(bool on); 38 | 39 | void setReuseAddr(bool on); 40 | 41 | void setKeepAlive(bool on); 42 | 43 | int getSocketError(); 44 | }; 45 | 46 | } 47 | 48 | #endif //TINYWS_SOCKET_H 49 | -------------------------------------------------------------------------------- /multiProcess2/net/TcpServer.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TCPSERVER_H 2 | #define TINYWS_TCPSERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../base/noncopyable.h" 10 | #include "Socket.h" 11 | #include "../base/ProcessMutexLock.h" 12 | #include "TcpConnection.h" 13 | #include "Timer.h" 14 | #include "type.h" 15 | 16 | namespace tinyWS_process2 { 17 | 18 | class EventLoop; 19 | class Acceptor; 20 | class ProcessPool; 21 | class InternetAddress; 22 | class TimerId; 23 | 24 | // TcpServer 的功能:管理 Acceptor 获得的 TcpConnection。 25 | // TcpServer 是供用户直接使用的,生命周期由用户控制。 26 | // 用户只需要设置好 callback,再调用 createProccesses() 即可。 27 | // 28 | // 尽量让依赖是单向的。 29 | // TcpServer 用到 Acceptor,但 Acceptor 并不知道 TcpServer 的存在。 30 | // TcpServer 创建 TcpConnection,但 TcpConnection 并不知道 TcpServer 的存在 31 | class TcpServer { 32 | public: 33 | using ProcessInitCallback = std::function; 34 | 35 | private: 36 | using ConnectionMap = std::map; 37 | 38 | Socket socketBeforeFork_; // 用于在进程池 fork 子进程之前,保存 listen sockfd 39 | ProcessMutexLock processMutexLock_; 40 | bool isLock_; 41 | 42 | 43 | std::unique_ptr processPool_; 44 | EventLoop* loop_; 45 | std::unique_ptr acceptor_; 46 | 47 | const std::string name_; 48 | 49 | int nextConnectionId_; 50 | bool started_; 51 | ConnectionMap connectionMap_; 52 | 53 | 54 | ConnectionCallback connectionCallback_; // 连接建立的回调函数 55 | MessageCallback messageCallback_; // 消息到来的回调函数 56 | ProcessInitCallback threadInitCallback_; // 线程初始化的回调函数 57 | 58 | public: 59 | TcpServer(const InternetAddress &address, const std::string &name); 60 | 61 | ~TcpServer(); 62 | 63 | EventLoop* getLoop() const; 64 | 65 | void start(); 66 | 67 | /** 68 | * 设置连接建立的回调函数 69 | * @param cb 回调函数 70 | */ 71 | void setConnectionCallback(const ConnectionCallback &cb); 72 | 73 | /** 74 | * 设置消息到来的回调函数 75 | * @param cb 回调函数 76 | */ 77 | void setMessageCallback(const MessageCallback &cb); 78 | 79 | TimerId runAt(TimeType runTime, const Timer::TimerCallback& cb); 80 | 81 | TimerId runAfter(TimeType delay, const Timer::TimerCallback& cb); 82 | 83 | TimerId runEvery(TimeType interval, const Timer::TimerCallback& cb); 84 | 85 | private: 86 | void newConnectionInParent(Socket socket, const InternetAddress& peerAddress); 87 | 88 | void newConnectionInChild(EventLoop* loop, Socket socket); 89 | 90 | void removeConnection(const TcpConnectionPtr& connection); 91 | 92 | void reset(); 93 | 94 | void lockAcceptor(); 95 | 96 | void unlockAcceptor(); 97 | }; 98 | } 99 | 100 | 101 | #endif //TINYWS_TCPSERVER_H 102 | -------------------------------------------------------------------------------- /multiProcess2/net/Timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Timer.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace tinyWS_process2; 8 | 9 | AtomicInt64 Timer::s_numCreated_; 10 | 11 | Timer::Timer(const Timer::TimerCallback &cb, TimeType timeout, TimeType interval) 12 | : timerCallback_(cb), 13 | expiredTime_(timeout), 14 | interval_(interval), 15 | repeat_(interval_ > 0), 16 | sequence_(s_numCreated_.incrementAndGet()) { 17 | 18 | } 19 | 20 | void Timer::run() const { 21 | timerCallback_(); 22 | } 23 | 24 | TimeType Timer::getExpiredTime() { 25 | return expiredTime_; 26 | } 27 | 28 | void Timer::updateExpiredTime(TimeType timeout) { 29 | expiredTime_ = timeout; 30 | } 31 | 32 | bool Timer::repeat() const { 33 | return repeat_; 34 | } 35 | 36 | int64_t Timer::getSequence() const { 37 | return sequence_; 38 | } 39 | 40 | bool Timer::isValid() const { 41 | return expiredTime_ >= Timer::now(); 42 | } 43 | 44 | TimeType Timer::invalid() const { 45 | return 0; 46 | } 47 | 48 | void Timer::restart(TimeType now) { 49 | if (repeat_) { 50 | expiredTime_ = now + interval_; 51 | } else { 52 | expiredTime_ = invalid(); 53 | } 54 | } 55 | 56 | TimeType Timer::now() { 57 | // struct timeval { 58 | // time_t tv_sec; // seconds since Jan. 1, 1970 59 | // suseconds_t tv_usec; // and microseconds 60 | // }; 61 | std::shared_ptr tv(std::make_shared()); 62 | 63 | // SUSv4 指定 gettimeofday() 函数现已弃用。 64 | // gettimeofday() 不是系统调用,是在用户态实现的,没有上下文切换和陷入内核的开销。 65 | // 精度为1纳秒。 66 | gettimeofday(tv.get(), nullptr); 67 | 68 | return static_cast(tv->tv_sec * Timer::kMicroSecondsPerSecond + tv->tv_usec); 69 | } 70 | 71 | int64_t Timer::createNum() { 72 | return s_numCreated_.get(); 73 | } -------------------------------------------------------------------------------- /multiProcess2/net/Timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TIMER_H 2 | #define TINYWS_TIMER_H 3 | 4 | #include 5 | 6 | #include "../base/noncopyable.h" 7 | #include "type.h" 8 | #include "../base/Atomic.h" 9 | 10 | namespace tinyWS_process2 { 11 | 12 | class Timer : noncopyable { 13 | public: 14 | using TimerCallback = std::function; 15 | 16 | const static TimeType kMicroSecondsPerSecond = 1000 * 1000; // 一秒有 1000 * 1000 微秒 17 | 18 | private: 19 | const TimerCallback timerCallback_; 20 | TimeType expiredTime_; 21 | const TimeType interval_; 22 | const bool repeat_; 23 | const int64_t sequence_; 24 | 25 | static AtomicInt64 s_numCreated_; 26 | 27 | public: 28 | Timer(const TimerCallback& cb, TimeType timeout, TimeType interval = 0); 29 | 30 | void run() const; 31 | 32 | TimeType getExpiredTime(); 33 | 34 | void updateExpiredTime(TimeType timeout); 35 | 36 | bool repeat() const; 37 | 38 | int64_t getSequence() const; 39 | 40 | bool isValid() const; 41 | 42 | TimeType invalid() const; 43 | 44 | void restart(TimeType now); 45 | 46 | static TimeType now(); 47 | 48 | static int64_t createNum(); 49 | }; 50 | } 51 | 52 | 53 | #endif //TINYWS_TIMER_H 54 | -------------------------------------------------------------------------------- /multiProcess2/net/TimerId.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TIMERID_H 2 | #define TINYWS_TIMERID_H 3 | 4 | #include 5 | 6 | #include "Timer.h" 7 | 8 | namespace tinyWS_process2 { 9 | class TimerId{ 10 | public: 11 | friend class TimerQueue; 12 | 13 | private: 14 | std::weak_ptr timer_; 15 | int64_t sequence_; 16 | 17 | public: 18 | TimerId() : sequence_(0) {} 19 | 20 | explicit TimerId(const std::weak_ptr& timer) 21 | : timer_(timer), 22 | sequence_(timer.lock() ? timer.lock()->getSequence() : 0) { 23 | 24 | } 25 | }; 26 | } 27 | 28 | #endif //TINYWS_TIMERID_H 29 | -------------------------------------------------------------------------------- /multiProcess2/net/TimerQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TIMERQUEUE_H 2 | #define TINYWS_TIMERQUEUE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Channel.h" 10 | #include "../base/noncopyable.h" 11 | #include "Timer.h" 12 | 13 | namespace tinyWS_process2 { 14 | 15 | class EventLoop; 16 | class TimerId; 17 | 18 | class TimerQueue : noncopyable { 19 | private: 20 | using Entry = std::pair>; 21 | using TimerList = std::set; 22 | 23 | using ActiveTimer = std::pair, TimeType>; 24 | using ActiveTimerSet = std::set; 25 | 26 | EventLoop *loop_; 27 | 28 | const int timerfd_; 29 | Channel timerfdChannel_; 30 | TimerList timers_; 31 | 32 | ActiveTimerSet activeTimers_; 33 | bool callingExpiredTimers_; 34 | ActiveTimerSet cancelTimers_; 35 | 36 | public: 37 | explicit TimerQueue(EventLoop *loop); 38 | 39 | ~TimerQueue(); 40 | 41 | TimerId addTimer(const Timer::TimerCallback& cb, TimeType timeout, TimeType interval); 42 | 43 | void cancel(const TimerId& timerId); 44 | 45 | private: 46 | void handleRead(); 47 | 48 | std::vector getExpired(TimeType now); 49 | 50 | void reset(const std::vector& expired, TimeType now); 51 | 52 | bool insert(const std::shared_ptr& timer); 53 | }; 54 | } 55 | 56 | 57 | #endif //TINYWS_TIMERQUEUE_H 58 | -------------------------------------------------------------------------------- /multiProcess2/net/status.cpp: -------------------------------------------------------------------------------- 1 | #include "status.h" 2 | 3 | int tinyWS_process2::status_quit_softly = 0; //QUIT 4 | int tinyWS_process2::status_terminate = 0; //TERM,INT 5 | int tinyWS_process2::status_exiting = 0; 6 | int tinyWS_process2::status_restart = 0; 7 | int tinyWS_process2::status_reconfigure = 0; //HUP,reboot 8 | int tinyWS_process2::status_child_quit = 0; //CHLD 9 | -------------------------------------------------------------------------------- /multiProcess2/net/status.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_STATUS_H 2 | #define TINYWS_STATUS_H 3 | 4 | namespace tinyWS_process2 { 5 | extern int status_quit_softly; //QUIT 6 | extern int status_terminate; //TERM,INT 7 | extern int status_exiting; 8 | extern int status_restart; 9 | extern int status_reconfigure; //HUP,reboot 10 | extern int status_child_quit; //CHLD 11 | } 12 | 13 | #endif //TINYWS_STATUS_H 14 | -------------------------------------------------------------------------------- /multiProcess2/net/type.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TYPE_H 2 | #define TINYWS_TYPE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tinyWS_process2 { 9 | // 定时器中的时间类型 10 | using TimeType = int64_t; 11 | 12 | // TcpConnection 相关的类型 13 | class TcpConnection; 14 | class Buffer; 15 | 16 | // TcpConnection 对象的智能指针类型 17 | using TcpConnectionPtr = std::shared_ptr; 18 | 19 | // 连接建立的回调函数的类型 20 | using ConnectionCallback = std::function; 21 | 22 | // 消息到来的回调函数的类型 23 | using MessageCallback = std::function; 24 | 25 | // 连接断开的的回调函数的类型 26 | using CloseCallback = std::function; 27 | 28 | // 写完成的回调函数的类型 29 | using WriteCompleteCallback = std::function; 30 | } 31 | 32 | #endif //TINYWS_TYPE_H 33 | -------------------------------------------------------------------------------- /multiProcess2/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiProcess2/web/favicon.ico -------------------------------------------------------------------------------- /multiProcess2/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tinyWS 5 | 6 | 7 | 8 | 9 | Hello 10 | 11 | 12 |

这是我的HTML

13 |

Hello World!Chen Shuaihao's NetServer

14 | 15 | 16 | -------------------------------------------------------------------------------- /multiThread/README.md: -------------------------------------------------------------------------------- 1 | # tinyWS 2 | A C++ Tiny Web Server 3 | 4 | ## 已完成 5 | 6 | - 完成基本的Tcp(被动连接)库; 7 | - 简易的HTTP服务器,可访问主页HTML和网站favicon图标。 8 | 9 | ## 技术 10 | 11 | - 主从 Reactor 模式: 12 | - 主 Reactor 负责监听连接,当有新的连接,accept 到新的 socket 后,使用 Round Robin 方法选择从 Reactor,将 socket 派发给从 Reactor; 13 | - 从 Reactor 负责管理时间描述符(timerfd用于定时任务)、事件描述符(eventfd 用于唤醒 IO 线程)和 派发过来的 socket 文件描述符。 14 | - multiple Reactors + thread pool (one loop per thread + thread pool); 15 | - EventLoop:使用 Epoll 水平触发的模式结合非阻塞 IO; 16 | - 线程池: 17 | - 使用多线程能发挥多核的优势; 18 | - 线程池可以避免线程的频繁地创建和销毁的开销。 19 | - 双缓冲异步日志系统; 20 | - 基于最小堆的定时器; 21 | - 使用智能指针等 RAII 机制,降低内存泄漏的可能性; 22 | 23 | ## 并发模型 24 | 25 | 并发模型为 multiple reactors + thread pool (one loop per thread + thread pool); + 非阻塞 IO,新连接使用 Round Robin 策略派发。 26 | 27 | ![并发模型](doc/model.png) 28 | 29 | ## 压测 30 | 31 | [压测结果](doc/pressure_test.md)。 32 | 33 | ## TODO 34 | 35 | - 编写文档,解释核心原理 36 | 37 | ## 参考 38 | 39 | - [陈硕老师的Blog](http://www.cppblog.com/solstice/) 40 | - [linyacool](https://github.com/linyacool)的[WebServer](https://github.com/linyacool/WebServer) 41 | - [uv-cpp](https://github.com/wlgq2/uv-cpp) 42 | - [开源HTTP解析器](https://www.cnblogs.com/arnoldlu/p/6497837.html) 43 | - [HTTP请求和响应格式](https://www.cnblogs.com/yaozhongxiao/archive/2013/03/02/2940252.html) 44 | - [写一个并发http服务器](https://zhuanlan.zhihu.com/p/23336565)(有代码) 45 | - [有什么适合提高 C/C++ 网络编程能力的开源项目推荐? - Variation的回答 - 知乎](https://www.zhihu.com/question/20124494/answer/733016078)(master/worker、proactor 等 IO 模型) 46 | - [Reactor事件驱动的两种设计实现:面向对象 VS 函数式编程](http://www.cnblogs.com/me115/p/5088914.html) 47 | - [网络编程中的关键问题总结](https://www.cnblogs.com/me115/p/5092091.html) 48 | - [muduo源码剖析 - cyhone的文章 - 知乎](https://zhuanlan.zhihu.com/p/85101271) 49 | - [Muduo 源码分析](https://youjiali1995.github.io/network/muduo/) -------------------------------------------------------------------------------- /multiThread/base/AsyncLogger.cpp: -------------------------------------------------------------------------------- 1 | #include "AsyncLogger.h" 2 | 3 | #include 4 | #include 5 | //#include 6 | #include 7 | 8 | #include "AsyncLogging.h" 9 | 10 | using namespace tinyWS_thread; 11 | 12 | static pthread_once_t ponce = PTHREAD_ONCE_INIT; 13 | static AsyncLogging *asyncLoggingPtr; 14 | 15 | void once_init() { 16 | asyncLoggingPtr = new AsyncLogging(AsyncLogger::getLogFilename()); 17 | asyncLoggingPtr->start(); 18 | } 19 | 20 | void output(const char* message, int len) { 21 | pthread_once(&ponce, once_init); 22 | asyncLoggingPtr->append(message, len); 23 | } 24 | 25 | AsyncLogger::Impl::Impl(const char* filename, int line) 26 | : stream_(), 27 | line_(line), 28 | basename_(filename) { 29 | formatTime(); 30 | } 31 | 32 | void AsyncLogger::Impl::formatTime() { 33 | struct timeval tv; 34 | time_t time; 35 | char str_t[26] = {0}; 36 | ::gettimeofday(&tv, nullptr); 37 | time = tv.tv_sec; 38 | struct tm* p_time = ::localtime(&time); 39 | strftime(str_t, 26, "%Y-%m-%d %H:%M:%S\n", p_time); 40 | stream_ << str_t; 41 | } 42 | 43 | std::string AsyncLogger::logFilename_; 44 | 45 | AsyncLogger::AsyncLogger(const char* filename, int line) 46 | : impl_(filename, line) { 47 | 48 | } 49 | 50 | AsyncLogger::~AsyncLogger() { 51 | impl_.stream_ << " -- " << impl_.basename_ << ':' << impl_.line_ << '\n'; 52 | const LogStream::Buffer& buffer(stream().buffer()); 53 | output(buffer.data(), buffer.length()); 54 | } 55 | 56 | LogStream& AsyncLogger::stream() { 57 | return impl_.stream_; 58 | } 59 | 60 | void AsyncLogger::setLogFilename(std::string filename) { 61 | AsyncLogger::logFilename_ = filename; 62 | } 63 | 64 | std::string AsyncLogger::getLogFilename() { 65 | return AsyncLogger::logFilename_; 66 | } 67 | -------------------------------------------------------------------------------- /multiThread/base/AsyncLogger.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_ASYNCLOGGER_H 2 | #define TINYWS_ASYNCLOGGER_H 3 | 4 | #include 5 | 6 | #include "LogStream.h" 7 | 8 | namespace tinyWS_thread { 9 | 10 | class AsyncLogger { 11 | private: 12 | class Impl { 13 | public: 14 | LogStream stream_; 15 | int line_; 16 | std::string basename_; 17 | 18 | public: 19 | Impl(const char* filename, int line); 20 | 21 | void formatTime(); 22 | }; 23 | 24 | Impl impl_; 25 | static std::string logFilename_; 26 | 27 | public: 28 | AsyncLogger(const char* filename, int line); 29 | 30 | ~AsyncLogger(); 31 | 32 | LogStream& stream(); 33 | 34 | static void setLogFilename(std::string filename); 35 | 36 | static std::string getLogFilename(); 37 | }; 38 | 39 | #define LOG AsyncLogger(__FILE__, __LINE__).stream() 40 | } 41 | 42 | #endif //TINYWS_ASYNCLOGGER_H 43 | -------------------------------------------------------------------------------- /multiThread/base/AsyncLogging.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_ASYNCLOGGING_H 2 | #define TINYWS_ASYNCLOGGING_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "noncopyable.h" 9 | #include "LogStream.h" 10 | #include "Thread.h" 11 | #include "MutexLock.h" 12 | #include "Condition.h" 13 | #include "CountDownLatch.h" 14 | 15 | namespace tinyWS_thread { 16 | 17 | class AsyncLogging : noncopyable { 18 | private: 19 | using Buffer = FixedBuffer; 20 | using BufferPtr = std::shared_ptr; 21 | using BufferVector = std::vector; 22 | 23 | const int flushInterval_; 24 | bool running_; 25 | std::string basename_; 26 | Thread thread_; 27 | MutexLock mutex_; 28 | Condition condition_; 29 | BufferPtr currentBuffer_; 30 | BufferPtr nextBuffer_; 31 | BufferVector buffers_; 32 | CountDownLatch latch_; 33 | 34 | public: 35 | AsyncLogging(const std::string& basename, int flushInterval = 2); 36 | 37 | ~AsyncLogging(); 38 | 39 | void append(const char* logline, int len); 40 | 41 | void start(); 42 | 43 | void stop(); 44 | 45 | private: 46 | void threadFunction(); 47 | }; 48 | } 49 | 50 | #endif //TINYWS_ASYNCLOGGING_H 51 | -------------------------------------------------------------------------------- /multiThread/base/Atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_ATOMIC_H 2 | #define TINYWS_ATOMIC_H 3 | 4 | #include 5 | 6 | #include "noncopyable.h" 7 | 8 | namespace tinyWS_thread { 9 | template 10 | class AtomicIntegerT : noncopyable { 11 | public: 12 | AtomicIntegerT() : value_(0) {} 13 | 14 | T get() { 15 | return __atomic_load_n(&value_, __ATOMIC_SEQ_CST); 16 | } 17 | 18 | T getAndAdd(T x) { 19 | return __atomic_fetch_add(&value_, x, __ATOMIC_SEQ_CST); 20 | } 21 | 22 | T addAndGet(T x) { 23 | return getAndAdd(x) + x; 24 | } 25 | 26 | T incrementAndGet() { 27 | return addAndGet(1); 28 | } 29 | 30 | T decrementAndGet() { 31 | return addAndGet(-1); 32 | } 33 | 34 | void add(T x) { 35 | getAndAdd(x); 36 | } 37 | 38 | void increment() { 39 | incrementAndGet(); 40 | } 41 | 42 | void decrement() { 43 | decrementAndGet(); 44 | } 45 | 46 | T getAndSet(T newValue) { 47 | return __atomic_exchange_n(&value_, newValue, __ATOMIC_SEQ_CST); 48 | } 49 | private: 50 | // volatile 关键字,参考: 51 | // https://zh.cppreference.com/w/cpp/language/cv 52 | // https://liam.page/2018/01/18/volatile-in-C-and-Cpp/ 53 | volatile T value_; 54 | }; 55 | 56 | using AtomicInt32 = AtomicIntegerT; 57 | using AtomicInt64 = AtomicIntegerT; 58 | } 59 | 60 | 61 | #endif //TINYWS_ATOMIC_H 62 | -------------------------------------------------------------------------------- /multiThread/base/BlockingQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_BLOCKINGQUEUE_H 2 | #define TINYWS_BLOCKINGQUEUE_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "noncopyable.h" 9 | #include "MutexLock.h" 10 | #include "Condition.h" 11 | 12 | namespace tinyWS_thread { 13 | 14 | template 15 | class BlockingQueue : noncopyable { 16 | public: 17 | BlockingQueue() : mutex_(), notEmpty_(mutex_), queue_() {} 18 | 19 | void put(const T &value) { 20 | MutexLockGuard lock(mutex_); 21 | queue_.push_back(value); 22 | notEmpty_.notify(); 23 | } 24 | 25 | void put(T &&value) { 26 | MutexLockGuard lock(mutex_); 27 | queue_.push_back(std::move(value)); 28 | notEmpty_.notify(); 29 | } 30 | 31 | T take() { 32 | MutexLockGuard lock(mutex_); 33 | while (queue_.empty()) { 34 | notEmpty_.wait(); 35 | } 36 | assert(!queue_.empty()); 37 | 38 | T front(std::move(queue_.front())); 39 | queue_.pop_front(); 40 | 41 | return front; 42 | } 43 | 44 | size_t size() const { 45 | MutexLockGuard lock(mutex_); 46 | return queue_.size(); 47 | } 48 | 49 | bool empty() const { 50 | return size() == 0; 51 | } 52 | 53 | private: 54 | mutable MutexLock mutex_; 55 | Condition notEmpty_; 56 | std::deque queue_; 57 | }; 58 | } 59 | 60 | 61 | #endif //TINYWS_BLOCKINGQUEUE_H 62 | -------------------------------------------------------------------------------- /multiThread/base/Condition.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_CONDITION_H 2 | #define TINYWS_CONDITION_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "MutexLock.h" 9 | #include "noncopyable.h" 10 | 11 | namespace tinyWS_thread { 12 | // 条件变量对象 13 | class Condition : noncopyable { 14 | public: 15 | explicit Condition(MutexLock &mutex) 16 | : mutex_(mutex), 17 | cond_{} { 18 | assert(pthread_cond_init(&cond_, nullptr) == 0); 19 | } 20 | 21 | ~Condition() { 22 | assert(pthread_cond_destroy(&cond_) == 0); 23 | } 24 | 25 | void wait() { 26 | pthread_cond_wait(&cond_, mutex_.getPthreadMutexPtr()); 27 | } 28 | 29 | /** 30 | * 等待规定时间 31 | * @param second 等待的时间 32 | * @return 如果超时,则返回true;否则,返回false 33 | */ 34 | bool waitForSecond(int second) { 35 | struct timespec timeout{}; 36 | clock_getres(CLOCK_REALTIME, &timeout); 37 | timeout.tv_sec += second; 38 | return pthread_cond_timedwait(&cond_, mutex_.getPthreadMutexPtr(), &timeout) == ETIMEDOUT; 39 | } 40 | 41 | void notify() { 42 | pthread_cond_signal(&cond_); 43 | } 44 | 45 | void notifyAll() { 46 | pthread_cond_broadcast(&cond_); 47 | } 48 | 49 | private: 50 | MutexLock &mutex_; 51 | pthread_cond_t cond_; 52 | }; 53 | } 54 | 55 | 56 | #endif //TINYWS_CONDITION_H 57 | -------------------------------------------------------------------------------- /multiThread/base/CountDownLatch.cpp: -------------------------------------------------------------------------------- 1 | #include "CountDownLatch.h" 2 | 3 | using namespace tinyWS_thread; 4 | 5 | CountDownLatch::CountDownLatch(int count) 6 | : mutex_(), 7 | condition_(mutex_), 8 | count_(count) { 9 | 10 | } 11 | 12 | void CountDownLatch::wait() { 13 | MutexLockGuard lock(mutex_); 14 | while (count_ > 0) { 15 | condition_.wait(); 16 | } 17 | } 18 | 19 | void CountDownLatch::countDown() { 20 | MutexLockGuard lock(mutex_); 21 | --count_; 22 | if (count_ == 0) { 23 | condition_.notifyAll(); 24 | } 25 | } 26 | 27 | int CountDownLatch::getCount() const { 28 | return count_; 29 | } 30 | -------------------------------------------------------------------------------- /multiThread/base/CountDownLatch.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_COUNTDOWNLATCH_H 2 | #define TINYWS_COUNTDOWNLATCH_H 3 | 4 | #include "noncopyable.h" 5 | #include "MutexLock.h" 6 | #include "Condition.h" 7 | 8 | namespace tinyWS_thread { 9 | 10 | class CountDownLatch : noncopyable { 11 | private: 12 | mutable MutexLock mutex_; 13 | Condition condition_; 14 | int count_; 15 | 16 | public: 17 | explicit CountDownLatch(int count); 18 | 19 | void wait(); 20 | 21 | void countDown(); 22 | 23 | int getCount() const; 24 | }; 25 | } 26 | 27 | #endif //TINYWS_COUNTDOWNLATCH_H 28 | -------------------------------------------------------------------------------- /multiThread/base/Exception.cpp: -------------------------------------------------------------------------------- 1 | #include "Exception.h" 2 | 3 | #include 4 | #include // The header provides an interface to the C++ ABI. 5 | #include 6 | 7 | using namespace tinyWS_thread; 8 | 9 | Exception::Exception(const char* what) : message_(what) { 10 | const int length = 200; 11 | void* buffer[length]; 12 | int nptrs = ::backtrace(buffer, length); 13 | char** strings = ::backtrace_symbols(buffer, nptrs); 14 | if (strings) { 15 | for (int i = 0; i < nptrs; ++i) { 16 | stack_.append(strings[i]); 17 | stack_.push_back('\n'); 18 | } 19 | ::free(strings); 20 | } 21 | } 22 | 23 | 24 | const char* Exception::what() const throw() { 25 | return message_.c_str(); 26 | } 27 | 28 | const char * Exception::stackTrace() const throw() { 29 | return stack_.c_str(); 30 | } 31 | -------------------------------------------------------------------------------- /multiThread/base/Exception.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_EXCEPTION_H 2 | #define TINYWS_EXCEPTION_H 3 | 4 | #include 5 | #include 6 | 7 | namespace tinyWS_thread { 8 | 9 | class Exception : public std::exception { 10 | private: 11 | std::string message_; 12 | std::string stack_; 13 | 14 | public: 15 | explicit Exception(const char* what); 16 | 17 | ~Exception() noexcept override = default; 18 | 19 | virtual const char* what() const throw(); 20 | 21 | const char* stackTrace() const throw(); 22 | }; 23 | } 24 | 25 | #endif //TINYWS_EXCEPTION_H 26 | -------------------------------------------------------------------------------- /multiThread/base/FileUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "FileUtil.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace tinyWS_thread; 7 | 8 | FileUtil::FileUtil(std::string filename) 9 | : fp_(fopen(filename.c_str(), "ae")) { 10 | setbuffer(fp_, buffer_, sizeof(buffer_)); 11 | } 12 | 13 | FileUtil::~FileUtil() { 14 | fclose(fp_); 15 | } 16 | 17 | void FileUtil::append(const char* logline, const size_t len) { 18 | size_t n = this->write(logline, len); 19 | size_t remain = len - n; 20 | while (remain > 0) { 21 | size_t tmp = this->write(logline + n, remain); 22 | if (tmp == 0) { 23 | int err = ::ferror(fp_); 24 | if (err) { 25 | fprintf(stderr, "FileUtil::append() failed !\n"); 26 | } 27 | break; 28 | } 29 | remain -= tmp; 30 | } 31 | } 32 | 33 | void FileUtil::flush() { 34 | ::fflush(fp_); 35 | } 36 | 37 | size_t FileUtil::write(const char* logline, size_t len) { 38 | return ::fwrite_unlocked(logline, 1, len, fp_); 39 | } 40 | -------------------------------------------------------------------------------- /multiThread/base/FileUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_FILEUTIL_H 2 | #define TINYWS_FILEUTIL_H 3 | 4 | #include 5 | 6 | #include "noncopyable.h" 7 | 8 | namespace tinyWS_thread { 9 | 10 | class FileUtil : noncopyable { 11 | public: 12 | static const int kBufferSize = 65536; 13 | 14 | public: 15 | explicit FileUtil(std::string filename); 16 | 17 | ~FileUtil(); 18 | 19 | void append(const char* logline, const size_t len); 20 | 21 | void flush(); 22 | 23 | private: 24 | size_t write(const char* logline, size_t len); 25 | 26 | FILE* fp_; 27 | char buffer_[kBufferSize]; 28 | }; 29 | } 30 | 31 | #endif //TINYWS_FILEUTIL_H 32 | -------------------------------------------------------------------------------- /multiThread/base/LogFile.cpp: -------------------------------------------------------------------------------- 1 | #include "LogFile.h" 2 | 3 | using namespace tinyWS_thread; 4 | 5 | LogFile::LogFile(const std::string& basename, int flushEveryN) 6 | : basename_(basename), 7 | flushEveryN_(flushEveryN), 8 | count_(0), 9 | mutex_(new MutexLock), 10 | file_(new FileUtil(basename)) { 11 | 12 | } 13 | 14 | void LogFile::append(const char* logline, int len) { 15 | MutexLockGuard lock(*mutex_); 16 | append_unlocked(logline, len); 17 | } 18 | 19 | void LogFile::flush() { 20 | MutexLockGuard lock(*mutex_); 21 | file_->flush(); 22 | } 23 | 24 | void LogFile::append_unlocked(const char* logline, int len) { 25 | file_->append(logline, len); 26 | ++count_; 27 | if (count_ >= flushEveryN_) { 28 | count_ = 0; 29 | file_->flush(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /multiThread/base/LogFile.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_LOGFILE_H 2 | #define TINYWS_LOGFILE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "noncopyable.h" 8 | #include "MutexLock.h" 9 | #include "FileUtil.h" 10 | 11 | namespace tinyWS_thread { 12 | 13 | class LogFile : noncopyable { 14 | private: 15 | const std::string basename_; 16 | const int flushEveryN_; 17 | 18 | int count_; 19 | std::unique_ptr mutex_; 20 | std::unique_ptr file_; 21 | 22 | public: 23 | LogFile(const std::string& basename, int flushEveryN = 1024); 24 | 25 | ~LogFile() = default; 26 | 27 | void append(const char* logline, int len); 28 | 29 | void flush(); 30 | 31 | void rollFile(); 32 | 33 | private: 34 | void append_unlocked(const char* logline, int len); 35 | }; 36 | } 37 | 38 | #endif //TINYWS_LOGFILE_H 39 | -------------------------------------------------------------------------------- /multiThread/base/LogStream.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_LOGSTREAM_H 2 | #define TINYWS_LOGSTREAM_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "noncopyable.h" 9 | 10 | namespace tinyWS_thread { 11 | const int kSmallBuffer = 4000; 12 | const int kLargeBuffer = 4000 * 1000; 13 | 14 | template 15 | class FixedBuffer : noncopyable { 16 | private: 17 | char data_[SIZE]; 18 | char* cur_; 19 | 20 | public: 21 | FixedBuffer() : cur_(data_) {} 22 | 23 | ~FixedBuffer() = default; 24 | 25 | void append(const char* buffer, size_t len) { 26 | 27 | } 28 | 29 | const char* data() const { 30 | return data_; 31 | } 32 | 33 | int length() const { 34 | return static_cast(cur_ - data_); 35 | } 36 | 37 | char* current() { 38 | return cur_; 39 | } 40 | 41 | int avail() const { 42 | return static_cast(end() - cur_); 43 | } 44 | 45 | void add(size_t len) { 46 | cur_ += len; 47 | } 48 | 49 | void reset() { 50 | cur_ = data_; 51 | } 52 | 53 | void bzero() { 54 | ::memset(data_, 0, sizeof(data_)); 55 | } 56 | 57 | private: 58 | const char* end() const { 59 | return data_ + sizeof(data_); 60 | } 61 | }; 62 | 63 | class LogStream : noncopyable { 64 | public: 65 | using Buffer = FixedBuffer; 66 | 67 | private: 68 | Buffer buffer_; 69 | 70 | static const int kMaxNumericSize; 71 | 72 | public: 73 | LogStream& operator<<(bool flag); 74 | LogStream& operator<<(short); 75 | LogStream& operator<<(unsigned short); 76 | LogStream& operator<<(int); 77 | LogStream& operator<<(unsigned int); 78 | LogStream& operator<<(long); 79 | LogStream& operator<<(unsigned long); 80 | LogStream& operator<<(long long); 81 | LogStream& operator<<(unsigned long long); 82 | LogStream& operator<<(const void*); 83 | LogStream& operator<<(float n); 84 | LogStream& operator<<(double); 85 | LogStream& operator<<(long double); 86 | LogStream& operator<<(char c); 87 | LogStream& operator<<(char* str); 88 | LogStream& operator<<(const char* str); 89 | LogStream& operator<<(const unsigned char* str); 90 | LogStream& operator<<(const std::string& v); 91 | 92 | void append(const char* data, int len); 93 | 94 | const Buffer& buffer() const; 95 | 96 | void resetBuffer(); 97 | 98 | private: 99 | void staticCheck(); 100 | 101 | template 102 | void formatInteger(T data); 103 | }; 104 | 105 | template 106 | inline LogStream& operator<<(LogStream& s, T data); 107 | } 108 | 109 | #endif //TINYWS_LOGSTREAM_H 110 | -------------------------------------------------------------------------------- /multiThread/base/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace tinyWS_thread; 12 | 13 | ConsoleLogger tinyWS_thread::debug; 14 | //FileLogger tinyWS::record("test.log"); 15 | 16 | static const std::map LogLevelStr = 17 | { 18 | {LogLevel::TRACE, "TRACE"}, 19 | {LogLevel::DEBUG, "DEBUG"}, 20 | {LogLevel ::INFO, "INFO"}, 21 | {LogLevel ::WARN, "WARN"}, 22 | {LogLevel::ERROR, "ERROR"}, 23 | {LogLevel::FATAL, "FATAL"} 24 | }; 25 | 26 | std::ostream& operator<<(std::ostream &stream, const tm *tmStr) { 27 | return stream << 1900 + tmStr->tm_year << '-' 28 | << std::setfill('0') << std::setw(2) << tmStr->tm_mon + 1 << '-' 29 | << std::setfill('0') << std::setw(2) << tmStr->tm_mday << ' ' 30 | << std::setfill('0') << std::setw(2) << tmStr->tm_hour << ':' 31 | << std::setfill('0') << std::setw(2) << tmStr->tm_min << ':' 32 | << std::setfill('0') << std::setw(2) << tmStr->tm_sec; 33 | 34 | } 35 | 36 | BaseLogger::LogStream BaseLogger::operator()(LogLevel level) { 37 | return LogStream(*this, level); 38 | } 39 | 40 | const tm* BaseLogger::getLocalTime() { 41 | auto now = std::chrono::system_clock::now(); 42 | auto in_time_t = std::chrono::system_clock::to_time_t(now); 43 | localtime_r(reinterpret_cast(&in_time_t), &localtime_); 44 | 45 | return &localtime_; 46 | } 47 | 48 | void BaseLogger::endine(LogLevel level, const std::string &message) { 49 | MutexLockGuard lock(mutex_); 50 | output(getLocalTime(), LogLevelStr.find(level)->second, message); 51 | } 52 | 53 | BaseLogger::LogStream::LogStream(BaseLogger &logger, tinyWS_thread::LogLevel level) 54 | : logger_(logger), 55 | level_(level) { 56 | 57 | } 58 | 59 | BaseLogger::LogStream::LogStream(const BaseLogger::LogStream &other) 60 | : logger_(other.logger_), 61 | level_(other.level_) { 62 | 63 | } 64 | 65 | BaseLogger::LogStream::~LogStream() { 66 | // 在析构的时候,打印日志 67 | logger_.endine(level_, static_cast(std::move(str()))); 68 | } 69 | 70 | void ConsoleLogger::output(const tm *tmPtr, const std::string &levelStr, const std::string &messageStr) { 71 | std::cout << '[' << tmPtr << ']' 72 | << '[' << levelStr << ']' 73 | << '\t' << messageStr << std::endl; 74 | } 75 | 76 | FileLogger::FileLogger(std::string filename) noexcept : BaseLogger() { 77 | std::string validFilename(filename.size(), '\0'); 78 | std::regex express(R"(/|:| |>|<|\"|\\*|\\?|\\|)"); 79 | std::regex_replace(validFilename.begin(), filename.begin(), filename.end(), express, "-"); 80 | file_.open(validFilename, std::fstream::out | std::fstream::app | std::fstream::ate); 81 | 82 | assert(!file_.fail()); 83 | } 84 | 85 | FileLogger::~FileLogger() { 86 | file_.flush(); 87 | file_.close(); 88 | } 89 | 90 | void FileLogger::output(const tm *tmPtr, const std::string &levelStr, const std::string &messageStr) { 91 | file_ << '[' << tmPtr << ']' 92 | << '[' << levelStr << ']' 93 | << '\t' << messageStr << std::endl; 94 | file_.flush(); 95 | } -------------------------------------------------------------------------------- /multiThread/base/Logger.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_LOGGER_H 2 | #define TINYWS_LOGGER_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "MutexLock.h" 11 | #include "noncopyable.h" 12 | 13 | namespace tinyWS_thread { 14 | // 参考 https://blog.csdn.net/u014755412/article/details/79334572 15 | 16 | // 日志等级 17 | // 使用枚举类来避免污染命名空间。 18 | enum class LogLevel { 19 | TRACE, 20 | DEBUG, 21 | INFO, 22 | WARN, 23 | ERROR, 24 | FATAL 25 | }; 26 | 27 | // 纯虚日志基类 28 | class BaseLogger { 29 | class LogStream; // 用于文本缓冲的内部类声明 30 | public: 31 | 32 | BaseLogger() = default; 33 | 34 | virtual ~BaseLogger() = default; 35 | 36 | /** 37 | * 重载 operator(),使日志实例变为可调用。 38 | * @param level 日志等级 39 | * @return 缓冲区对象 40 | */ 41 | virtual LogStream operator()(LogLevel level = LogLevel::DEBUG); 42 | 43 | private: 44 | MutexLock mutex_; 45 | tm localtime_{}; 46 | 47 | /** 48 | * 获取当前本地时间 49 | * @return 本地时间 50 | */ 51 | const tm* getLocalTime(); 52 | 53 | /** 54 | * --- 线程安全 --- 55 | * @param level 日志等级 56 | * @param message 打印的信息 57 | */ 58 | void endine(LogLevel level, const std::string &message); 59 | 60 | /** 61 | * 纯虚函数,打印日志。 62 | * @param tmPtr 本地时间 63 | * @param levelStr 日志等级 64 | * @param messageStr 打印的信息 65 | */ 66 | virtual void output(const tm *tmPtr, const std::string &levelStr, const std::string &messageStr) = 0; 67 | }; 68 | 69 | class BaseLogger::LogStream : public std::ostringstream { 70 | public: 71 | LogStream(BaseLogger &logger, LogLevel level); 72 | LogStream(const LogStream &other); 73 | ~LogStream() override; 74 | private: 75 | BaseLogger &logger_; 76 | LogLevel level_; 77 | }; 78 | 79 | // 控制台日志类 80 | class ConsoleLogger : public BaseLogger { 81 | using BaseLogger::BaseLogger; 82 | void output(const tm *tmPtr, const std::string &levelStr, const std::string &messageStr) override; 83 | }; 84 | 85 | // 文档日志类 86 | class FileLogger 87 | : public BaseLogger, 88 | private noncopyable { 89 | public: 90 | explicit FileLogger(std::string filename) noexcept; 91 | 92 | ~FileLogger() override; 93 | private: 94 | std::ofstream file_; 95 | 96 | void output(const tm *tmPtr, const std::string &levelStr, const std::string &messageStr) override; 97 | }; 98 | 99 | // 全局实例 100 | extern ConsoleLogger debug; 101 | // extern FileLogger record; 102 | } 103 | 104 | 105 | #endif //TINYWS_LOGGER_H 106 | -------------------------------------------------------------------------------- /multiThread/base/MutexLock.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_MUTEXLOCK_H 2 | #define TINYWS_MUTEXLOCK_H 3 | 4 | #include 5 | #include 6 | 7 | #include "Thread.h" 8 | #include "noncopyable.h" 9 | 10 | namespace tinyWS_thread { 11 | // 这段代码没有达到工业强度,详见 《Linux多线程服务端编程》P46 12 | class MutexLock : noncopyable { 13 | private: 14 | pthread_mutex_t mutex_; // 互斥量 15 | pid_t holder_; // 持有锁的线程的 ID 16 | 17 | public: 18 | MutexLock() : mutex_{}, holder_(0) { 19 | assert(pthread_mutex_init(&mutex_, nullptr) == 0); 20 | } 21 | 22 | ~MutexLock() { 23 | assert(holder_ == 0); 24 | assert(pthread_mutex_destroy(&mutex_) == 0); 25 | } 26 | 27 | /** 28 | * 当前线程是否为加锁线程 29 | * @return true / false 30 | */ 31 | bool isLockedByThisThread() { 32 | return holder_ == Thread::gettid(); 33 | } 34 | 35 | /** 36 | * 断言,当前线程是否为加锁线程 37 | */ 38 | void assertLocked() { 39 | assert(isLockedByThisThread()); 40 | } 41 | 42 | /** 43 | * 加锁(仅供 MutexLockGuard 调用,严禁用户调用) 44 | */ 45 | void lock() { 46 | pthread_mutex_lock(&mutex_); // 必须先加锁,才能修改holder_ 47 | holder_ = Thread::gettid(); 48 | } 49 | 50 | /** 51 | * 释放锁(仅供 MutexLockGuard 调用,严禁用户调用) 52 | */ 53 | void unlock() { 54 | holder_ = 0; 55 | pthread_mutex_unlock(&mutex_); 56 | } 57 | 58 | /** 59 | * 获取互斥量原始指针(仅供 Condition 调用,严禁用户调用) 60 | * 仅供 Condition 调用 61 | * @return 互斥量原始指针 62 | */ 63 | pthread_mutex_t* getPthreadMutexPtr() { 64 | return &mutex_; 65 | } 66 | }; 67 | 68 | // 自动维护互斥量加锁和解锁: 69 | // 1 创建对象的时候,加锁; 70 | // 2 离开作用域,销毁对象时,解锁 71 | class MutexLockGuard : noncopyable { 72 | private: 73 | MutexLock &mutex_; 74 | public: 75 | explicit MutexLockGuard(MutexLock &mutex) : mutex_(mutex) { 76 | mutex_.lock(); 77 | } 78 | 79 | ~MutexLockGuard() { 80 | mutex_.unlock(); 81 | } 82 | }; 83 | 84 | // 防止类似误用:MutexLockGuard(mutex_) 85 | // 临时对象不能长时间持有锁,一产生对象又马上被销毁! 86 | // 正确写法:MutexLockGuard lock(mutex) 87 | #define MutexLockGuard(x) error "Missing guard object name" 88 | 89 | } 90 | 91 | #endif //TINYWS_MUTEXLOCK_H 92 | -------------------------------------------------------------------------------- /multiThread/base/ObjectPool.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_OBJECTPOOL_H 2 | #define TINYWS_OBJECTPOOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace tinyWS_thread { 10 | 11 | // 借助 C++ 智能指针的自定义删除器, 12 | // 在智能指针释放的时候会调用删除器, 13 | // 在删除器中将用完的对象重新放回对象池。 14 | // 参考 15 | // https://www.cnblogs.com/qicosmos/p/4995248.html 16 | // https://www.cnblogs.com/qicosmos/p/3673723.html 17 | // https://zhuanlan.zhihu.com/p/73066435 18 | 19 | // unique_ptr 版本的对象池 20 | template 21 | class ObjectPool_unique { 22 | public: 23 | using DeleterType = std::function; 24 | using ObjectType = std::unique_ptr; 25 | 26 | private: 27 | std::vector> pool_; 28 | 29 | public: 30 | void add(std::unique_ptr obj) { 31 | pool_.emplace_back(std::move(obj)); 32 | } 33 | 34 | ObjectType get() { 35 | if (pool_.empty()) { 36 | throw std::logic_error("no more object"); 37 | } 38 | 39 | // 释放原生指针,并将其返回给用户 40 | ObjectType obj(pool_.back().release(), [this](T* t) { 41 | // 将对象放回对象池 42 | this->pool_.emplace_back(std::unique_ptr(t)); 43 | }); 44 | 45 | pool_.pop_back(); 46 | return std::move(obj); 47 | } 48 | 49 | bool empty() const { 50 | return pool_.empty(); 51 | } 52 | 53 | size_t size() const { 54 | return pool_.size(); 55 | } 56 | }; 57 | 58 | 59 | // shared_ptr 版本的对象池 60 | template 61 | class ObjectPool_shared { 62 | public: 63 | using DeleterType = std::function; 64 | using ObjectType = std::shared_ptr; 65 | 66 | private: 67 | std::vector> pool_; 68 | 69 | public: 70 | void add(std::unique_ptr obj) { 71 | pool_.emplace_back(std::move(obj)); 72 | } 73 | 74 | ObjectType get() { 75 | if (pool_.empty()) { 76 | throw std::logic_error("no more object"); 77 | } 78 | 79 | 80 | ObjectType obj = pool_.back(); 81 | // 需要拷贝构造 T 对象 82 | ObjectType newObj = ObjectType(new T(*obj.get()), [this](T* t) { 83 | // 将对象放回对象池 84 | pool_.emplace_back(std::shared_ptr(t)); 85 | }); 86 | 87 | pool_.pop_back(); 88 | return std::move(obj); 89 | } 90 | 91 | bool empty() const { 92 | return pool_.empty(); 93 | } 94 | 95 | size_t size() const { 96 | return pool_.size(); 97 | } 98 | }; 99 | } 100 | 101 | #endif //TINYWS_OBJECTPOOL_H 102 | -------------------------------------------------------------------------------- /multiThread/base/Singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_SINGLETON_H 2 | #define TINYWS_SINGLETON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "noncopyable.h" 10 | 11 | namespace tinyWS_thread { 12 | 13 | // 参考 14 | // https://segmentfault.com/q/1010000000593968 15 | // https://www.cnblogs.com/loveis715/archive/2012/07/18/2598409.html 16 | // https://blog.csdn.net/lsaejn/article/details/78409001 17 | // http://kaiyuan.me/2018/05/08/sfinae/ 18 | // https://www.jianshu.com/p/45a2410d4085 19 | // https://zhuanlan.zhihu.com/p/21314708 20 | // http://kaiyuan.me/2018/05/08/sfinae/ 21 | 22 | /** 23 | * 判断 T 类型是否有 no_destroy 方法。 24 | * 如果有,则 value == true; 25 | * 否则,value == false。 26 | * 27 | * @tparam T 28 | */ 29 | template 30 | struct has_no_destroy { 31 | // 使用了 SFINAE 技术,即匹配失败不是错误,在编译时期来确定某个 type 是否具有我们需要的性质。 32 | // 此时用于确定 T 类型是否有 no_destroy 方法。 33 | // 如果 T 是 POD 类型,没有 no_destroy 方法,不可以使用 &C::no_destroy 方式调用, 34 | // 意味这如果匹配 test(decltype(&C::no_destroy)) 函数会导致编译错误, 35 | // 那么它会寻找下一个函数去实例化。 36 | // 因为 test(...) 的存在,任何不匹配上一个函数的到这里都会匹配, 37 | // 所以声明的成员函数 test 是的返回类型是 int32_t,而不是 char。 38 | // sizeof(int32_t) != 1,所以 value 的值为 false。 39 | // 参考 http://kaiyuan.me/2018/05/08/sfinae/ 40 | template static char test(decltype(&C::no_destroy)); 41 | template static int32_t test(...); 42 | 43 | const static bool value = sizeof(test(0)) == 1; 44 | }; 45 | 46 | template 47 | class Singleton : noncopyable { 48 | private: 49 | // 使用 pthread_once_t 来保证 lazy-initialization 的线程安全,线程安全性由 Pthread 库保证。 50 | static pthread_once_t ponce_; 51 | static T *value_; 52 | 53 | public: 54 | static T& instance() { 55 | // 使用 pthread_once 函数,保证对象只会初始化一次。 56 | pthread_once(&ponce_, &Singleton::init); 57 | assert(value_ != nullptr); 58 | 59 | return *value_; 60 | } 61 | 62 | private: 63 | Singleton() = default; 64 | ~Singleton() = default; 65 | 66 | static void init() { 67 | value_ = new T(); 68 | // 如果类型 T 没有 no_destroy 方法,即有 destroy 的方式, 69 | // 则注册终止函数,在程序退出前清理资源。 70 | if (!has_no_destroy::value) { 71 | ::atexit(destory); 72 | } 73 | } 74 | 75 | static void destory() { 76 | // 在编译期间检查不完全类型错误。 77 | // 用 typedef 定义了一个数组类型。 78 | // 因为数组的大小不能为 -1,如果 T 是不完全类型, 79 | // sizeof(T) == 0,则数组大小为 -1,编译阶段就会发现错误。 80 | typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1]; 81 | T_must_be_complete_type dummy; 82 | (void)(dummy); 83 | 84 | delete value_; 85 | value_ = nullptr; 86 | } 87 | }; 88 | 89 | // 必须在头文件中定义 static 变量 90 | template 91 | pthread_once_t Singleton::ponce_ = PTHREAD_ONCE_INIT; 92 | 93 | template 94 | T* Singleton::value_ = nullptr; 95 | } 96 | 97 | #endif //TINYWS_SINGLETON_H 98 | -------------------------------------------------------------------------------- /multiThread/base/SpinLock.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_SPINLOCK_H 2 | #define TINYWS_SPINLOCK_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "Thread.h" 9 | #include "noncopyable.h" 10 | 11 | namespace tinyWS_thread { 12 | 13 | // 参考 14 | // https://www.cnblogs.com/chris-cp/p/5413445.html 15 | class SpinLock : noncopyable { 16 | private: 17 | pthread_spinlock_t spinlock_; // 自旋锁 18 | pid_t holder_; // 持有锁的线程的 ID 19 | 20 | public: 21 | SpinLock() : spinlock_{}, holder_(0) { 22 | // PTHREAD_PROCESS_SHARED:该自旋锁可以在多个进程中的线程之间共享。 23 | // PTHREAD_PROCESS_PRIVATE: 仅初始化本自旋锁的线程所在的进程内的线程才能够使用该自旋锁。 24 | assert(pthread_spin_init(&spinlock_, PTHREAD_PROCESS_PRIVATE) == 0); 25 | } 26 | 27 | ~SpinLock() { 28 | assert(holder_ == 0); 29 | assert(pthread_spin_destroy(&spinlock_)); 30 | } 31 | 32 | /** 33 | * 当前线程是否为加锁线程 34 | * @return true / false 35 | */ 36 | bool isLockedByThisThread() { 37 | return holder_ == Thread::gettid(); 38 | } 39 | 40 | /** 41 | * 断言,当前线程是否为加锁线程 42 | */ 43 | void assertLocked() { 44 | assert(isLockedByThisThread()); 45 | } 46 | 47 | void lock() { 48 | pthread_spin_lock(&spinlock_); // 必须先加锁,才能修改holder_ 49 | holder_ = Thread::gettid(); 50 | } 51 | 52 | void unlock() { 53 | holder_ = 0; 54 | pthread_spin_unlock(&spinlock_); 55 | } 56 | 57 | pthread_spinlock_t* getPthreadSpinLock() { 58 | return &spinlock_; 59 | } 60 | }; 61 | 62 | // 自动维护自旋锁加锁和解锁: 63 | // 1 创建对象的时候,加锁; 64 | // 2 离开作用域,销毁对象时,解锁 65 | class SpinLockGuard : noncopyable { 66 | private: 67 | SpinLock& spinlock_; 68 | public: 69 | explicit SpinLockGuard(SpinLock& spinlock) : spinlock_(spinlock) { 70 | spinlock_.lock(); 71 | } 72 | 73 | ~SpinLockGuard() { 74 | spinlock_.unlock(); 75 | } 76 | }; 77 | 78 | // 防止类似误用:SpinLockGuard(spinlock_) 79 | // 临时对象不能长时间持有锁,一产生对象又马上被销毁! 80 | // 正确写法:SpinLockGuard lock(spinlock_) 81 | #define MutexLockGuard(x) error "Missing guard object name" 82 | } 83 | 84 | 85 | #endif //TINYWS_SPINLOCK_H 86 | -------------------------------------------------------------------------------- /multiThread/base/Thread.cpp: -------------------------------------------------------------------------------- 1 | #include "Thread.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "Logger.h" 12 | 13 | using namespace tinyWS_thread; 14 | 15 | Thread::Thread(const Thread::ThreadFunction& func, const std::string& name) 16 | : started_(false), 17 | joined_(false), 18 | pthreadId_(0), 19 | tid_(0), 20 | func_(func), 21 | name_(name) { 22 | } 23 | 24 | Thread::~Thread() { 25 | if (started_ && !joined_) { 26 | pthread_detach(pthreadId_); 27 | } 28 | } 29 | 30 | void Thread::start() { 31 | assert(!started_); 32 | 33 | // 启动线程 34 | started_ = true; 35 | errno = pthread_create(&pthreadId_, nullptr, &startThread, this); 36 | if (errno != 0) { 37 | debug(LogLevel::ERROR) << "Failed in pthread_create" << std::endl; 38 | } 39 | } 40 | 41 | bool Thread::started() { 42 | return started_; 43 | } 44 | 45 | int Thread::join() { 46 | assert(started_); 47 | assert(!joined_); 48 | 49 | joined_ = true; 50 | return pthread_join(pthreadId_, nullptr); 51 | } 52 | 53 | pid_t Thread::tid() const { 54 | return tid_; 55 | } 56 | 57 | const std::string& Thread::name() const { 58 | return name_; 59 | } 60 | 61 | 62 | pid_t Thread::gettid() { 63 | return static_cast(::syscall(__NR_gettid)); 64 | } 65 | 66 | // 通过 void* 类型的形参,将线程对象传递过来。 67 | void* Thread::startThread(void* obj) { 68 | auto *thread = static_cast(obj); 69 | thread->runInThread(); 70 | return nullptr; 71 | } 72 | 73 | void Thread::runInThread() { 74 | tid_ = Thread::gettid(); 75 | try { 76 | func_(); 77 | } catch (const std::exception &ex) { 78 | debug(LogLevel::ERROR) << "exception caught in Thread " << tid_ << std::endl; 79 | debug(LogLevel::ERROR) << "reason: " << ex.what() << std::endl; 80 | abort(); 81 | } catch (...) { 82 | throw; // rethrow 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /multiThread/base/Thread.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_THREAD_H 2 | #define TINYWS_THREAD_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "noncopyable.h" 10 | 11 | namespace tinyWS_thread { 12 | class Thread : noncopyable { 13 | public: 14 | using ThreadFunction = std::function; // 线程执行函数的类型 15 | 16 | /** 17 | * 构造函数 18 | * @param func 线程执行函数 19 | * @param name 线程名 20 | */ 21 | explicit Thread(const ThreadFunction& func, const std::string& name = std::string()); 22 | 23 | ~Thread(); 24 | 25 | /** 26 | * 创建线程 27 | */ 28 | void start(); 29 | 30 | /** 31 | * 线程是否创建 32 | * @return 33 | */ 34 | bool started(); 35 | 36 | /** 37 | * join 38 | * @return pthread_join() 39 | */ 40 | int join(); 41 | 42 | /** 43 | * 获取线程 tid 44 | * @return 系统调用得到的 tid 45 | */ 46 | pid_t tid() const; 47 | 48 | /** 49 | * 获取线程名 50 | * @return 线程名 51 | */ 52 | const std::string& name() const; 53 | 54 | /** 55 | * 使用系统调用获取线程 id 56 | * @return 线程 ID 57 | */ 58 | static pid_t gettid(); 59 | 60 | private: 61 | bool started_; // 线程是否启动 62 | bool joined_; // 线程是否 joined 63 | pthread_t pthreadId_; // pthread 库返回的线程id 64 | pid_t tid_; // 系统调用返回的线程id 65 | ThreadFunction func_; // 线程执行函数 66 | std::string name_; // 线程名 67 | 68 | /** 69 | * 启动线程 70 | * 实际执行的是 Thread::runInThread() 函数 71 | * @param obj 线程对象 72 | * @return nullptr 73 | */ 74 | static void* startThread(void* obj); 75 | 76 | /** 77 | * 在线程中指向注册的函数 78 | */ 79 | void runInThread(); 80 | }; 81 | } 82 | 83 | #endif //TINYWS_THREAD_H 84 | -------------------------------------------------------------------------------- /multiThread/base/ThreadLocal.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_THREADLOCAL_H 2 | #define TINYWS_THREADLOCAL_H 3 | 4 | #include 5 | #include 6 | #include "noncopyable.h" 7 | 8 | namespace tinyWS_thread { 9 | 10 | template 11 | class ThreadLocal : noncopyable { 12 | private: 13 | pthread_key_t pkey_; 14 | 15 | public: 16 | ThreadLocal() { 17 | ::pthread_key_create(&pkey_, &ThreadLocal::destructor); 18 | } 19 | 20 | ~ThreadLocal() { 21 | pthread_key_delete(pkey_); 22 | } 23 | 24 | T& value() { 25 | T* perThreadValue = static_cast(pthread_getspecific(pkey_)); 26 | if (!perThreadValue) { 27 | T* newObj = new T(); 28 | pthread_setspecific(pkey_, newObj); 29 | perThreadValue = newObj; 30 | } 31 | 32 | return *perThreadValue; 33 | } 34 | 35 | private: 36 | static void destructor(void* x) { 37 | T* obj = static_cast(x); 38 | delete obj; 39 | } 40 | }; 41 | } 42 | 43 | #endif //TINYWS_THREADLOCAL_H 44 | -------------------------------------------------------------------------------- /multiThread/base/ThreadPool.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadPool.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Thread.h" 9 | #include "Logger.h" 10 | 11 | using namespace std::placeholders; 12 | using namespace tinyWS_thread; 13 | 14 | ThreadPool::ThreadPool(const std::string &name) 15 | : mutex_(), 16 | cond_(mutex_), 17 | name_(name), 18 | running_(false) { 19 | } 20 | 21 | ThreadPool::~ThreadPool() { 22 | if (running_) { 23 | stop(); 24 | } 25 | } 26 | 27 | void ThreadPool::start(int numThreads) { 28 | assert(threads_.empty()); 29 | running_ = true; 30 | // 先分配足够的空间来存储线程对象,避免频繁内存空间重分配 31 | threads_.reserve(static_cast(numThreads)); 32 | for (int i = 0; i < numThreads; ++i) { 33 | char id[32]; 34 | snprintf(id, sizeof(id), "%d", i); 35 | // 每个线程都会调用 ThreadPool::runInThread() 函数, 36 | // 该函数会不断地从任务队列中取出任务执行。 37 | threads_.push_back(std::unique_ptr( 38 | new Thread(std::bind(&ThreadPool::runInThread, this), 39 | name_ + id))); 40 | threads_[i]->start(); 41 | } 42 | } 43 | 44 | void ThreadPool::stop() { 45 | { 46 | 47 | MutexLockGuard lock(mutex_); 48 | running_ = false; 49 | cond_.notifyAll(); // 可移出临界区 50 | } 51 | // for_each 向 bind 包装好的函数传递 Thread unique_ptr,作为 this 指针传入 52 | std::for_each(threads_.begin(), threads_.end(), std::bind(&Thread::join, _1)); 53 | } 54 | 55 | void ThreadPool::run(const ThreadPool::Task &task) { 56 | if (threads_.empty()) { 57 | // 如果线程池为空,只有主线程,则直接在主线程中执行任务。 58 | task(); 59 | } else { 60 | MutexLockGuard lock(mutex_); 61 | dequeue_.push_back(task); 62 | cond_.notify(); 63 | } 64 | } 65 | 66 | void ThreadPool::runInThread() { 67 | try { 68 | while (running_) { 69 | Task task(take()); 70 | if (task) { 71 | task(); 72 | } 73 | } 74 | } catch (const std::exception &ex) { 75 | debug(LogLevel::ERROR) << "exception caught in ThreadPool " << name_.c_str() << std::endl; 76 | debug(LogLevel::ERROR) << "reason: " << ex.what() << std::endl; 77 | abort(); 78 | } catch (...) { 79 | debug(LogLevel::ERROR) << "unkonw exception caugt in ThreadPool " << name_.c_str() << std::endl; 80 | throw; // rethrow 81 | } 82 | 83 | } 84 | 85 | ThreadPool::Task ThreadPool::take() { 86 | MutexLockGuard lock(mutex_); 87 | // 忙等待,以防虚假唤醒 88 | while (dequeue_.empty() && running_) { 89 | cond_.wait(); 90 | } 91 | 92 | Task task; 93 | if (!dequeue_.empty()) { 94 | task = dequeue_.front(); 95 | dequeue_.pop_front(); 96 | } 97 | 98 | return task; 99 | } 100 | -------------------------------------------------------------------------------- /multiThread/base/ThreadPool.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_THREADPOOL_H 2 | #define TINYWS_THREADPOOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "noncopyable.h" 11 | #include "MutexLock.h" 12 | #include "Condition.h" 13 | 14 | namespace tinyWS_thread { 15 | 16 | class Thread; 17 | 18 | class ThreadPool : noncopyable { 19 | public: 20 | using Task = std::function; // 任务函数类型 21 | 22 | /** 23 | * 构造函数 24 | * @param name 线程池名 25 | */ 26 | explicit ThreadPool(const std::string &name = std::string()); 27 | 28 | ~ThreadPool(); 29 | 30 | /** 31 | * 初始化线程池 32 | * @param numThreads 线程池线程数 33 | */ 34 | void start(int numThreads); 35 | 36 | /** 37 | * 关闭线程池,并等待线程终止 38 | */ 39 | void stop(); 40 | 41 | /** 42 | * 执行任务 43 | * @param task 任务函数 44 | */ 45 | void run(const Task &task); 46 | 47 | private: 48 | using ThreadList = std::vector>; // 线程列表类型 49 | MutexLock mutex_; // 互斥锁 50 | Condition cond_; // 条件变量 51 | std::string name_; // 线程池名 52 | ThreadList threads_; // 线程列表 53 | std::deque dequeue_; // 任务队列(双向队列) 54 | bool running_; // 线程池是否启动 55 | 56 | /** 57 | * 不断地从任务队列中取出任务执行 58 | */ 59 | void runInThread(); 60 | 61 | /** 62 | * 获取任务 63 | * @return 任务函数 64 | */ 65 | Task take(); 66 | }; 67 | } 68 | 69 | 70 | #endif //TINYWS_THREADPOOL_H 71 | -------------------------------------------------------------------------------- /multiThread/base/ThreadPool_cpp11.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadPool_cpp11.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Thread.h" 11 | #include "Logger.h" 12 | 13 | using namespace std::placeholders; 14 | using namespace tinyWS_thread; 15 | 16 | ThreadPool_cpp11::ThreadPool_cpp11(const std::string& name) 17 | : name_(name), 18 | running_(false) { 19 | } 20 | 21 | ThreadPool_cpp11::~ThreadPool_cpp11() { 22 | if (running_) { 23 | stop(); 24 | } 25 | } 26 | 27 | void ThreadPool_cpp11::start(int numThreads) { 28 | assert(threads_.empty()); 29 | running_ = true; 30 | threads_.reserve(static_cast(numThreads)); 31 | for (int i = 0; i < numThreads; ++i) { 32 | char id[32]; 33 | snprintf(id, sizeof(id), "%d", i); 34 | // 每个线程都会调用 ThreadPool::runInThread() 函数, 35 | // 该函数会不断地从任务队列中取出任务执行。 36 | threads_.emplace_back(std::bind(&ThreadPool_cpp11::runInThread, this)); 37 | } 38 | } 39 | 40 | void ThreadPool_cpp11::stop() { 41 | { 42 | std::unique_lock lock(mutex_); 43 | running_ = false; 44 | cond_.notify_all(); 45 | } 46 | // for_each 向 bind 包装好的函数传递 Thread unique_ptr,作为 this 指针传入 47 | std::for_each(threads_.begin(), threads_.end(), [](std::thread& t) { 48 | t.join(); 49 | }); 50 | } 51 | 52 | 53 | void ThreadPool_cpp11::run(const Task& task) { 54 | if (threads_.empty()) { 55 | // 如果线程池为空,只有主线程,则直接在主线程中执行任务。 56 | task(); 57 | } else { 58 | std::unique_lock lock(mutex_); 59 | queue_.push(task); 60 | cond_.notify_one(); 61 | } 62 | } 63 | 64 | void ThreadPool_cpp11::runInThread() { 65 | try { 66 | while (running_) { 67 | Task task(take()); 68 | if (task) { 69 | task(); 70 | } 71 | } 72 | } catch (const std::exception& ex) { 73 | debug(LogLevel::ERROR) << "exception caught in ThreadPool " << name_.c_str() << std::endl; 74 | debug(LogLevel::ERROR) << "reason: " << ex.what() << std::endl; 75 | abort(); 76 | } catch (...) { 77 | debug(LogLevel::ERROR) << "unkonw exception caugt in ThreadPool " << name_.c_str() << std::endl; 78 | throw; // rethrow 79 | } 80 | 81 | } 82 | 83 | ThreadPool_cpp11::Task ThreadPool_cpp11::take() { 84 | std::unique_lock lock(mutex_); 85 | // 忙等待,以防虚假唤醒 86 | while (queue_.empty() && running_) { 87 | cond_.wait(lock); 88 | } 89 | 90 | Task task; 91 | if (!queue_.empty()) { 92 | task = queue_.front(); 93 | queue_.pop(); 94 | } 95 | 96 | return task; 97 | } 98 | -------------------------------------------------------------------------------- /multiThread/base/ThreadPool_cpp11.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_THREADPOOL_CPP11_H 2 | #define TINYWS_THREADPOOL_CPP11_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "noncopyable.h" 13 | 14 | namespace tinyWS_thread { 15 | 16 | // 线程池 C++ 11 实现 17 | // 参考 18 | // https://wangpengcheng.github.io/2019/05/17/cplusplus_theadpool/ 19 | // http://www.cclk.cc/2017/11/14/c++/c++_threadpool/ 20 | class ThreadPool_cpp11 : noncopyable { 21 | public: 22 | using Task = std::function; // 任务函数类型 23 | 24 | private: 25 | using ThreadList = std::vector; // 线程列表类型 26 | std::mutex mutex_; 27 | std::condition_variable cond_; 28 | std::string name_; // 线程池名 29 | ThreadList threads_; // 线程列表 30 | std::queue queue_; // 任务队列(双向队列) 31 | bool running_; // 线程池是否启动 32 | 33 | public: 34 | /** 35 | * 构造函数 36 | * @param name 线程池名 37 | */ 38 | explicit ThreadPool_cpp11(const std::string& name = std::string()); 39 | 40 | ~ThreadPool_cpp11(); 41 | 42 | /** 43 | * 初始化线程池 44 | * @param numThreads 线程池线程数 45 | */ 46 | void start(int numThreads); 47 | 48 | /** 49 | * 关闭线程池,并等待线程终止 50 | */ 51 | void stop(); 52 | 53 | /** 54 | * 执行任务 55 | * @param task 任务函数 56 | */ 57 | void run(const Task& task); 58 | 59 | private: 60 | /** 61 | * 不断地从任务队列中取出任务执行 62 | */ 63 | void runInThread(); 64 | 65 | /** 66 | * 获取任务 67 | * @return 任务函数 68 | */ 69 | Task take(); 70 | }; 71 | } 72 | 73 | 74 | #endif //TINYWS_THREADPOOL_CPP11_H 75 | -------------------------------------------------------------------------------- /multiThread/base/noncopyable.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_NONCOPYABLE_H 2 | #define TINYWS_NONCOPYABLE_H 3 | 4 | namespace tinyWS_thread { 5 | // 不可拷贝类 6 | class noncopyable { 7 | protected: 8 | // 默认的构造函数和析构函数是 protected, 9 | // 不允许创建 noncopyable 实例,但允许子类创建实例 10 | // (即允许派生类构造和析构)。 11 | noncopyable() = default; 12 | 13 | ~noncopyable() = default; 14 | 15 | private: 16 | // 使用 delete 关键字禁止编译器自动产生复制构造函数和复制赋值操作符。 17 | noncopyable(const noncopyable&) = delete; 18 | 19 | noncopyable& operator=(const noncopyable&) = delete; 20 | }; 21 | } 22 | 23 | #endif //TINYWS_NONCOPYABLE_H 24 | -------------------------------------------------------------------------------- /multiThread/doc/model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiThread/doc/model.png -------------------------------------------------------------------------------- /multiThread/doc/pressure_test.md: -------------------------------------------------------------------------------- 1 | # 压力测试 2 | 3 | ## 测试环境 4 | 5 | ### 腾讯云虚拟主机 6 | 7 | - OS: Ubuntu 16.04 LTS 8 | - CPU: 1核 9 | - 内存:2M 10 | 11 | ## 测试方法 12 | 13 | - 为了避免带宽带来的影响,将服务器程序和 WebBench 程序都运行在同一台主机上。 14 | - 使用工具 Webbench,开启 1000 客户端进程,时间为 60s。 15 | - 分别测试短连接和长连接的情况。 16 | - 关闭所有的普通的 Log,只留下 ERROR 级别的 Log。 17 | - 为避免磁盘 IO 对测试结果的影响,测试响应为内存中的"Hello World"字符加上必要的HTTP头。 18 | - 线程池开启 4 个线程。 19 | 20 | ## 测试结果 21 | 22 | | 短连接QPS | 长连接QPS | 23 | | --------- | --------- | 24 | | 8377 | 31325 | 25 | 26 | 处理请求数方面,长连接比短连接能很多,多了差不多 4 倍。 27 | 28 | ## 测试结果截图 29 | 30 | - 短连接 31 | 32 | ![短连接](./pressure_test_short.png) 33 | 34 | - 长连接 35 | 36 | ![长连接](./pressure_test_keep_alive.png) 37 | 38 | ## 参考 39 | 40 | - [linyacool](https://github.com/linyacool)的[WebServer](https://github.com/linyacool/WebServer)中的[测试及改进]([https://github.com/linyacool/WebServer/blob/HEAD/%E6%B5%8B%E8%AF%95%E5%8F%8A%E6%94%B9%E8%BF%9B.md](https://github.com/linyacool/WebServer/blob/HEAD/测试及改进.md))。 41 | - [WebBench](https://github.com/linyacool/WebBench)来自[linyacool](https://github.com/linyacool)的[WebServer](https://github.com/linyacool/WebServer)。 -------------------------------------------------------------------------------- /multiThread/doc/pressure_test_keep_alive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiThread/doc/pressure_test_keep_alive.png -------------------------------------------------------------------------------- /multiThread/doc/pressure_test_short.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiThread/doc/pressure_test_short.png -------------------------------------------------------------------------------- /multiThread/http/HttpContext.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPCONTEXT_H 2 | #define TINYWS_HTTPCONTEXT_H 3 | 4 | #include "HttpRequest.h" 5 | #include "../net/Timer.h" 6 | 7 | namespace tinyWS_thread { 8 | class Buffer; 9 | 10 | class HttpContext { 11 | public: 12 | // 解析状态 13 | enum HttpRequestParseState { 14 | kExpectRequestLine, // 解析行 15 | kExpectHeader, // 解析请求头 16 | kExpectBody, // 解析 Body 17 | kGotAll // 解析完成 18 | }; 19 | 20 | HttpContext(); 21 | 22 | /** 23 | * 解析 Buffer 的数据,并将相应的信息添加到 HttpRequest 中,最后返回是否解析成功。 24 | * @param buffer 缓冲区 25 | * @param receiveTime 连接接收时间 26 | * @return 是否解析成功 27 | */ 28 | bool parseRequest(Buffer *buffer, Timer::TimeType receiveTime); 29 | 30 | /* 31 | * 是否解析完成 32 | */ 33 | bool gotAll() const; 34 | 35 | /** 36 | * 重置解析状态为 kExpectRequestLine,清空 HttpRequest 37 | */ 38 | void reset(); 39 | 40 | /** 41 | * 获取 HttpRequest 42 | * @return HttpRequest 43 | */ 44 | const HttpRequest& request() const; 45 | 46 | // 同上 47 | HttpRequest& request(); 48 | 49 | private: 50 | HttpRequestParseState state_; // 当前解析状态 51 | HttpRequest request_; // 请求 52 | 53 | /** 54 | * 解析行 55 | * @param start 起始指针 56 | * @param end 末尾指针 57 | * @return 是否解析成功 58 | */ 59 | bool processRequestLine(const char *start, const char *end); 60 | }; 61 | } 62 | 63 | 64 | #endif //TINYWS_HTTPCONTEXT_H 65 | -------------------------------------------------------------------------------- /multiThread/http/HttpRequest.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPREQUEST_H 2 | #define TINYWS_HTTPREQUEST_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../net/Timer.h" 8 | 9 | namespace tinyWS_thread { 10 | // 用于保存请求相关的信息:请求方法、请求路径、查询字段、接收请求的时间、请求头 11 | class HttpRequest { 12 | public: 13 | // 请求方法 14 | enum Method { 15 | kInvalid, kGet, kPost, kHead, kPut, kDelete 16 | }; 17 | 18 | HttpRequest(); 19 | 20 | /** 21 | * 设置请求方法,并返回请求方法是否有效。 22 | * @param start 请求方法字符串的起始指针 23 | * @param end 请求方法字符串的末尾指针 24 | * @return 请求方法是否有效 25 | */ 26 | bool setMethod(const char *start, const char *end); 27 | 28 | /** 29 | * 获取请求方法 30 | * @return 请求方法 31 | */ 32 | Method method() const; 33 | 34 | /** 35 | * 获取请求方法的字符串 36 | * @return 请求方法字符串 37 | */ 38 | const char* methodString() const; 39 | 40 | /** 41 | * 设置请求路径 42 | * @param start 请求路径字符串的起始指针 43 | * @param end 请求路径字符串的末尾指针 44 | */ 45 | void setPath(const char *start, const char *end); 46 | 47 | /** 48 | * 获取请求路径 49 | * @return 请求路径字符串 50 | */ 51 | const std::string& path() const; 52 | 53 | /** 54 | * 设置查询字段 55 | * @param start 查询字段字符串的起始指针 56 | * @param end 查询字段字符串的末尾指针 57 | */ 58 | void setQuery(const char *start, const char *end); 59 | 60 | /** 61 | * 获取查询字段 62 | * @return 查询字段字符串 63 | */ 64 | const std::string& query() const; 65 | 66 | /** 67 | * 设置请求接收时间 68 | * @param time 时间 69 | */ 70 | void setReceiveTime(Timer::TimeType time); 71 | 72 | /** 73 | * 获取请求接收时间 74 | * @return 请求接收时间 75 | */ 76 | Timer::TimeType receiveTime() const; 77 | 78 | /** 79 | * 添加请求头字段 80 | * @param start 请求头字段字符串的起始位置 81 | * @param colon 分隔符(:)的位置 82 | * @param end 请求头字段字符串的末尾位置 83 | */ 84 | void addHeader(const char *start, const char *colon, const char *end); 85 | 86 | /** 87 | * 获取特定请求头字段的值 88 | * @param field 名字 89 | * @return 值 90 | */ 91 | std::string getHeader(const std::string &field) const; 92 | 93 | /** 94 | * 获取所有请求头 95 | * @return 请求头映射 96 | */ 97 | const std::map& headers() const; 98 | 99 | // 交换 100 | void swap(HttpRequest &that); 101 | 102 | private: 103 | Method method_; // 请求方法 104 | std::string path_; // 请求路径 105 | std::string query_; // 查询字段 106 | Timer::TimeType receiveTime_; // 请求接收时间 107 | std::map headers_; // 请求头映射 108 | }; 109 | } 110 | 111 | #endif //TINYWS_HTTPREQUEST_H 112 | -------------------------------------------------------------------------------- /multiThread/http/HttpResponse.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpResponse.h" 2 | 3 | #include 4 | 5 | #include "../net/Buffer.h" 6 | 7 | using namespace tinyWS_thread; 8 | 9 | HttpResponse::HttpResponse(bool close) 10 | : statusCode_(kUnknown), 11 | closeConnection_(close){ 12 | 13 | } 14 | 15 | void HttpResponse::setStatusCode(HttpStatusCode code) { 16 | statusCode_ = code; 17 | } 18 | 19 | void HttpResponse::setStatusMessage(const std::string &message) { 20 | statusMessage_ = message; 21 | } 22 | 23 | void HttpResponse::setCloseConnection(bool on) { 24 | closeConnection_ = on; 25 | } 26 | 27 | bool HttpResponse::closeConnection() const { 28 | return closeConnection_; 29 | } 30 | 31 | void HttpResponse::setContentType(const std::string &contentType) { 32 | addHeader("Content-Type", contentType); 33 | } 34 | 35 | void HttpResponse::addHeader(const std::string &key, const std::string &value) { 36 | headers_[key] = value; 37 | } 38 | 39 | void HttpResponse::setBody(const std::string &body) { 40 | body_ = body; 41 | } 42 | 43 | void HttpResponse::appendToBuffer(Buffer *output) const { 44 | // 响应行:请求方法 路径 HTTP/版本 45 | char buf[32]; 46 | snprintf(buf, sizeof(buf), "HTTP/1.1 %d ", statusCode_); 47 | 48 | output->append(buf); 49 | output->append(statusMessage_); 50 | output->append("\r\n"); 51 | 52 | // 添加响应头 53 | if (closeConnection_) { 54 | // 关闭连接 55 | output->append("Connection: close\r\n"); 56 | } else { 57 | snprintf(buf, sizeof(buf), "Content-Length: %zd\r\n", body_.size()); 58 | output->append(buf); 59 | output->append("Connection: Keep-Alive\r\n"); 60 | } 61 | 62 | for (const auto &header : headers_) { 63 | output->append(header.first); 64 | output->append(": "); 65 | output->append(header.second); 66 | output->append("\r\n"); 67 | } 68 | 69 | output->append("\r\n"); 70 | output->append(body_); 71 | } 72 | -------------------------------------------------------------------------------- /multiThread/http/HttpResponse.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPRESPONSE_H 2 | #define TINYWS_HTTPRESPONSE_H 3 | 4 | #include 5 | #include 6 | 7 | namespace tinyWS_thread { 8 | class Buffer; 9 | 10 | class HttpResponse { 11 | public: 12 | // 状态码 13 | enum HttpStatusCode { 14 | kUnknown, 15 | k200OK = 200, 16 | k301MovedPermanently = 301, 17 | k400BadRequest = 400, 18 | k404NotFound = 404 19 | }; 20 | 21 | /** 22 | * 构造函数 23 | * @param close 连接是否已关闭 24 | */ 25 | explicit HttpResponse(bool close); 26 | 27 | /** 28 | * 设置状态码 29 | * @param code 状态码 30 | */ 31 | void setStatusCode(HttpStatusCode code); 32 | 33 | /** 34 | * 设置状态信息 35 | * @param message 状态信息 36 | */ 37 | void setStatusMessage(const std::string &message); 38 | 39 | /** 40 | * 设置是否将 Connection 字段设置为 close 41 | * @param on true / false 42 | */ 43 | void setCloseConnection(bool on); 44 | 45 | /** 46 | * 获取是否已将 Connection 字段设置为 close 47 | * @return true / false 48 | */ 49 | bool closeConnection() const; 50 | 51 | /** 52 | * 设置 Content-Type 53 | * @param contentType Content-Type 字符串 54 | */ 55 | void setContentType(const std::string &contentType); 56 | 57 | /** 58 | * 添加响应头 59 | * @param key 60 | * @param value 61 | */ 62 | void addHeader(const std::string &key, const std::string & value); 63 | 64 | /** 65 | * 设置 Response Body 66 | * @param body Response Body 字符串 67 | */ 68 | void setBody(const std::string &body); 69 | 70 | /** 71 | * 将响应数据(包括响应头和 Body)添加到 Buffer 中 72 | * @param output 数据指针 73 | */ 74 | void appendToBuffer(Buffer *output) const; 75 | 76 | private: 77 | std::map headers_; // 响应头映射 78 | HttpStatusCode statusCode_; // 状态码 79 | std::string statusMessage_; // 状态信息 80 | bool closeConnection_; // 是否将 Connection 字段设置为 close 81 | std::string body_; // Response Body 82 | }; 83 | } 84 | 85 | #endif //TINYWS_HTTPRESPONSE_H 86 | -------------------------------------------------------------------------------- /multiThread/http/HttpServer.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpServer.h" 2 | 3 | #include "HttpContext.h" 4 | #include "HttpRequest.h" 5 | #include "HttpResponse.h" 6 | 7 | using namespace std::placeholders; 8 | using namespace tinyWS_thread; 9 | 10 | HttpServer::HttpServer(EventLoop *loop, 11 | const InternetAddress &listenAddress, 12 | const std::string &name) 13 | : tcpServer_(loop, listenAddress, name), 14 | httpCallback_() { 15 | tcpServer_.setConnectionCallback( 16 | std::bind(&HttpServer::onConnection, this, _1)); 17 | tcpServer_.setMessageCallback( 18 | std::bind(&HttpServer::onMessage, this, _1, _2, _3)); 19 | } 20 | 21 | EventLoop* HttpServer::getLoop() const { 22 | return tcpServer_.getLoop(); 23 | } 24 | 25 | void HttpServer::setHttpCallback(const HttpCallback &cb) { 26 | httpCallback_ = cb; 27 | } 28 | 29 | void HttpServer::setThreadNum(int threadsNum) { 30 | tcpServer_.setThreadNumber(threadsNum); 31 | } 32 | 33 | void HttpServer::start() { 34 | tcpServer_.start(); 35 | } 36 | 37 | void HttpServer::onConnection(const TcpConnectionPtr &connection) { 38 | if (connection->connected()) { 39 | connection->setContext(HttpContext()); 40 | } 41 | } 42 | 43 | void HttpServer::onMessage(const TcpConnectionPtr &connection, Buffer *buffer, 44 | Timer::TimeType receiveTime) { 45 | auto context = tinyWS_thread::any_cast(connection->getMutableContext()); 46 | if (!context->parseRequest(buffer, receiveTime)) { 47 | // 404 48 | connection->send("HTTP/1.1 400 Bad Request\r\n\r\n"); 49 | connection->shutdown(); 50 | } 51 | 52 | // 解析失败 跟 解析完成 互斥 53 | 54 | if (context->gotAll()) { 55 | // 解析完成,响应请求 56 | onRequest(connection, context->request()); 57 | // 重置 HttpContext 58 | context->reset(); 59 | } 60 | } 61 | 62 | void HttpServer::onRequest(const TcpConnectionPtr &connection, 63 | const HttpRequest &httpRequest) { 64 | const std::string &connectionStr = httpRequest.getHeader("Connection"); 65 | bool isClose = connectionStr == "close"; 66 | HttpResponse response(isClose); 67 | 68 | if (httpCallback_) { 69 | httpCallback_(httpRequest, response); 70 | } 71 | 72 | Buffer buffer; 73 | response.appendToBuffer(&buffer); 74 | connection->send(buffer.toString()); 75 | // 如果 isClose 为 true,即将要关闭连接,但是数据还没发送完,连接也会在数据发完才会关闭。 76 | // 在 TcpConnection::handleWrite() 和 77 | // TcpConnection::send() 只调用了一次 write(2)而不会反复调用直至它返回 EAGAIN, 78 | // 因为如果第一次 write(2) 没有能够将数据发送完,第二次调用 write(2) 几乎肯定会将数据发送完,并返回 EAGAIN。 79 | // 那么第二次调用 write(2) 会发生在连接关闭之前吗? 80 | // 会。 81 | // 第一次如果没有将数据发送完,会令 Channel 关注写事件(此时调用 Channel::isWriting() 会返回 true,表示 Channel 处在写数据状态)。 82 | // 当写事件到来时,调用 TcpConnection::handleWrite(),其中会第二次调用 write(2) 将数据发送完。 83 | // 而 TcpConnection::shutdown() 只有当对应的 CHannel 不处在写数据状态才会关闭连接,而当对应 Channel 处在写数据状态,则不做任何操作。 84 | // 所以,数据肯定能发送完,在关闭连接。 85 | if (response.closeConnection()) { 86 | connection->shutdown(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /multiThread/http/HttpServer.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_HTTPSERVER_H 2 | #define TINYWS_HTTPSERVER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../base/noncopyable.h" 8 | #include "../net/TcpServer.h" 9 | #include "../net/TcpConnection.h" 10 | #include "../net/Timer.h" 11 | 12 | namespace tinyWS_thread{ 13 | class Buffer; 14 | class HttpRequest; 15 | class HttpResponse; 16 | 17 | class HttpServer : noncopyable { 18 | public: 19 | // HTTP 请求到来时的回调函数的类型 20 | using HttpCallback = std::function; 21 | 22 | /** 23 | * 构造函数 24 | * @param loop 所属 EventLoop 25 | * @param listenAddress 监听地址 26 | * @param name TcpServer name 27 | */ 28 | HttpServer(EventLoop *loop, 29 | const InternetAddress& listenAddress, 30 | const std::string &name); 31 | 32 | /** 33 | * 获取所属 EventLoop 34 | * @return EventLoop 35 | */ 36 | EventLoop* getLoop() const; 37 | 38 | /** 39 | * 设置 HTTP 请求到来时的回调函数 40 | * @param cb 41 | */ 42 | void setHttpCallback(const HttpCallback &cb); 43 | 44 | /** 45 | * 设置 IO 线程数 46 | * @param threadsNum 线程数 47 | */ 48 | void setThreadNum(int threadsNum); 49 | 50 | /** 51 | * 启动 TcpServer 52 | */ 53 | void start(); 54 | private: 55 | TcpServer tcpServer_; // TcpServer 56 | HttpCallback httpCallback_; // HTTP 请求到来时的回调函数 57 | 58 | /** 59 | * 连接建立后,将 HttpContext 传给 TcpConnection。 60 | * @param connection TcpConnectionPtr 61 | */ 62 | void onConnection(const TcpConnectionPtr &connection); 63 | 64 | /** 65 | * 请求到来后,解析请求,响应请求。 66 | * @param connection 67 | * @param buffer 68 | * @param receiveTime 69 | */ 70 | void onMessage(const TcpConnectionPtr &connection, 71 | Buffer *buffer, 72 | Timer::TimeType receiveTime); 73 | /** 74 | * 当解析完一条请求信息后,响应请求。 75 | * @param connection TcpConnectionPtr 76 | * @param httpRequest 77 | */ 78 | void onRequest(const TcpConnectionPtr &connection, 79 | const HttpRequest &httpRequest); 80 | }; 81 | } 82 | 83 | #endif //TINYWS_HTTPSERVER_H 84 | -------------------------------------------------------------------------------- /multiThread/net/Acceptor.cpp: -------------------------------------------------------------------------------- 1 | #include "Acceptor.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "../base/Logger.h" 10 | #include "EventLoop.h" 11 | #include "InternetAddress.h" 12 | 13 | using namespace tinyWS_thread; 14 | 15 | Acceptor::Acceptor(EventLoop *loop, const InternetAddress &listenAddress) 16 | : loop_(loop), 17 | acceptSocket_(createNonblocking()), 18 | acceptChannel_(loop_, acceptSocket_.fd()), 19 | isListening_(false) { 20 | // 设置端口复用、绑定地址、设置"读"回调函数 21 | acceptSocket_.setReuseAddr(true); 22 | acceptSocket_.bindAddress(listenAddress); 23 | acceptChannel_.setReadCallback(std::bind(&Acceptor::hadleRead, this)); 24 | } 25 | 26 | Acceptor::~Acceptor() { 27 | acceptChannel_.disableAll(); 28 | acceptChannel_.remove(); 29 | } 30 | 31 | void Acceptor::setNewConnectionCallback(const NewConnectionCallback &cb) { 32 | newConnectionCallback_ = cb; 33 | } 34 | 35 | bool Acceptor::isListening() const { 36 | return isListening_; 37 | } 38 | 39 | void Acceptor::listen() { 40 | loop_->assertInLoopThread(); 41 | isListening_ = true; 42 | acceptSocket_.listen(); 43 | acceptChannel_.enableReading(); 44 | } 45 | 46 | int Acceptor::createNonblocking() { 47 | int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP); 48 | if (sockfd < 0) { 49 | debug(LogLevel::ERROR) << "sockets::createNonblockingOrDie" << std::endl; 50 | } 51 | return sockfd; 52 | } 53 | 54 | void Acceptor::hadleRead() { 55 | loop_->assertInLoopThread(); 56 | InternetAddress peerAddress; 57 | // 每次只 accept 一次 58 | Socket connectionSocket(acceptSocket_.accept(&peerAddress)); 59 | if (connectionSocket.fd() >= 0) { 60 | if (newConnectionCallback_) { 61 | // 移动 Socket,保证资源的安全释放 62 | newConnectionCallback_(std::move(connectionSocket), peerAddress); 63 | } 64 | } 65 | 66 | // 一直 accept 到 EAGAIN 为止。这样需要多一次系统调用。 67 | // TODO 测试新能 68 | // while (auto sockfd = acceptSocket_.accept(&peerAddress)) { 69 | // if (sockfd < 0) { 70 | // return; 71 | // } 72 | // 73 | // Socket connectionSocket(sockfd); 74 | // if (newConnectionCallback_) { 75 | // newConnectionCallback_(std::move(connectionSocket), peerAddress); 76 | // } 77 | // } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /multiThread/net/Acceptor.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_ACCEPTOR_H 2 | #define TINYWS_ACCEPTOR_H 3 | 4 | #include 5 | 6 | #include "../base/noncopyable.h" 7 | #include "Channel.h" 8 | #include "Socket.h" 9 | 10 | namespace tinyWS_thread { 11 | class InternetAddress; 12 | class EventLoop; 13 | 14 | // 内部类,供 TcpServer 使用,生命周期由 TcpServer 控制。 15 | // 用于 accept(2) 新 TCP 连接,并通过回调函数通知使用者。 16 | class Acceptor : noncopyable { 17 | public: 18 | // 新连接到来时的回调函数的类型 19 | using NewConnectionCallback = std::function; 20 | 21 | /** 22 | * 构造函数 23 | * @param loop 所属 EventLoop 24 | * @param listenAddress 监听的地址 25 | */ 26 | Acceptor(EventLoop *loop, const InternetAddress &listenAddress); 27 | 28 | ~Acceptor(); 29 | 30 | /** 31 | * 设置新连接到来时的回调函数 32 | * @param cb 回调函数 33 | */ 34 | void setNewConnectionCallback(const NewConnectionCallback &cb); 35 | 36 | /** 37 | * 是否处于监听状态 38 | * @return true / false 39 | */ 40 | bool isListening() const; 41 | 42 | /** 43 | * 监听 44 | */ 45 | void listen(); 46 | 47 | /** 48 | * 创建无阻塞的 socket 49 | * @return socket 50 | */ 51 | static int createNonblocking(); 52 | 53 | private: 54 | EventLoop *loop_; // 所属 EventLoop 55 | Socket acceptSocket_; // listening socket 56 | Channel acceptChannel_; // 用于观察 acceptSocket_ 的 readable 事件 57 | NewConnectionCallback newConnectionCallback_; // 新连接到来时的回调函数 58 | bool isListening_; // 是否处于监听状态 59 | 60 | /** 61 | * 读数据,获取新连接对应的 socket fd,并调用新连接到来时的回调函数,停止用户有新的连接。 62 | */ 63 | void hadleRead(); 64 | }; 65 | } 66 | 67 | 68 | #endif //TINYWS_ACCEPTOR_H 69 | -------------------------------------------------------------------------------- /multiThread/net/CallBack.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_CALLBACK_H 2 | #define TINYWS_CALLBACK_H 3 | 4 | #include 5 | #include 6 | 7 | namespace tinyWS_thread { 8 | class TcpConnection; 9 | class Buffer; 10 | 11 | // TcpConnection 对象的智能指针类型 12 | using TcpConnectionPtr = std::shared_ptr; 13 | 14 | // 连接建立的回调函数的类型 15 | using ConnectionCallback = std::function; 16 | 17 | // 消息到来的回调函数的类型 18 | using MessageCallback = std::function; 19 | 20 | // 连接断开的的回调函数的类型 21 | using CloseCallback = std::function; 22 | 23 | // 写完成的回调函数的类型 24 | using WriteCompleteCallback = std::function; 25 | 26 | // "高水位"回调函数的类型 27 | using HighWaterMarkCallback = std::function; 28 | 29 | void defaultConnectionCallback(const TcpConnectionPtr& conn); 30 | 31 | void defaultMessageCallback(const TcpConnectionPtr& conn, 32 | Buffer* buffer, 33 | Timer::TimeType receiveTime); 34 | 35 | } 36 | 37 | 38 | #endif //TINYWS_CALLBACK_H 39 | -------------------------------------------------------------------------------- /multiThread/net/Connector.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_CONNECTOR_H 2 | #define TINYWS_CONNECTOR_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../base/noncopyable.h" 8 | #include "InternetAddress.h" 9 | 10 | namespace tinyWS_thread { 11 | 12 | class EventLoop; 13 | class Channel; 14 | 15 | class Connector : noncopyable, 16 | public std::enable_shared_from_this { 17 | public: 18 | using NewConnectionCallback = std::function; 19 | 20 | Connector(EventLoop* loop, const InternetAddress &serverAddress); 21 | 22 | ~Connector(); 23 | 24 | void setNewConnectionCallback(const NewConnectionCallback& cb); 25 | 26 | 27 | void start(); 28 | 29 | void restart(); 30 | 31 | void stop(); 32 | 33 | const InternetAddress& serverAddress() const; 34 | 35 | private: 36 | enum States { kDisconnected, kConnecting, kConnected }; 37 | static const int kMaxRetryDelayMs = 30 * 1000; 38 | static const int kInitRetryDelayMs = 500; 39 | 40 | EventLoop* loop_; 41 | InternetAddress serverAddress_; 42 | bool connect_; 43 | States state_; 44 | std::unique_ptr channel_; 45 | NewConnectionCallback newConnectionCallback_; 46 | int retryDelayMs_; 47 | 48 | void setState(States state); 49 | 50 | void startInLoop(); 51 | 52 | void stopInLoop(); 53 | 54 | void connect(); 55 | 56 | void connecting(int sockfd); 57 | 58 | void handleWrite(); 59 | 60 | void handleError(); 61 | 62 | void retry(int sockfd); 63 | 64 | int removeAndResetChannel(); 65 | 66 | void resetChannel(); 67 | 68 | int getSocketError(int sockfd); 69 | 70 | bool isSelfConnect(int sockfd); 71 | }; 72 | } 73 | 74 | 75 | #endif //TINYWS_CONNECTOR_H 76 | -------------------------------------------------------------------------------- /multiThread/net/EventLoopThread.cpp: -------------------------------------------------------------------------------- 1 | #include "EventLoopThread.h" 2 | 3 | #include 4 | 5 | #include "EventLoop.h" 6 | 7 | using namespace tinyWS_thread; 8 | 9 | EventLoopThread::EventLoopThread(const EventLoopThreadCallback &cb) 10 | : loop_(nullptr), 11 | exiting_(false), 12 | thread_(std::bind(&EventLoopThread::threadFunction, this)), 13 | mutex_(), 14 | cond_(mutex_), 15 | callback_(cb) { 16 | 17 | } 18 | 19 | EventLoopThread::~EventLoopThread() { 20 | exiting_ = true; 21 | // 如果 IO 线程已经创建, 22 | // 则要退出 EventLoop 和 结束线程。 23 | if (loop_ != nullptr) { 24 | loop_->quit(); 25 | thread_.join(); 26 | } 27 | } 28 | 29 | EventLoop* EventLoopThread::startThread() { 30 | // 检查线程是否启动,防止重复启动线程 31 | assert(!thread_.started()); 32 | thread_.start(); 33 | 34 | { 35 | // 使用条件变量,一直等到 IO 线程创建好, 36 | // 即 loop_ 不为 nullptr,再返回 loop_。 37 | MutexLockGuard lock(mutex_); 38 | while (loop_ == nullptr) { 39 | cond_.wait(); 40 | } 41 | } 42 | return loop_; 43 | } 44 | 45 | /** 46 | * 在新创建的线程 stack 上定义 EventLoop 对象, 47 | * 然后将其地址赋值给 loop_ 成员变量, 48 | * 最后,使用条件变量唤醒主线程。 49 | */ 50 | void EventLoopThread::threadFunction() { 51 | EventLoop loop; 52 | 53 | if (callback_) { 54 | callback_(&loop); 55 | } 56 | 57 | { 58 | MutexLockGuard lock(mutex_); 59 | loop_ = &loop; 60 | cond_.notify(); 61 | } 62 | 63 | loop.loop(); 64 | } -------------------------------------------------------------------------------- /multiThread/net/EventLoopThread.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_EVENTLOOPTHREAD_H 2 | #define TINYWS_EVENTLOOPTHREAD_H 3 | 4 | #include 5 | 6 | #include "../base/noncopyable.h" 7 | #include "../base/MutexLock.h" 8 | #include "../base/Condition.h" 9 | #include "../base/Thread.h" 10 | 11 | namespace tinyWS_thread { 12 | class EventLoop; 13 | 14 | // EventLoopThread 启动自己的线程, 15 | // 在其中运行 EventLoop::loop,并返回 EventLoop 指针 16 | class EventLoopThread : noncopyable { 17 | public: 18 | using EventLoopThreadCallback = std::function; // 线程回调函数类型 19 | 20 | /** 21 | * 构造函数 22 | * @param cb 回调函数 23 | */ 24 | explicit EventLoopThread(const EventLoopThreadCallback &cb = EventLoopThreadCallback()); 25 | 26 | ~EventLoopThread(); 27 | 28 | /** 29 | * 启动线程,并在创建的线程中执行 EventLoop::loop() 30 | * @return EventLoop 所属 EventLoop 31 | */ 32 | EventLoop* startThread(); 33 | 34 | private: 35 | // EventLoop 的生命周期与线程主函数的作用域相同, 36 | // 因此在 threadFunction 退出后,这个指针就失效了。 37 | // 但服务器程序一般不要求能安全地退出,这应该不是什么大问题。 38 | EventLoop *loop_; // 所属 EventLoop 39 | bool exiting_; // 是否已经创建 EventLoop 40 | Thread thread_; // 线程 41 | MutexLock mutex_; // 互斥量 42 | Condition cond_; // 条件变量 43 | EventLoopThreadCallback callback_; // 回调函数 44 | 45 | /** 46 | * 启动线程执行的函数 47 | */ 48 | void threadFunction(); 49 | }; 50 | } 51 | 52 | 53 | #endif //TINYWS_EVENTLOOPTHREAD_H 54 | -------------------------------------------------------------------------------- /multiThread/net/EventLoopThreadPool.cpp: -------------------------------------------------------------------------------- 1 | #include "EventLoopThreadPool.h" 2 | 3 | #include 4 | 5 | #include "EventLoop.h" 6 | #include "EventLoopThread.h" 7 | #include "../base/MutexLock.h" 8 | 9 | using namespace tinyWS_thread; 10 | 11 | EventLoopThreadPool::EventLoopThreadPool(EventLoop *baseLoop) 12 | : baseLoop_(baseLoop), 13 | started_(false), 14 | numThreads_(0), 15 | next_(0) { 16 | 17 | } 18 | 19 | EventLoopThreadPool::~EventLoopThreadPool() { 20 | // Don't delete loop, it's stack variable 21 | } 22 | 23 | void EventLoopThreadPool::setThreadNum(int numThreads) { 24 | assert(numThreads >= 0); 25 | numThreads_ = numThreads; 26 | } 27 | 28 | void EventLoopThreadPool::start(const EventLoopThreadPool::EventLoopThreadPoolCallback &cb) { 29 | assert(!started_); 30 | baseLoop_->assertInLoopThread(); 31 | 32 | started_ = true; 33 | 34 | // 创建线程池 35 | for (int i = 0; i < numThreads_; ++i) { 36 | auto *t = new EventLoopThread(cb); 37 | threads_.push_back(std::unique_ptr(t)); 38 | loops_.push_back(t->startThread()); 39 | } 40 | // 如果线程池为空,则在主线程执行。 41 | if (numThreads_ == 0 && cb) { 42 | cb(baseLoop_); 43 | } 44 | } 45 | 46 | EventLoop* EventLoopThreadPool::getNextLoop() { 47 | baseLoop_->assertInLoopThread(); 48 | EventLoop *loop = baseLoop_; 49 | 50 | // 如果线程池为空,即 TcpServer 为单线程,则返回主 EventLoop; 51 | // 如果线程池不为空,则使用 Round-robin 策略选择一个 EventLoop 返回。 52 | if (!loops_.empty()) { 53 | // Round-robin 54 | loop = loops_[next_]; 55 | ++next_; 56 | if (static_cast(next_) >= loops_.size()) { 57 | next_ = 0; 58 | } 59 | } 60 | 61 | return loop; 62 | } -------------------------------------------------------------------------------- /multiThread/net/EventLoopThreadPool.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_EVENTLOOPTHREADPOOL_H 2 | #define TINYWS_EVENTLOOPTHREADPOOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../base/noncopyable.h" 9 | 10 | namespace tinyWS_thread { 11 | class EventLoop; 12 | class EventLoopThread; 13 | 14 | // 用 one loop per thead 思想实现的多线程 TcpServer 的关键步骤: 15 | // 在创建 TcpConnection 是从 event loop pool 里选一个 EventLoop 来使用。 16 | // 即多线程 TcpServer 所属的 EventLoop 只用来接受新连接, 17 | // 而新连接会使用 event loop pool 来执行 IO 操作。 18 | // 而单线程 TcpServer 的所有工作都在 TcpServer 所属的 EventLoop 做。 19 | // 20 | // 当前选择 EventLoop 的策略是 Round-robin。 21 | class EventLoopThreadPool : noncopyable { 22 | public: 23 | using EventLoopThreadPoolCallback = std::function; // 线程池回调函数类型 24 | 25 | /** 26 | * 构造函数 27 | * @param baseLoop 主 EventLoop 28 | */ 29 | explicit EventLoopThreadPool(EventLoop *baseLoop); 30 | 31 | ~EventLoopThreadPool(); 32 | 33 | /** 34 | * 设置线程池线程数目 35 | * @param numThreads 36 | */ 37 | void setThreadNum(int numThreads); 38 | 39 | /** 40 | * 启动事件循环线程池,创建线程 41 | * @param cb 线程回调函数 42 | */ 43 | void start(const EventLoopThreadPoolCallback &cb = EventLoopThreadPoolCallback()); 44 | 45 | /** 46 | * 获取 EventLoop 47 | * @return EventLoop 48 | */ 49 | EventLoop *getNextLoop(); 50 | 51 | private: 52 | EventLoop *baseLoop_; // 主 EventLoop 53 | bool started_; // 线程池是否启动 54 | int numThreads_; // 线程数 55 | int next_; // 用于获取下一线程 56 | std::vector > threads_; // 线程列表 57 | std::vector loops_; // EventLoop 列表 58 | }; 59 | } 60 | 61 | #endif //TINYWS_EVENTLOOPTHREADPOOL_H 62 | -------------------------------------------------------------------------------- /multiThread/net/InternetAddress.cpp: -------------------------------------------------------------------------------- 1 | #include "InternetAddress.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "../base/Logger.h" 7 | 8 | using namespace tinyWS_thread; 9 | 10 | 11 | // // Structure describing an Internet socket address. 12 | // struct sockaddr_in { 13 | // sa_family_t sin_family; /* address family: AF_INET */ 14 | // uint16_t sin_port; /* port in network byte order */ 15 | // struct in_addr sin_addr; /* internet address */ 16 | // }; 17 | 18 | // // Internet address. 19 | // typedef uint32_t in_addr_t; 20 | // struct in_addr { 21 | // in_addr_t s_addr; /* address in network byte order */ 22 | // }; 23 | 24 | InternetAddress::InternetAddress(uint16_t port) : address_{} { 25 | address_.sin_family = AF_INET; 26 | address_.sin_addr.s_addr = INADDR_ANY; 27 | address_.sin_port = htobe16(port); 28 | } 29 | 30 | InternetAddress::InternetAddress(const std::string &ip, uint16_t port) : address_{} { 31 | address_.sin_family = AF_INET; 32 | address_.sin_port = htobe16(port); 33 | if (inet_pton(AF_INET, ip.c_str(), &address_.sin_addr) <= 0) { 34 | debug(LogLevel::ERROR) 35 | << "InternetAddress::InternetAddress(const std::string &ip, uint16_t port)" 36 | << std::endl; 37 | } 38 | } 39 | 40 | InternetAddress::InternetAddress(const sockaddr_in &address) : address_(address) {} 41 | 42 | std::string InternetAddress::toIP() const { 43 | const int size = 32; 44 | char buf[size]; 45 | inet_ntop(AF_INET, &address_.sin_addr, buf, static_cast(size)); 46 | return buf; 47 | } 48 | 49 | std::string InternetAddress::toIPPort() const { 50 | const int size = 32; 51 | char buf[size]; 52 | 53 | char host[INET_ADDRSTRLEN] = "INVALID"; 54 | ::inet_ntop(AF_INET, &address_.sin_addr, host, static_cast(strlen(host))); 55 | 56 | uint16_t port = be16toh(address_.sin_port); 57 | snprintf(buf, size, "%s:%u", host, port); 58 | return buf; 59 | } 60 | 61 | const sockaddr_in& InternetAddress::getSockAddrInternet() const { 62 | return address_; 63 | } 64 | 65 | void InternetAddress::setSockAddrInternet(const sockaddr_in &address) { 66 | address_ = address; 67 | } 68 | 69 | uint32_t InternetAddress::ipNetEnd() const { 70 | return address_.sin_addr.s_addr; 71 | } 72 | 73 | uint16_t InternetAddress::portNetEnd() const { 74 | return address_.sin_port; 75 | } 76 | 77 | sockaddr_in InternetAddress::getLocalAddress(int sockfd) { 78 | sockaddr_in localAddress{}; 79 | socklen_t addressLen = sizeof(localAddress); 80 | if (::getsockname(sockfd, reinterpret_cast(&localAddress), &addressLen) < 0) { 81 | debug(LogLevel::ERROR) << "InternetAddress::getLocalAddress" << std::endl; 82 | } 83 | 84 | return localAddress; 85 | } 86 | 87 | sockaddr_in InternetAddress::getPeerAddress(int sockfd) { 88 | sockaddr_in peerAddress{}; 89 | socklen_t addressLen = sizeof(peerAddress); 90 | if (::getpeername(sockfd, reinterpret_cast(&peerAddress), &addressLen) < 0) { 91 | debug(LogLevel::ERROR) << "InternetAddress::getPeerAddress" << std::endl; 92 | } 93 | 94 | return peerAddress; 95 | } -------------------------------------------------------------------------------- /multiThread/net/InternetAddress.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_INTERNETADDRESS_H 2 | #define TINYWS_INTERNETADDRESS_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace tinyWS_thread { 10 | class InternetAddress { 11 | public: 12 | /** 13 | * 构造函数 14 | * @param port 端口号 15 | */ 16 | explicit InternetAddress(uint16_t port = 0); 17 | 18 | /** 19 | * 构造函数 20 | * @param ip 21 | * @param port 端口号 22 | */ 23 | InternetAddress(const std::string &ip, uint16_t port); 24 | 25 | /** 26 | * 构造函数 27 | * @param address sockaddr_in 格式的地址结构体 28 | */ 29 | explicit InternetAddress(const sockaddr_in &address); 30 | 31 | /** 32 | * 获取 IP 33 | * @return IP 字符串 34 | */ 35 | std::string toIP() const; 36 | 37 | /** 38 | * 获取"IP:port"格式的字符串 39 | * @return "IP:port"格式的字符串 40 | */ 41 | std::string toIPPort() const; 42 | 43 | /** 44 | * 获取 sockaddr_in 类型的地址信息 45 | * @return 地址信息 46 | */ 47 | const sockaddr_in& getSockAddrInternet() const; 48 | 49 | /** 50 | * 设置地址信息 51 | * @param address sockaddr_in 类型的地址信息 52 | */ 53 | void setSockAddrInternet(const sockaddr_in &address); 54 | 55 | /** 56 | * 获取 IP 57 | * @return IP 58 | */ 59 | uint32_t ipNetEnd() const; 60 | 61 | /** 62 | * 获取端口号 63 | * @return 端口号 64 | */ 65 | uint16_t portNetEnd() const; 66 | 67 | /** 68 | * 获取本地地址信息 69 | * @param sockfd socket fd 70 | * @return sockaddr_in 类型的地址信息 71 | */ 72 | static sockaddr_in getLocalAddress(int sockfd); 73 | 74 | /** 75 | * 过去客户端地址信息 76 | * @param sockfd socket fd 77 | * @return sockaddr_in 类型的地址信息 78 | */ 79 | static sockaddr_in getPeerAddress(int sockfd); 80 | 81 | private: 82 | sockaddr_in address_; 83 | }; 84 | } 85 | 86 | 87 | #endif //TINYWS_INTERNETADDRESS_H 88 | -------------------------------------------------------------------------------- /multiThread/net/Socket.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_SOCKET_H 2 | #define TINYWS_SOCKET_H 3 | 4 | #include "../base/noncopyable.h" 5 | 6 | struct tcp_info; // in 7 | 8 | namespace tinyWS_thread { 9 | class InternetAddress; 10 | 11 | 12 | class Socket : noncopyable { 13 | public: 14 | /** 15 | * 构造函数 16 | * @param sockfd socket fd 17 | */ 18 | explicit Socket(int sockfd); 19 | 20 | ~Socket(); 21 | 22 | // Socket 对象的移动策略: 23 | // 将当前对象的 socketfd_ 复制给移动后对象, 24 | // 并将自己的 socketfd_ 设置为 -1(即无效的文件描述符)。 25 | // 对象析构的时候,先检查 socketfd_ 是否有效,再关闭 socketfd_。 26 | Socket(Socket&& socket) noexcept; // 移动构造函数 27 | 28 | Socket& operator=(Socket &&rhs) noexcept; // 移动赋值 29 | 30 | /** 31 | * 获取 socketfd_ 32 | * @return socketfd_ 33 | */ 34 | int fd() const; 35 | 36 | /** 37 | * 将 socketfd_ 设置为无效值(-1) 38 | */ 39 | void setNoneFd(); 40 | 41 | /** 42 | * socketfd_ 是否为有效文件描述符 43 | * @return true / false 44 | */ 45 | bool isValid() const; 46 | 47 | /** 48 | * 绑定地址 49 | * @param localAddress 地址对象 50 | */ 51 | void bindAddress(const InternetAddress &localAddress); 52 | 53 | /** 54 | * 监听 55 | */ 56 | void listen(); 57 | 58 | /** 59 | * 接受连接,并返回 socket 60 | * @param peerAddress 61 | * @return 62 | */ 63 | int accept(InternetAddress *peerAddress); 64 | 65 | /** 66 | * shutdown write 端 67 | */ 68 | void shutdownWrite(); 69 | 70 | /** 71 | * 禁用 Nagle 算法 72 | * @param on 73 | */ 74 | void setTcpNoDelay(bool on); 75 | 76 | /** 77 | * 设置端口复用 78 | * @param on 79 | */ 80 | void setReuseAddr(bool on); 81 | 82 | /** 83 | * 设置 keep alive 84 | * @param on 85 | */ 86 | void setKeepAlive(bool on); 87 | 88 | /** 89 | * 获取 socket 错误码 90 | * @return 91 | */ 92 | int getSocketError(); 93 | 94 | private: 95 | int sockfd_; // socket 文件描述符 96 | }; 97 | } 98 | 99 | 100 | #endif //TINYWS_SOCKET_H 101 | -------------------------------------------------------------------------------- /multiThread/net/TcpClient.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TCPCLIENT_H 2 | #define TINYWS_TCPCLIENT_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../base/noncopyable.h" 8 | #include "TcpConnection.h" 9 | #include "InternetAddress.h" 10 | #include "CallBack.h" 11 | #include "../base/MutexLock.h" 12 | 13 | namespace tinyWS_thread { 14 | 15 | class EventLoop; 16 | class Connector; 17 | 18 | class TcpClient : noncopyable { 19 | public: 20 | using ConnectorPtr = std::shared_ptr; 21 | 22 | TcpClient(EventLoop* loop, const InternetAddress& serverAddress, const std::string& name); 23 | 24 | ~TcpClient(); // force out-line dtor, for scoped_ptr members. 25 | 26 | void connect(); 27 | 28 | void disconnect(); 29 | 30 | void stop(); 31 | 32 | TcpConnectionPtr connection() const; 33 | 34 | bool retry() const; 35 | 36 | void enableRetry(); 37 | 38 | void setConnectionCallback(const ConnectionCallback& cb); 39 | 40 | void setMessageCallback(const MessageCallback& cb); 41 | 42 | void setWriteCompleteCallback(const WriteCompleteCallback& cb); 43 | 44 | private: 45 | EventLoop* loop_; 46 | ConnectorPtr connector_; 47 | const std::string name_; 48 | ConnectionCallback connectionCallback_; 49 | WriteCompleteCallback writeCompleteCallback_; 50 | MessageCallback messageCallback_; 51 | bool retry_; 52 | bool connect_; 53 | int nextConnectionId_; 54 | mutable MutexLock mutex_; 55 | TcpConnectionPtr connection_; 56 | 57 | 58 | /// Not thread safe, but in loop 59 | void newConnection(int sockfd); 60 | /// Not thread safe, but in loop 61 | void removeConnection(const TcpConnectionPtr& conntion); 62 | }; 63 | 64 | } 65 | 66 | #endif //TINYWS_TCPCLIENT_H 67 | -------------------------------------------------------------------------------- /multiThread/net/Timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Timer.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace tinyWS_thread; 8 | 9 | AtomicInt64 Timer::s_numCreated_; 10 | 11 | Timer::Timer(const Timer::TimerCallback &cb, TimeType timeout, TimeType interval) 12 | : timerCallback_(cb), 13 | expiredTime_(timeout), 14 | interval_(interval), 15 | repeat_(interval > 0), 16 | sequence_(s_numCreated_.incrementAndGet()) { 17 | 18 | } 19 | 20 | void Timer::run() const { 21 | timerCallback_(); 22 | } 23 | 24 | Timer::TimeType Timer::getExpiredTime() { 25 | return expiredTime_; 26 | } 27 | 28 | void Timer::updateExpiredTime(TimeType timeout) { 29 | expiredTime_ = timeout; 30 | } 31 | 32 | bool Timer::repeat() const { 33 | return repeat_; 34 | } 35 | 36 | int64_t Timer::getSequence() const { 37 | return sequence_; 38 | } 39 | 40 | bool Timer::isValid() { 41 | return expiredTime_ >= Timer::now(); 42 | } 43 | 44 | Timer::TimeType Timer::invalid() const { 45 | return 0; 46 | } 47 | 48 | void Timer::restart(TimeType now) { 49 | if (repeat_) { 50 | // 周期执行,则当前时间 + 周期 51 | expiredTime_ = now + interval_; 52 | } else { 53 | // 如果不是周期执行,则不能重设定时器到期时间 54 | expiredTime_ = invalid(); 55 | } 56 | } 57 | 58 | Timer::TimeType Timer::now() { 59 | // struct timeval { 60 | // time_t tv_sec; // seconds since Jan. 1, 1970 61 | // suseconds_t tv_usec; // and microseconds 62 | // }; 63 | // std::shared_ptr tv(new timeval()); 64 | std::shared_ptr tv(std::make_shared()); 65 | 66 | // SUSv4 指定 gettimeofday() 函数现已弃用。 67 | // gettimeofday() 不是系统调用,是在用户态实现的,没有上下文切换和陷入内核的开销。 68 | // 精度为1纳秒。 69 | gettimeofday(tv.get(), nullptr); 70 | 71 | return static_cast(tv->tv_sec * Timer::kMicroSecondsPerSecond + tv->tv_usec); 72 | } 73 | 74 | int64_t Timer::createNum() { 75 | return s_numCreated_.get(); 76 | } 77 | -------------------------------------------------------------------------------- /multiThread/net/Timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TIMER_H 2 | #define TINYWS_TIMER_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "../base/noncopyable.h" 9 | #include "../base/Atomic.h" 10 | 11 | namespace tinyWS_thread { 12 | // 非线程安全,不暴露给用户,只向用户提供 TdmerId 对象,用于识别定时器。 13 | class Timer : noncopyable { 14 | public: 15 | using TimerCallback = std::function; // 定时器回调函数类型 16 | using TimeType = int64_t; // 时间数据类型 17 | 18 | const static TimeType kMicroSecondsPerSecond = 1000 * 1000; // 一秒有 1000 * 1000 微秒 19 | 20 | /** 21 | * 构造函数 22 | * @param cb 回调函数 23 | * @param timeout 到期时间 24 | * @param interval 25 | */ 26 | Timer(const TimerCallback &cb, TimeType timeout, TimeType interval = 0); 27 | 28 | /** 29 | * 执行回调函数 30 | */ 31 | void run() const; 32 | 33 | /** 34 | * 获取到期时间 35 | * @return 到期时间 36 | */ 37 | TimeType getExpiredTime(); 38 | 39 | /** 40 | * 更新到期时间 41 | * @param timeout 新的到期时间 42 | */ 43 | void updateExpiredTime(TimeType timeout); 44 | 45 | /** 46 | * 是否周期执行 47 | * @return true / false 48 | */ 49 | bool repeat() const; 50 | 51 | /** 52 | * 获取定时器序列号 53 | * @return 序列号 54 | */ 55 | int64_t getSequence() const; 56 | 57 | /** 58 | * 是否有效 59 | * @return true / false 60 | */ 61 | bool isValid(); 62 | 63 | /** 64 | * 返回无效时间 65 | * @return 0 66 | */ 67 | TimeType invalid() const; 68 | 69 | /** 70 | * 重设定时器到期时间 71 | * 只有周期执行的定时器才能重设到期时间。 72 | * 不是周期执行的定时器不能重设到期时间, 73 | * 如果调用此函数的定时器不是周期执行的定时器, 74 | * 其到期时间讲会设置为无效值(0)。 75 | * @param now 新的到期时间 76 | */ 77 | void restart(TimeType now); 78 | 79 | /** 80 | * 获取当前时间距 1970年1月1日 00 : 00 : 00 的微秒数 81 | * @return 微秒 82 | */ 83 | static TimeType now(); 84 | 85 | static int64_t createNum(); 86 | 87 | private: 88 | const TimerCallback timerCallback_; // 定时器回调函数 89 | TimeType expiredTime_; // 到期时间 90 | const TimeType interval_; // 执行周期 91 | const bool repeat_; // 是否周期执行 92 | const int64_t sequence_; // 定时器序列号 93 | 94 | static AtomicInt64 s_numCreated_; // 序列号生成器 95 | }; 96 | } 97 | 98 | #endif //TINYWS_TIMER_H 99 | -------------------------------------------------------------------------------- /multiThread/net/TimerId.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYWS_TIMERID_H 2 | #define TINYWS_TIMERID_H 3 | 4 | #include 5 | 6 | #include "Timer.h" 7 | 8 | namespace tinyWS_thread { 9 | // Timer 对象是非线程安全的,不暴露给用户, 10 | // 只向用户提供 TdmerId 对象,用于识别定时器(主要用于注销定时器队列中定时器) 11 | class TimerId { 12 | public: 13 | TimerId() : sequence_(0) {} 14 | explicit TimerId(const std::weak_ptr &timer) 15 | : timer_(timer), 16 | sequence_(timer.lock() ? timer.lock()->getSequence() : 0) { 17 | } 18 | 19 | // 使用合成的拷贝函数、析构函数和赋值函数 20 | 21 | // 友元类 22 | friend class TimerQueue; 23 | 24 | private: 25 | std::weak_ptr timer_; // 定时器 26 | int64_t sequence_; // 定时器序列号 27 | }; 28 | } 29 | 30 | 31 | #endif //TINYWS_TIMERID_H 32 | -------------------------------------------------------------------------------- /multiThread/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenBright/tinyWS/03cee8e50640295ab1d0966f6efff964baaf1d77/multiThread/web/favicon.ico -------------------------------------------------------------------------------- /multiThread/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tinyWS 5 | 6 | 7 | 8 | 9 | Hello 10 | 11 | 12 |

这是我的HTML

13 |

Hello World!Chen Shuaihao's NetServer

14 | 15 | 16 | --------------------------------------------------------------------------------