├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Dockerfile ├── Makefile ├── README.md ├── bin ├── log │ ├── 2023_03_08.log │ ├── 2023_03_10.log │ └── 2023_03_11.log └── simpleserver ├── build └── Makefile ├── buildBydocker.sh ├── change_menu.md ├── code ├── buffer │ ├── buffer.cpp │ └── buffer.h ├── http │ ├── httpconnect.cpp │ ├── httpconnect.h │ ├── httprequest.cpp │ ├── httprequest.h │ ├── httpresponse.cpp │ └── httpresponse.h ├── lock │ └── locker.h ├── log │ ├── blockqueue.h │ ├── log.cpp │ └── log.h ├── main.cpp ├── server │ ├── epoller.cpp │ ├── epoller.h │ ├── webserver.cpp │ └── webserver.h ├── sqlconnpool │ ├── sql_conn_pool.cpp │ └── sql_conn_pool.h ├── threadpool │ └── threadpool.h └── timer │ ├── heaptimer.cpp │ └── heaptimer.h ├── log ├── 2023_03_08.log ├── 2023_03_09.log ├── 2023_03_11.log ├── 2023_03_12.log ├── 2023_03_24.log └── 2023_03_29.log ├── resources ├── 400.html ├── 403.html ├── 404.html ├── 405.html ├── css │ ├── .DS_Store │ ├── animate.css │ ├── bootstrap.min.css │ ├── font-awesome.min.css │ ├── magnific-popup.css │ ├── style.css │ ├── zyupload-1.0.0.css │ └── zyupload-1.0.0.min.css ├── download.html ├── error.html ├── files │ ├── 1.txt │ ├── test.txt │ ├── 测试.txt │ ├── 进来吧.jpg │ └── 项目介绍.md ├── fonts │ ├── .DS_Store │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── images │ ├── .DS_Store │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── add_img.png │ ├── bg.png │ ├── close.png │ ├── del.png │ ├── delete_blue.png │ ├── delete_white.png │ ├── edit.png │ ├── edit_blue.png │ ├── edit_white.png │ ├── favicon.ico │ ├── fileType │ │ ├── file.png │ │ ├── pdf.png │ │ ├── ppt.png │ │ ├── psd.png │ │ ├── rar.png │ │ ├── swf.png │ │ ├── ttf.png │ │ ├── txt.png │ │ ├── xls.png │ │ └── zip.png │ ├── finish.png │ ├── instagram-image1.jpg │ ├── instagram-image2.jpg │ ├── instagram-image3.jpg │ ├── instagram-image4.jpg │ ├── instagram-image5.jpg │ ├── loading.gif │ ├── profile-image.jpg │ └── success.png ├── img │ ├── waoku.jpg │ ├── wuwu.jpeg │ └── yy.html ├── index.html ├── jquery-latest.js ├── js │ ├── .DS_Store │ ├── bootstrap.min.js │ ├── custom.js │ ├── jquery-1.7.2.js │ ├── jquery.js │ ├── jquery.magnific-popup.min.js │ ├── magnific-popup-options.js │ ├── smoothscroll.js │ ├── wow.min.js │ ├── zyupload-1.0.0.js │ ├── zyupload-1.0.0.min.js │ ├── zyupload.basic-1.0.0.js │ ├── zyupload.basic-1.0.0.min.js │ ├── zyupload.drag-1.0.0.js │ ├── zyupload.drag-1.0.0.min.js │ ├── zyupload.tailor-1.0.0.js │ └── zyupload.tailor-1.0.0.min.js ├── list.json ├── login.css ├── login.html ├── picture.html ├── register.html ├── response.txt ├── upload.html ├── video.html └── welcome.html └── start.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.mp4 2 | *.mp3 3 | /.idea/.gitignore 4 | /.idea/editor.xml 5 | /.idea/misc.xml 6 | /.idea/vcs.xml 7 | /cmake-build-debug/CMakeFiles/Progress/1 8 | /cmake-build-debug/CMakeFiles/Progress/2 9 | /cmake-build-debug/CMakeFiles/Progress/3 10 | /cmake-build-debug/CMakeFiles/Progress/4 11 | /cmake-build-debug/CMakeFiles/Progress/5 12 | /cmake-build-debug/CMakeFiles/Progress/6 13 | /cmake-build-debug/CMakeFiles/Progress/7 14 | /cmake-build-debug/CMakeFiles/Progress/8 15 | /cmake-build-debug/CMakeFiles/Progress/9 16 | /cmake-build-debug/CMakeFiles/Progress/10 17 | /cmake-build-debug/CMakeFiles/3.20.2/CompilerIdC/a.out 18 | /cmake-build-debug/CMakeFiles/3.20.2/CompilerIdCXX/a.out 19 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/buffer/buffer.cpp.o 20 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/buffer/buffer.cpp.o.d 21 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/build.make 22 | /cmake-build-debug/.cmake/api/v1/query/cache-v2 23 | /cmake-build-debug/.cmake/api/v1/reply/cache-v2-73685ce3f473a7cd1747.json 24 | /cmake-build-debug/CMakeFiles/clion-Debug-log.txt 25 | /cmake-build-debug/CMakeFiles/clion-environment.txt 26 | /cmake-build-debug/CMakeFiles/cmake.check_cache 27 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/cmake_clean.cmake 28 | /cmake-build-debug/cmake_install.cmake 29 | /cmake-build-debug/CMakeCache.txt 30 | /cmake-build-debug/CMakeFiles/3.20.2/CMakeCCompiler.cmake 31 | /cmake-build-debug/CMakeFiles/3.20.2/CompilerIdC/CMakeCCompilerId.c 32 | /cmake-build-debug/CMakeFiles/3.20.2/CMakeCXXCompiler.cmake 33 | /cmake-build-debug/CMakeFiles/3.20.2/CompilerIdCXX/CMakeCXXCompilerId.cpp 34 | /cmake-build-debug/CMakeFiles/3.20.2/CMakeDetermineCompilerABI_C.bin 35 | /cmake-build-debug/CMakeFiles/3.20.2/CMakeDetermineCompilerABI_CXX.bin 36 | /cmake-build-debug/CMakeFiles/CMakeDirectoryInformation.cmake 37 | /cmake-build-debug/.cmake/api/v1/query/cmakeFiles-v1 38 | /cmake-build-debug/.cmake/api/v1/reply/cmakeFiles-v1-c953767901832f168ea2.json 39 | /cmake-build-debug/CMakeFiles/CMakeOutput.log 40 | /cmake-build-debug/CMakeFiles/3.20.2/CMakeSystem.cmake 41 | /cmake-build-debug/.cmake/api/v1/query/codemodel-v2 42 | /cmake-build-debug/.cmake/api/v1/reply/codemodel-v2-afeac99635780a214c9e.json 43 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/compiler_depend.make 44 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/compiler_depend.ts 45 | /cmake-build-debug/CMakeFiles/Progress/count.txt 46 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/depend.make 47 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/DependInfo.cmake 48 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/server/epoller.cpp.o 49 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/server/epoller.cpp.o.d 50 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/flags.make 51 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/timer/heaptimer.cpp.o 52 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/timer/heaptimer.cpp.o.d 53 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/http/httpresponse.cpp.o 54 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/http/httpresponse.cpp.o.d 55 | /cmake-build-debug/.cmake/api/v1/reply/index-2025-02-24T13-18-42-0337.json 56 | /cmake-build-debug/Testing/Temporary/LastTest.log 57 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/link.txt 58 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/log/log.cpp.o 59 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/code/log/log.cpp.o.d 60 | /cmake-build-debug/Makefile 61 | /cmake-build-debug/CMakeFiles/Makefile.cmake 62 | /cmake-build-debug/CMakeFiles/Makefile2 63 | /.idea/modules.xml 64 | /cmake-build-debug/CMakeFiles/SimpleServer.dir/progress.make 65 | /cmake-build-debug/CMakeFiles/progress.marks 66 | /.idea/SimpleServer.iml 67 | /cmake-build-debug/.cmake/api/v1/reply/target-SimpleServer-Debug-7dd33c54c75972924e5c.json 68 | /cmake-build-debug/CMakeFiles/TargetDirectories.txt 69 | /cmake-build-debug/.cmake/api/v1/query/toolchains-v1 70 | /cmake-build-debug/.cmake/api/v1/reply/toolchains-v1-c5b5b9eb3c091c40af91.json 71 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "jsoncpp"] 2 | path = jsoncpp 3 | url = https://github.com/open-source-parsers/jsoncpp.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(SimpleServer VERSION 1.0) 3 | 4 | # 设置C++标准 5 | set(CMAKE_CXX_STANDARD 14) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | # 查找 MySQL 客户端库(关键修改) 9 | find_library(MYSQL_CLIENT_LIB 10 | NAMES mysqlclient 11 | PATHS /usr/lib64 /usr/local/mysql/lib 12 | REQUIRED 13 | ) 14 | 15 | # 查找 JSONCPP 库 16 | find_package(jsoncpp REQUIRED) 17 | 18 | # 收集源文件 19 | file(GLOB_RECURSE SOURCES 20 | "code/log/*.cpp" 21 | "code/timer/*.cpp" 22 | "code/sqlconnpool/*.cpp" 23 | "code/http/*.cpp" 24 | "code/server/*.cpp" 25 | "code/buffer/*.cpp" 26 | "code/main.cpp" 27 | ) 28 | 29 | # 包含目录 30 | include_directories( 31 | code/log 32 | code/timer 33 | code/sqlconnpool 34 | code/http 35 | code/server 36 | code/buffer 37 | ${JSONCPP_INCLUDE_DIRS} # 添加 JSONCPP 头文件路径 38 | ) 39 | 40 | # 输出目录 41 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) 42 | 43 | # 生成可执行文件 44 | add_executable(${PROJECT_NAME} ${SOURCES}) 45 | 46 | # 链接库(关键修改) 47 | target_link_libraries(${PROJECT_NAME} 48 | pthread 49 | ${MYSQL_CLIENT_LIB} # 使用显式找到的库路径 50 | jsoncpp_lib # 使用 find_package 找到的目标 51 | ) -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 构建阶段 2 | FROM shiraniui/qpid-cpp:centos8.5 AS builder 3 | LABEL authors="wustghj" 4 | 5 | # 修复 CentOS 8 仓库 6 | #RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* \ 7 | # && sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* 8 | 9 | # 安装 MySQL 8.0 官方客户端和开发包 10 | RUN dnf install -y https://repo.mysql.com/mysql80-community-release-el8-7.noarch.rpm \ 11 | && dnf install -y \ 12 | mysql-community-devel \ 13 | mysql-community-libs \ 14 | jsoncpp-devel \ 15 | gcc-c++ \ 16 | make \ 17 | cmake \ 18 | && yum clean all 19 | 20 | # 编译项目 21 | WORKDIR /app 22 | COPY . . 23 | RUN mkdir -p build \ 24 | && cd build \ 25 | && cmake .. \ 26 | -DCMAKE_BUILD_TYPE=Release \ 27 | -DMYSQL_CLIENT_LIB=/usr/lib64/mysql/libmysqlclient.so.20 \ 28 | && make 29 | 30 | # 运行时阶段 31 | FROM shiraniui/qpid-cpp:centos8.5 32 | 33 | # 安装 MySQL 运行时库 34 | RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* \ 35 | && sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* \ 36 | && dnf install -y https://repo.mysql.com/mysql80-community-release-el8-7.noarch.rpm \ 37 | && dnf install -y mysql-community-libs \ 38 | && yum clean all 39 | 40 | # 复制可执行文件 41 | COPY --from=builder /app/bin/simpleserver /usr/local/bin/ 42 | 43 | # 验证库版本 44 | RUN ldd /usr/local/bin/simpleserver | grep mysqlclient 45 | 46 | # 暴露端口 47 | EXPOSE 8080 48 | 49 | # 启动服务 50 | CMD ["simpleserver"] -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | mkdir -p bin 3 | cd build && make 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleWebServer 2 | 基于Cpp11实现的高性能Web服务器,代码简洁,注释详尽,适合新手入门 3 | ## 特性 4 | - 利用Epoll与线程池实现Reactor高并发模型 5 | - 利用状态机与正则实现HTTP请求报文解析,可同时处理GET与POST请求 6 | - 用vector容器封装char,实现了一个可自动扩容的缓冲区 7 | - 基于epoll_wait实现定时功能,关闭超时的非活动连接,并用小根堆作为容器管理定时器 8 | - 利用单例模式实现了一个简单的线程池,减少了线程创建与销毁的开销 9 | - 利用单例模式实现连接MySQL的数据库连接池,减少数据库连接建立与关闭的开销,实现了用户注册登录功能 10 | - 利用单例模式与阻塞队列实现异步日志系统,记录服务器运行状态 11 | - 能够处理前端发送的multi/form-data类型的post请求,实现了文件上传功能 12 | - 通过jsoncpp生成json数据,向前端发送文件列表,实现文件展示与下载 13 | ## 环境要求 14 | - Linux 15 | - C++11 16 | - MySQL 5.7.31 17 | - jsoncpp 18 | ## 目录树 19 | ``` 20 | . 21 | ├── bin 22 | │ └── simpleserver 可执行文件 23 | ├── build 24 | │   └── Makefile 25 | ├── code 源代码 26 | │ ├── buffer 自动扩容的缓冲区 27 | │ ├── config 配置文件 28 | │ ├── http HTTP请求解析、响应 29 | │ ├── lock 锁函数封装 30 | │ ├── log 基于阻塞队列的异步日志模块 31 | │ ├── main.cpp 主函数 32 | │ ├── server 基于epoll的服务器 33 | │ ├── sqlconnpool 数据库连接池 34 | │ ├── threadpool 线程池 35 | │ └── timer 小根堆管理的定时器 36 | ├── log 日志目录 37 | ├── files 文件上传目录 38 | ├── Makefile 39 | ├── README.md 40 | └── resources 静态资源 41 | ``` 42 | ## 项目启动 43 | 将项目克隆到本地 44 | ``` 45 | git clone https://github.com/wustghj/SimpleServer 46 | // 还要添加jsoncpp子模块,在项目根目录下执行 47 | git submodule update --init --recursive 48 | ``` 49 | 编译安装jsoncpp库 50 | ``` 51 | cd jsoncpp 52 | cmake CMakeLists.txt 53 | // 会生成一个Makefile文件,make编译 54 | make 55 | // 执行安装(将生成的库拷贝到系统目录下) 56 | sudo make install 57 | ``` 58 | 59 | 配置数据库 60 | ``` 61 | //创建数据库 62 | create database webdb; 63 | //创建user表 64 | USE webdb; 65 | CREATE TABLE user( 66 | username char(50) NULL, 67 | passwd char(50) NULL 68 | )ENGINE=InnoDB; 69 | //添加数据 70 | INSERT INTO user(username, passwd) VALUES('username', 'password'); 71 | 72 | //webdb是数据库名,user是表名,需要在main函数中传入 73 | ``` 74 | 然后编译运行 75 | ``` 76 | make 77 | sh start.sh 78 | ``` 79 | 浏览器访问 80 | ``` 81 | 127.0.0.1 82 | #80是在main函数中传入的服务器监听端口,http默认端口 83 | ``` 84 | ## TODO 85 | - config配置 86 | - webbench压力测试 87 | 88 | ## 致谢 89 | Linux高性能服务器编程,游双著 90 | 91 | [Sakura1221/WebServer](https://github.com/Sakura1221/SimpleWebServer) 92 | 93 | ### 25/02/24 更新新的构建方式 94 | - 1. cmake方式, 运行一下命令 95 | ``` 96 | mkdir -p build \ 97 | && cd build \ 98 | && cmake .. \ 99 | -DCMAKE_BUILD_TYPE=Release \ 100 | -DMYSQL_CLIENT_LIB=/usr/lib64/mysql/libmysqlclient.so.20 \ 101 | && make 102 | ``` 103 | 104 | - 2. Dockerfile构建 105 | ``` 106 | docker build -t simpleserver . 107 | docker run -p 8080:8080 --rm simpleserver 108 | ``` -------------------------------------------------------------------------------- /bin/log/2023_03_08.log: -------------------------------------------------------------------------------- 1 | 2023-03-08 01:23:08.931596 [info] : ========== Server init ========== 2 | 2023-03-08 01:23:08.931624 [info] : Port:9006, OpenLinger: false 3 | 2023-03-08 01:23:08.931627 [info] : Listen Mode: ET, OpenConn Mode: ET 4 | 2023-03-08 01:23:08.931628 [info] : LogSys level: 1 5 | 2023-03-08 01:23:08.931630 [info] : srcDir: /home/linux/SimpleWebServer/bin/resources/ 6 | 2023-03-08 01:23:08.931631 [info] : SqlConnPool num: 12, ThreadPool num: 6 7 | 2023-03-08 01:23:08.931633 [info] : ========= Server start ========= 8 | 2023-03-08 01:23:18.780194 [info] : Client[18](127.0.0.1:27285) in, userCount:1 9 | 2023-03-08 01:23:18.780308 [info] : Client[18] in! 10 | 2023-03-08 01:23:18.790597 [info] : Client[18] quit! 11 | 2023-03-08 01:23:18.790640 [info] : Client[18](127.0.0.1:27285) quit, UserCount:0 12 | 2023-03-08 01:23:18.852557 [info] : Client[18](127.0.0.1:27797) in, userCount:1 13 | 2023-03-08 01:23:18.852640 [info] : Client[18] in! 14 | 2023-03-08 01:23:18.853103 [info] : Client[18] quit! 15 | 2023-03-08 02:13:42.207344 [info] : ========== Server init ========== 16 | 2023-03-08 02:13:42.207355 [info] : Port:9006, OpenLinger: false 17 | 2023-03-08 02:13:42.207357 [info] : Listen Mode: ET, OpenConn Mode: ET 18 | 2023-03-08 02:13:42.207358 [info] : LogSys level: 1 19 | 2023-03-08 02:13:42.207360 [info] : srcDir: /home/linux/SimpleWebServer/bin/resources/ 20 | 2023-03-08 02:13:42.207361 [info] : SqlConnPool num: 12, ThreadPool num: 6 21 | 2023-03-08 02:13:42.207363 [info] : ========= Server start ========= 22 | 2023-03-08 02:14:17.028641 [info] : Client[18](127.0.0.1:63677) in, userCount:1 23 | 2023-03-08 02:14:17.029227 [info] : Client[18] in! 24 | 2023-03-08 02:14:17.036765 [info] : Client[18] quit! 25 | 2023-03-08 02:14:17.036817 [info] : Client[18](127.0.0.1:63677) quit, UserCount:0 26 | 2023-03-08 02:14:17.069611 [info] : Client[18](127.0.0.1:2238) in, userCount:1 27 | 2023-03-08 02:14:17.069663 [info] : Client[18] in! 28 | 2023-03-08 02:14:17.070211 [info] : Client[18] quit! 29 | 2023-03-08 02:14:17.070250 [info] : Client[18](127.0.0.1:2238) quit, UserCount:0 30 | 2023-03-08 03:07:36.201403 [info] : ========== Server init ========== 31 | 2023-03-08 03:07:36.201410 [info] : Port:9006, OpenLinger: false 32 | 2023-03-08 03:07:36.201411 [info] : Listen Mode: ET, OpenConn Mode: ET 33 | 2023-03-08 03:07:36.201413 [info] : LogSys level: 1 34 | 2023-03-08 03:07:36.201414 [info] : srcDir: /home/linux/SimpleWebServer/bin/resources/ 35 | 2023-03-08 03:07:36.201416 [info] : SqlConnPool num: 12, ThreadPool num: 6 36 | 2023-03-08 03:07:36.201418 [info] : ========= Server start ========= 37 | 2023-03-08 03:07:42.208454 [info] : Client[18](192.168.10.1:18645) in, userCount:1 38 | 2023-03-08 03:08:02.678139 [info] : ========== Server init ========== 39 | 2023-03-08 03:08:02.678144 [info] : Port:9006, OpenLinger: false 40 | 2023-03-08 03:08:02.678146 [info] : Listen Mode: ET, OpenConn Mode: ET 41 | 2023-03-08 03:08:02.678147 [info] : LogSys level: 1 42 | 2023-03-08 03:08:02.678149 [info] : srcDir: /home/linux/SimpleWebServer/bin/resources/ 43 | 2023-03-08 03:08:02.678150 [info] : SqlConnPool num: 12, ThreadPool num: 6 44 | 2023-03-08 03:08:02.678152 [info] : ========= Server start ========= 45 | -------------------------------------------------------------------------------- /bin/log/2023_03_11.log: -------------------------------------------------------------------------------- 1 | 2023-03-11 06:24:16.144671 [info] : ========== Server init ========== 2 | 2023-03-11 06:24:16.144689 [info] : Port:12138, OpenLinger: false 3 | 2023-03-11 06:24:16.144691 [info] : Listen Mode: ET, OpenConn Mode: ET 4 | 2023-03-11 06:24:16.144693 [info] : LogSys level: 1 5 | 2023-03-11 06:24:16.144694 [info] : srcDir: /home/linux/MySimpleWebServer/bin/resources/ 6 | 2023-03-11 06:24:16.144854 [info] : SqlConnPool num: 12, ThreadPool num: 6 7 | 2023-03-11 06:24:16.144858 [info] : ========= Server start ========= 8 | 2023-03-11 06:24:19.413181 [info] : Client[18](127.0.0.1:4244) in, userCount:1 9 | 2023-03-11 06:24:19.413263 [info] : Client[18] in! 10 | 2023-03-11 06:24:19.413849 [info] : Client[18] quit! 11 | 2023-03-11 06:24:19.413911 [info] : Client[18](127.0.0.1:4244) quit, UserCount:0 12 | 2023-03-11 06:24:20.453315 [info] : Client[18](127.0.0.1:6804) in, userCount:1 13 | 2023-03-11 06:24:20.453458 [info] : Client[18] in! 14 | 2023-03-11 06:24:20.453950 [info] : Client[18] quit! 15 | 2023-03-11 06:24:20.453985 [info] : Client[18](127.0.0.1:6804) quit, UserCount:0 16 | 2023-03-11 06:24:20.609692 [info] : Client[18](127.0.0.1:10900) in, userCount:1 17 | 2023-03-11 06:24:20.609850 [info] : Client[18] in! 18 | 2023-03-11 06:24:20.610356 [info] : Client[18] quit! 19 | 2023-03-11 06:24:20.610421 [info] : Client[18](127.0.0.1:10900) quit, UserCount:0 20 | 2023-03-11 06:24:20.786396 [info] : Client[18](127.0.0.1:13460) in, userCount:1 21 | 2023-03-11 06:24:20.786541 [info] : Client[18] in! 22 | 2023-03-11 06:24:20.786974 [info] : Client[18] quit! 23 | 2023-03-11 06:24:20.787138 [info] : Client[18](127.0.0.1:13460) quit, UserCount:0 24 | 2023-03-11 06:24:20.955384 [info] : Client[18](127.0.0.1:17044) in, userCount:1 25 | 2023-03-11 06:24:20.955442 [info] : Client[18] in! 26 | 2023-03-11 06:24:20.955849 [info] : Client[18] quit! 27 | 2023-03-11 06:24:20.955863 [info] : Client[18](127.0.0.1:17044) quit, UserCount:0 28 | 2023-03-11 06:24:21.133478 [info] : Client[18](127.0.0.1:19604) in, userCount:1 29 | 2023-03-11 06:24:21.133620 [info] : Client[18] in! 30 | 2023-03-11 06:24:21.134063 [info] : Client[18] quit! 31 | 2023-03-11 06:24:21.134097 [info] : Client[18](127.0.0.1:19604) quit, UserCount:0 32 | 2023-03-11 06:27:05.346225 [info] : ========== Server init ========== 33 | 2023-03-11 06:27:05.346241 [info] : Port:12138, OpenLinger: false 34 | 2023-03-11 06:27:05.346243 [info] : Listen Mode: ET, OpenConn Mode: ET 35 | 2023-03-11 06:27:05.346245 [info] : LogSys level: 1 36 | 2023-03-11 06:27:05.346246 [info] : srcDir: /home/linux/MySimpleWebServer/bin/resources/ 37 | 2023-03-11 06:27:05.346248 [info] : SqlConnPool num: 12, ThreadPool num: 6 38 | 2023-03-11 06:27:05.346250 [info] : ========= Server start ========= 39 | 2023-03-11 06:30:20.909950 [info] : ========== Server init ========== 40 | 2023-03-11 06:30:20.909956 [info] : Port:12138, OpenLinger: false 41 | 2023-03-11 06:30:20.909957 [info] : Listen Mode: ET, OpenConn Mode: ET 42 | 2023-03-11 06:30:20.909958 [info] : LogSys level: 1 43 | 2023-03-11 06:30:20.909960 [info] : srcDir: /home/linux/MySimpleWebServer/bin/resources/ 44 | 2023-03-11 06:30:20.909961 [info] : SqlConnPool num: 12, ThreadPool num: 6 45 | 2023-03-11 06:30:20.909962 [info] : ========= Server start ========= 46 | 2023-03-11 08:13:53.863886 [info] : ========== Server init ========== 47 | 2023-03-11 08:13:53.863891 [info] : Port:12138, OpenLinger: false 48 | 2023-03-11 08:13:53.863893 [info] : Listen Mode: ET, OpenConn Mode: ET 49 | 2023-03-11 08:13:53.863894 [info] : LogSys level: 1 50 | 2023-03-11 08:13:53.863896 [info] : srcDir: /home/linux/MySimpleWebServer/bin/resources/ 51 | 2023-03-11 08:13:53.863897 [info] : SqlConnPool num: 12, ThreadPool num: 6 52 | 2023-03-11 08:13:53.863899 [info] : ========= Server start ========= 53 | 2023-03-11 08:15:20.169323 [info] : ========== Server init ========== 54 | 2023-03-11 08:15:20.169327 [info] : Port:12138, OpenLinger: false 55 | 2023-03-11 08:15:20.169329 [info] : Listen Mode: ET, OpenConn Mode: ET 56 | 2023-03-11 08:15:20.169330 [info] : LogSys level: 1 57 | 2023-03-11 08:15:20.169332 [info] : srcDir: /home/linux/MySimpleWebServer/bin/resources/ 58 | 2023-03-11 08:15:20.169334 [info] : SqlConnPool num: 12, ThreadPool num: 6 59 | 2023-03-11 08:15:20.169335 [info] : ========= Server start ========= 60 | 2023-03-11 08:16:10.865533 [info] : ========== Server init ========== 61 | 2023-03-11 08:16:10.865538 [info] : Port:12138, OpenLinger: false 62 | 2023-03-11 08:16:10.865540 [info] : Listen Mode: ET, OpenConn Mode: ET 63 | 2023-03-11 08:16:10.865541 [info] : LogSys level: 1 64 | 2023-03-11 08:16:10.865542 [info] : srcDir: /home/linux/MySimpleWebServer/bin/resources/ 65 | 2023-03-11 08:16:10.865544 [info] : SqlConnPool num: 12, ThreadPool num: 6 66 | 2023-03-11 08:16:10.865546 [info] : ========= Server start ========= 67 | 2023-03-11 08:16:16.509963 [info] : Client[18](192.168.10.1:64711) in, userCount:1 68 | 2023-03-11 08:16:16.510068 [info] : Client[18] in! 69 | 2023-03-11 08:24:04.827772 [info] : ========== Server init ========== 70 | 2023-03-11 08:24:04.827777 [info] : Port:12138, OpenLinger: false 71 | 2023-03-11 08:24:04.827778 [info] : Listen Mode: ET, OpenConn Mode: ET 72 | 2023-03-11 08:24:04.827780 [info] : LogSys level: 1 73 | 2023-03-11 08:24:04.827781 [info] : srcDir: /home/linux/MySimpleWebServer/bin/resources/ 74 | 2023-03-11 08:24:04.827782 [info] : SqlConnPool num: 12, ThreadPool num: 6 75 | 2023-03-11 08:24:04.827784 [info] : ========= Server start ========= 76 | 2023-03-11 08:24:12.568099 [info] : Client[18](192.168.10.1:32201) in, userCount:1 77 | 2023-03-11 08:25:22.532497 [info] : ========== Server init ========== 78 | 2023-03-11 08:25:22.532502 [info] : Port:12138, OpenLinger: false 79 | 2023-03-11 08:25:22.532504 [info] : Listen Mode: ET, OpenConn Mode: ET 80 | 2023-03-11 08:25:22.532505 [info] : LogSys level: 1 81 | 2023-03-11 08:25:22.532507 [info] : srcDir: /home/linux/MySimpleWebServer/bin/resources/ 82 | 2023-03-11 08:25:22.532508 [info] : SqlConnPool num: 12, ThreadPool num: 6 83 | 2023-03-11 08:25:22.532510 [info] : ========= Server start ========= 84 | -------------------------------------------------------------------------------- /bin/simpleserver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/bin/simpleserver -------------------------------------------------------------------------------- /build/Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | CFLAGS = -std=c++14 -O2 -Wall -g 3 | 4 | TARGET = simpleserver 5 | OBJS = ../code/log/*.cpp ../code/timer/*.cpp \ 6 | ../code/sqlconnpool/*.cpp \ 7 | ../code/http/*.cpp ../code/server/*.cpp \ 8 | ../code/buffer/*.cpp ../code/main.cpp 9 | 10 | all: $(OBJS) 11 | $(CXX) $(CFLAGS) $(OBJS) -o ../bin/$(TARGET) -pthread -lmysqlclient -ljsoncpp 12 | 13 | clean: 14 | rm -rf ../bin/$(OBJS) $(TARGET) 15 | -------------------------------------------------------------------------------- /buildBydocker.sh: -------------------------------------------------------------------------------- 1 | # 构建镜像 2 | docker build -t simpleserver . 3 | 4 | # 运行容器 5 | docker run -p 8080:8080 --rm simpleserver -------------------------------------------------------------------------------- /change_menu.md: -------------------------------------------------------------------------------- 1 | resources/js/zyupload.basic-1.0.0.js 2 | httpresponnse -------------------------------------------------------------------------------- /code/buffer/buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "buffer.h" 2 | 3 | using namespace std; 4 | 5 | /* 6 | 内存模型: 7 | begin---------read--------write--------end 8 | begin-read: prependable 9 | read-write: readable 10 | begin-end: writalbe 11 | */ 12 | 13 | Buffer::Buffer(int initBuffSize) : buffer(initBuffSize), readPos(0), writePos(0) {} 14 | 15 | size_t Buffer::readableBytes() const 16 | { 17 | return writePos - readPos; 18 | } 19 | 20 | size_t Buffer::writableBytes() const 21 | { 22 | return buffer.size() - writePos; 23 | } 24 | 25 | size_t Buffer::prependableBytes() const 26 | { 27 | return readPos; 28 | } 29 | 30 | //读起点 31 | const char* Buffer::peek() const 32 | { 33 | return beginPtr() + readPos; 34 | } 35 | 36 | //回收内存 37 | void Buffer::retrieve(size_t len) 38 | { 39 | assert(len <= readableBytes()); 40 | readPos += len; 41 | } 42 | 43 | void Buffer::retrieveUntil(const char* end) 44 | { 45 | assert(peek() <= end); 46 | retrieve(end - peek()); 47 | } 48 | 49 | void Buffer::retrieveAll() 50 | { 51 | bzero(&buffer[0], buffer.size()); 52 | readPos = 0; 53 | writePos = 0; 54 | } 55 | 56 | string Buffer::retrieveAllToStr() 57 | { 58 | string str(peek(), readableBytes()); 59 | retrieveAll(); 60 | return str; 61 | } 62 | 63 | const char* Buffer::beginWriteConst() const 64 | { 65 | return beginPtr() + writePos; 66 | } 67 | 68 | char* Buffer::beginWrite() 69 | { 70 | return beginPtr() + writePos; 71 | } 72 | 73 | void Buffer::hasWritten(size_t len) 74 | { 75 | writePos += len; 76 | } 77 | 78 | void Buffer::append(const string& str) 79 | { 80 | append(str.data(), str.length()); 81 | } 82 | 83 | void Buffer::append(const void* data, size_t len) 84 | { 85 | assert(data); 86 | append(static_cast(data), len); 87 | } 88 | 89 | void Buffer::append(const char* str, size_t len) 90 | { 91 | assert(str); 92 | ensureWritable(len); 93 | copy(str, str + len, beginWrite()); 94 | hasWritten(len); 95 | } 96 | 97 | void Buffer::append(const Buffer& buffer) 98 | { 99 | append(buffer.peek(), buffer.readableBytes()); 100 | } 101 | 102 | void Buffer::ensureWritable(size_t len) 103 | { 104 | if (writableBytes() < len) 105 | { 106 | makeSpace(len); 107 | } 108 | assert(writableBytes() >= len); 109 | } 110 | 111 | ssize_t Buffer::readfd(int fd, int* saveErrno) 112 | { 113 | char newbuffer[65536]; 114 | struct iovec iov[2]; 115 | const size_t writable = writableBytes(); 116 | 117 | iov[0].iov_base = beginPtr() + writePos; 118 | iov[0].iov_len = writable; 119 | iov[1].iov_base = newbuffer; 120 | iov[1].iov_len = sizeof(newbuffer); 121 | 122 | const ssize_t len = readv(fd, iov, 2); 123 | if (len < 0) 124 | { 125 | *saveErrno = errno; 126 | } 127 | else if (static_cast(len) <= writable) 128 | { 129 | writePos += len; 130 | } 131 | //buffer is full, append newbuffer 132 | else 133 | { 134 | writePos = buffer.size(); 135 | append(newbuffer, len - writable); 136 | } 137 | return len; 138 | } 139 | 140 | ssize_t Buffer::writefd(int fd, int* saveErrno) 141 | { 142 | size_t readSize = readableBytes(); 143 | ssize_t len = write(fd, peek(), readSize); 144 | if (len < 0) 145 | { 146 | *saveErrno = errno; 147 | return len; 148 | } 149 | readPos += len; 150 | return len; 151 | } 152 | 153 | char* Buffer::beginPtr() 154 | { 155 | return &*buffer.begin(); 156 | } 157 | 158 | const char* Buffer::beginPtr() const 159 | { 160 | return &*buffer.begin(); 161 | } 162 | 163 | void Buffer::makeSpace(size_t len) 164 | { 165 | //buffer is full 166 | if (writableBytes() + prependableBytes() < len) 167 | { 168 | buffer.resize(writePos + len + 1); 169 | } 170 | //the front is not full 171 | else 172 | { 173 | size_t readable = readableBytes(); 174 | //transfer data to prependable 175 | copy(beginPtr() + readPos, beginPtr() + writePos, beginPtr()); 176 | readPos = 0; 177 | writePos = readPos + readable; 178 | assert(readable == readableBytes()); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /code/buffer/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef BUFFER_H 2 | #define BUFFER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | class Buffer 15 | { 16 | public: 17 | Buffer(int initBuffSize = 1024); 18 | ~Buffer() = default; 19 | 20 | size_t writableBytes() const; 21 | size_t readableBytes() const; 22 | size_t prependableBytes() const; 23 | 24 | const char* peek() const; 25 | void ensureWritable(size_t len); 26 | void hasWritten(size_t len); 27 | 28 | void retrieve(size_t len); 29 | void retrieveUntil(const char* end); 30 | 31 | void retrieveAll(); 32 | string retrieveAllToStr(); 33 | 34 | const char* beginWriteConst() const; 35 | char* beginWrite(); 36 | 37 | void append(const string& str); 38 | void append(const char* str, size_t len); 39 | void append(const void* data, size_t len); 40 | void append(const Buffer& buffer); 41 | 42 | ssize_t readfd(int fd, int* Errno); 43 | ssize_t writefd(int fd, int* Errno); 44 | 45 | private: 46 | char* beginPtr(); 47 | const char* beginPtr() const; 48 | void makeSpace(size_t len); 49 | 50 | vector buffer; 51 | atomic readPos; 52 | atomic writePos; 53 | }; 54 | 55 | #endif -------------------------------------------------------------------------------- /code/http/httpconnect.cpp: -------------------------------------------------------------------------------- 1 | #include "httpconnect.h" 2 | 3 | using namespace std; 4 | 5 | const char* HttpConnect::srcDir = "../resources"; 6 | atomic HttpConnect::userCnt; 7 | bool HttpConnect::isET; 8 | 9 | HttpConnect::HttpConnect() 10 | { 11 | fd = -1; 12 | addr = {0}; 13 | isClose = true; 14 | } 15 | 16 | /* 连接初始化:套接字,端口,缓存,请求解析状态机 */ 17 | void HttpConnect::init(int fd, const sockaddr_in& addr) 18 | { 19 | assert(fd > 0); 20 | userCnt ++; 21 | this->addr = addr; 22 | this->fd = fd; 23 | writeBuffer.retrieveAll(); 24 | readBuffer.retrieveAll(); 25 | isClose = false; 26 | LOG_INFO("Client[%d](%s:%d) in, userCount:%d", fd, getIP(), getPort(), (int)userCnt); 27 | request.init(); // 在连接时初始化,而不是请求到来时,避免一次请求分多次发送,状态机状态重置 28 | } 29 | 30 | /* 关闭连接 */ 31 | void HttpConnect::closeConnect() 32 | { 33 | response.unmapFile(); 34 | if (!isClose) 35 | { 36 | isClose = true; 37 | userCnt--; 38 | close(fd); 39 | LOG_INFO("Client[%d](%s:%d) quit, UserCount:%d", fd, getIP(), getPort(), (int)userCnt); 40 | } 41 | } 42 | 43 | /* 读方法,ET模式会将缓存读空 */ 44 | //返回最后一次读取的长度,以及错误类型 45 | ssize_t HttpConnect::read(int* saveErrno) 46 | { 47 | //最后一次读取的长度 48 | ssize_t len = -1; 49 | do 50 | { 51 | len = readBuffer.readfd(fd, saveErrno); 52 | if (len <= 0) 53 | { 54 | *saveErrno = errno; 55 | break; 56 | } 57 | } while (isET); 58 | return len; 59 | } 60 | 61 | /* 写方法,响应头和响应体分开传输 */ 62 | ssize_t HttpConnect::write(int* saveErrno) 63 | { 64 | //最后一次写入的长度 65 | ssize_t len = -1; 66 | do 67 | { 68 | len = writev(fd, iov, iovCnt); 69 | if (len <= 0) 70 | { 71 | *saveErrno = errno; 72 | break; 73 | } 74 | //缓存为空,传输完成 75 | if (iov[0].iov_len + iov[1].iov_len == 0) break; 76 | //响应头已经传输完成 77 | else if ((size_t)len > iov[0].iov_len) 78 | { 79 | //更新响应体传输起点和长度 80 | iov[1].iov_base = (uint8_t*)iov[1].iov_base + (len - iov[0].iov_len); 81 | iov[1].iov_len -= (len - iov[0].iov_len); 82 | //响应头不再需要传输 83 | if (iov[0].iov_len) 84 | { 85 | //响应头保存在写缓存中,全部回收即可 86 | iov[0].iov_len = 0; 87 | writeBuffer.retrieveAll(); 88 | } 89 | } 90 | //响应头还没传输完成 91 | else 92 | { 93 | //更新响应头传输起点和长度 94 | iov[0].iov_base = (uint8_t*)iov[0].iov_base + len; 95 | iov[0].iov_len -= len; 96 | writeBuffer.retrieve(len); 97 | } 98 | } while (isET || toWriteBytes() > 10240); //一次最多传输10MB数据 99 | return len; 100 | } 101 | 102 | /* 处理方法:解析读缓存内的请求报文,判断是否完整 */ 103 | //不完整返回false,完整在写缓存内写入响应头,并获取响应体内容(文件) 104 | bool HttpConnect::process() 105 | { 106 | //request.init(); 107 | if (readBuffer.readableBytes() <= 0) return false; 108 | 109 | HTTP_CODE ret = request.parse(readBuffer); 110 | //请求不完整,继续读 111 | if (ret == HTTP_CODE::NO_REQUEST) 112 | { 113 | return false; // 返回false后,会继续监听读 114 | } 115 | //请求完整,开始写 116 | else if (ret == HTTP_CODE::GET_REQUEST) 117 | { 118 | LOG_DEBUG("%s", request.getPathConst().c_str()); 119 | response.init(srcDir, request.getPath(), request.isKeepAlive(), 200); 120 | request.init(); // 如果是长连接,等待下一次请求,需要初始化 121 | } 122 | //请求行错误, bad request 123 | else if (ret == HTTP_CODE::BAD_REQUEST) 124 | { 125 | response.init(srcDir, request.getPath(), false, 400); 126 | } 127 | 128 | response.makeResponse(writeBuffer); 129 | //响应头 130 | iov[0].iov_base = (char*)writeBuffer.peek(); 131 | iov[0].iov_len = writeBuffer.readableBytes(); 132 | iovCnt = 1; 133 | //响应体 134 | if (response.getFileLen() > 0 && response.getFile()) 135 | { 136 | iov[1].iov_base = response.getFile(); 137 | iov[1].iov_len = response.getFileLen(); 138 | iovCnt = 2; 139 | } 140 | 141 | LOG_DEBUG("filesize:%d, iovcnt:%d, write:%d bytes", response.getFileLen(), iovCnt, toWriteBytes()); 142 | return true; 143 | } -------------------------------------------------------------------------------- /code/http/httpconnect.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPCONNECT_H 2 | #define HTTPCONNECT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../log/log.h" 11 | #include "../sqlconnpool/sql_conn_pool.h" 12 | #include "../buffer/buffer.h" 13 | #include "httprequest.h" 14 | #include "httpresponse.h" 15 | 16 | using namespace std; 17 | 18 | class HttpConnect 19 | { 20 | public: 21 | HttpConnect(); 22 | ~HttpConnect() {closeConnect();} 23 | 24 | void init(int sockfd, const sockaddr_in& addr); 25 | 26 | ssize_t read(int* saveErrno); 27 | ssize_t write(int* saveErrno); 28 | 29 | void closeConnect(); 30 | 31 | int getfd() const {return fd;} 32 | sockaddr_in getAddr() const {return addr;} 33 | int getPort() const {return addr.sin_port;} 34 | const char* getIP() const {return inet_ntoa(addr.sin_addr);} 35 | 36 | bool process(); 37 | 38 | int toWriteBytes() 39 | { 40 | return iov[0].iov_len + iov[1].iov_len; 41 | } 42 | 43 | bool isKeepAlive() const 44 | { 45 | return request.isKeepAlive(); 46 | } 47 | 48 | static bool isET; 49 | static const char* srcDir; 50 | static atomic userCnt; 51 | 52 | private: 53 | int fd; 54 | struct sockaddr_in addr; 55 | 56 | bool isClose; 57 | 58 | int iovCnt; 59 | struct iovec iov[2]; 60 | 61 | Buffer readBuffer; 62 | Buffer writeBuffer; 63 | 64 | HttpRequest request; 65 | HttpResponse response; 66 | }; 67 | 68 | #endif -------------------------------------------------------------------------------- /code/http/httprequest.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPREQUEST_H 2 | #define HTTPREQUEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../buffer/buffer.h" 12 | #include "../log/log.h" 13 | #include "../sqlconnpool/sql_conn_pool.h" 14 | #include "../threadpool/threadpool.h" 15 | 16 | using namespace std; 17 | 18 | enum HTTP_CODE 19 | { 20 | NO_REQUEST = 0, 21 | GET_REQUEST, 22 | BAD_REQUEST, 23 | NO_RESOURSE, 24 | FORBIDDENT_REQUEST, 25 | FILE_REQUEST, 26 | INTERNAL_ERROR, 27 | CLOSED_CONNECTION 28 | }; 29 | 30 | enum PARSE_STATE 31 | { 32 | REQUEST_LINE = 0, 33 | HEADERS, 34 | BODY, 35 | FINISH 36 | }; 37 | 38 | class HttpRequest 39 | { 40 | public: 41 | HttpRequest() {init();} 42 | ~HttpRequest() = default; 43 | 44 | void init(); 45 | HTTP_CODE parse(Buffer& buffer); 46 | 47 | string getPathConst() const {return path;} 48 | string& getPath() {return path;} 49 | string getMethod() const {return method;} 50 | string getVersion() const {return version;} 51 | bool isKeepAlive() const; 52 | 53 | private: 54 | HTTP_CODE parseRequestLine(const string& line); 55 | HTTP_CODE parseHeader(const string& line); 56 | HTTP_CODE parseBody(); 57 | 58 | void parsePath(); 59 | void parseFromUrlEncoded(); 60 | void parseFormData(); 61 | 62 | static bool userVerify(const string& name, const string& pwd, bool isLogin); 63 | 64 | PARSE_STATE state; 65 | string method, path, version, body; 66 | bool linger; 67 | size_t contentLen; 68 | unordered_map header; 69 | unordered_map post; 70 | unordered_map fileInfo; 71 | 72 | static const unordered_set DEFAULT_HTML; 73 | static const unordered_map DEFAULT_HTML_TAG; 74 | static int convertHex(char ch); 75 | }; 76 | 77 | #endif -------------------------------------------------------------------------------- /code/http/httpresponse.cpp: -------------------------------------------------------------------------------- 1 | #include "httpresponse.h" 2 | 3 | using namespace std; 4 | 5 | const unordered_map HttpResponse::SUFFIX_TYPE = 6 | { 7 | { ".html", "text/html" }, 8 | { ".xml", "text/xml" }, 9 | { ".xhtml", "application/xhtml+xml" }, 10 | { ".txt", "text/plain" }, 11 | { ".rtf", "application/rtf" }, 12 | { ".pdf", "application/pdf" }, 13 | { ".word", "application/msword" }, 14 | { ".png", "image/png" }, 15 | { ".gif", "image/gif" }, 16 | { ".jpg", "image/jpeg" }, 17 | { ".jpeg", "image/jpeg" }, 18 | { ".au", "audio/basic" }, 19 | { ".mpeg", "video/mpeg" }, 20 | { ".mpg", "video/mpeg" }, 21 | { ".avi", "video/x-msvideo" }, 22 | { ".gz", "application/x-gzip" }, 23 | { ".tar", "application/x-tar" }, 24 | { ".css", "text/css "}, 25 | { ".js", "text/javascript "}, 26 | }; 27 | 28 | const unordered_map HttpResponse::CODE_STATUS = 29 | { 30 | { 200, "OK" }, 31 | { 400, "Bad Request" }, 32 | { 403, "Forbidden" }, 33 | { 404, "Not Found" }, 34 | }; 35 | 36 | const unordered_map HttpResponse::CODE_PATH = 37 | { 38 | { 400, "/400.html" }, 39 | { 403, "/403.html" }, 40 | { 404, "/404.html" }, 41 | }; 42 | 43 | HttpResponse::HttpResponse() 44 | { 45 | code = -1; 46 | path = srcDir = ""; 47 | isKeepAlive = false; 48 | mmFile = nullptr; 49 | mmFileStat = {0}; 50 | } 51 | 52 | HttpResponse::~HttpResponse() 53 | { 54 | unmapFile(); 55 | } 56 | 57 | /* 响应报文初始化: 状态码,长连接,文件路径 */ 58 | void HttpResponse::init(const string& srcDir, string& path, bool isKeepAlive, int code) 59 | { 60 | assert(srcDir != ""); 61 | if (mmFile) unmapFile(); 62 | this->code = code; 63 | this->isKeepAlive = isKeepAlive; 64 | this->path = path; 65 | this->srcDir = srcDir; 66 | mmFile = nullptr; 67 | mmFileStat = {0}; 68 | } 69 | 70 | /* 制作响应报文 */ 71 | void HttpResponse::makeResponse(Buffer& buffer) 72 | { 73 | if (stat((srcDir + path).data(), &mmFileStat) < 0 || S_ISDIR(mmFileStat.st_mode)) 74 | { 75 | code = 404; 76 | } 77 | else if (!(mmFileStat.st_mode & S_IROTH)) 78 | { 79 | code = 403; 80 | } 81 | else if (code == -1) 82 | { 83 | code = 200; 84 | } 85 | //如果代码存在,跳转到相应页面,否则跳转到错误页面 86 | errorHtml(); 87 | //添加状态行 88 | addState(buffer); 89 | //添加响应头 90 | addHeader(buffer); 91 | //添加响应体 92 | addContent(buffer); 93 | } 94 | 95 | /* 获取映射好的文件 */ 96 | char* HttpResponse::getFile() 97 | { 98 | return mmFile; 99 | } 100 | 101 | size_t HttpResponse::getFileLen() const 102 | { 103 | return mmFileStat.st_size; 104 | } 105 | 106 | /* 范围外错误页面 */ 107 | void HttpResponse::errorContent(Buffer& buffer, string message) 108 | { 109 | string body; 110 | string status; 111 | body += "Error"; 112 | body += ""; 113 | if (CODE_STATUS.count(code)) 114 | { 115 | status = CODE_STATUS.find(code)->second; 116 | } 117 | else 118 | { 119 | status = "Bad Request"; 120 | } 121 | body += to_string(code) + " : " + status + "\n"; 122 | body += "

" + message + "

"; 123 | body += "
SimpleWebServer"; 124 | 125 | buffer.append("Content-length: " + to_string(body.size()) + "\r\n\r\n"); 126 | buffer.append(body); 127 | } 128 | 129 | int HttpResponse::getCode() const 130 | { 131 | return code; 132 | } 133 | 134 | /* 解除文件映射 */ 135 | void HttpResponse::unmapFile() 136 | { 137 | if (mmFile) 138 | { 139 | munmap(mmFile, mmFileStat.st_size); 140 | mmFile = nullptr; 141 | } 142 | } 143 | 144 | /* 添加状态行 */ 145 | void HttpResponse::addState(Buffer& buffer) 146 | { 147 | string status; 148 | if (CODE_STATUS.count(code)) 149 | { 150 | status = CODE_STATUS.find(code)->second; 151 | } 152 | else 153 | { 154 | code = 400; 155 | status = CODE_STATUS.find(400)->second; 156 | } 157 | buffer.append("HTTP/1.1 " + to_string(code) + " " + status + "\r\n"); 158 | } 159 | 160 | /* 添加响应头 */ 161 | void HttpResponse::addHeader(Buffer& buffer) 162 | { 163 | buffer.append("Connection: "); 164 | if (isKeepAlive) 165 | { 166 | buffer.append("keep-alive\r\n"); 167 | buffer.append("keep-alive: max=6, timeout=120\r\n"); 168 | } 169 | else 170 | { 171 | buffer.append("close\r\n"); 172 | } 173 | buffer.append("Content-type: " + getFileType() + "\r\n"); 174 | } 175 | 176 | const static std::regex regex_mp4(".+.mp4"); 177 | /* 添加响应体 */ 178 | void HttpResponse::addContent(Buffer& buffer) 179 | { 180 | int srcfd = open((srcDir + path).data(), O_RDONLY); 181 | if (srcfd < 0) 182 | { 183 | errorContent(buffer, "File Not Found!"); 184 | return; 185 | } 186 | LOG_DEBUG("file path %s", (srcDir + path).data()); 187 | 188 | //将文件映射到内存提高文件的访问速度 189 | //PROT_READ:映射区可读 190 | //MAP_PRIVATE:写入时复制 191 | int* mmRet = (int*)mmap(0, mmFileStat.st_size+1, PROT_READ, MAP_PRIVATE, srcfd, 0); 192 | if (*mmRet == -1) 193 | { 194 | errorContent(buffer, "File Not Found!"); 195 | return; 196 | } 197 | mmFile = (char*)mmRet; 198 | close(srcfd); 199 | buffer.append("Content-length: " + to_string(mmFileStat.st_size) + "\r\n\r\n"); 200 | // if(regex_match(path,regex_mp4)){ 201 | // buffer.append("Accept-Ranges: bytes\r\n"); 202 | // buffer.append("Content-Range: bytes 0-" + to_string(mmFileStat.st_size-1)+"/"+to_string(mmFileStat.st_size) + "\r\n\r\n"); 203 | // } 204 | } 205 | 206 | /* 范围内的错误页面 */ 207 | void HttpResponse::errorHtml() 208 | { 209 | if (CODE_PATH.count(code)) 210 | { 211 | path = CODE_PATH.find(code)->second; 212 | stat((srcDir + path).data(), &mmFileStat); 213 | } 214 | } 215 | 216 | /* 判断文件类型 */ 217 | string HttpResponse::getFileType() 218 | { 219 | string::size_type idx = path.find_last_of('.'); 220 | if (idx == string::npos) return "text/plain"; 221 | string suffix = path.substr(idx); 222 | if (SUFFIX_TYPE.count(suffix)) 223 | { 224 | return SUFFIX_TYPE.find(suffix)->second; 225 | } 226 | return "text/plain"; 227 | } -------------------------------------------------------------------------------- /code/http/httpresponse.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPRESPONSE_H 2 | #define HTTPRESPONSE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../buffer/buffer.h" 13 | #include "../log/log.h" 14 | 15 | using namespace std; 16 | 17 | class HttpResponse 18 | { 19 | public: 20 | HttpResponse(); 21 | ~HttpResponse(); 22 | 23 | void init(const string& srcDir, string& path, bool isKeepAlive = false, int code = -1); 24 | void makeResponse(Buffer& buffer); 25 | char* getFile(); 26 | size_t getFileLen() const; 27 | void errorContent(Buffer& buffer, string message); 28 | int getCode() const; 29 | void unmapFile(); 30 | 31 | private: 32 | void addState(Buffer &buffer); 33 | void addHeader(Buffer &buffer); 34 | void addContent(Buffer &buffer); 35 | 36 | void errorHtml(); 37 | string getFileType(); 38 | 39 | int code; 40 | bool isKeepAlive; 41 | 42 | string path = ""; 43 | string srcDir = ""; 44 | 45 | char* mmFile; 46 | struct stat mmFileStat; 47 | 48 | static const unordered_map SUFFIX_TYPE; 49 | static const unordered_map CODE_STATUS; 50 | static const unordered_map CODE_PATH; 51 | }; 52 | 53 | #endif -------------------------------------------------------------------------------- /code/lock/locker.h: -------------------------------------------------------------------------------- 1 | #ifndef LOCKER_H 2 | #define LOCKER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class sem 9 | { 10 | public: 11 | sem() 12 | { 13 | if (sem_init(&m_sem, 0, 0) != 0) 14 | { 15 | throw std::exception(); 16 | } 17 | } 18 | sem(int num) 19 | { 20 | if (sem_init(&m_sem, 0, num) != 0) 21 | { 22 | throw std::exception(); 23 | } 24 | } 25 | ~sem() 26 | { 27 | sem_destroy(&m_sem); 28 | } 29 | bool wait() 30 | { 31 | return sem_wait(&m_sem) == 0; 32 | } 33 | bool post() 34 | { 35 | return sem_post(&m_sem) == 0; 36 | } 37 | 38 | private: 39 | sem_t m_sem; 40 | }; 41 | 42 | 43 | class mtx 44 | { 45 | public: 46 | mtx() 47 | { 48 | if (pthread_mutex_init(&m_mutex, NULL) != 0) 49 | { 50 | throw std::exception(); 51 | } 52 | } 53 | ~mtx() 54 | { 55 | pthread_mutex_destroy(&m_mutex); 56 | } 57 | 58 | bool lock() 59 | { 60 | return pthread_mutex_lock(&m_mutex) == 0; 61 | } 62 | bool unlock() 63 | { 64 | return pthread_mutex_unlock(&m_mutex) == 0; 65 | } 66 | pthread_mutex_t *get() 67 | { 68 | return &m_mutex; 69 | } 70 | 71 | private: 72 | pthread_mutex_t m_mutex; 73 | }; 74 | 75 | 76 | class cond { 77 | public: 78 | cond() 79 | { 80 | if (pthread_cond_init(&m_cond, NULL) != 0) 81 | { 82 | throw std::exception(); 83 | } 84 | } 85 | ~cond() 86 | { 87 | pthread_cond_destroy(&m_cond); 88 | } 89 | bool wait(pthread_mutex_t *m_mutex) 90 | { 91 | int ret = 0; 92 | ret = pthread_cond_wait(&m_cond, m_mutex); 93 | return ret == 0; 94 | } 95 | bool timewait(pthread_mutex_t *m_mutex, struct timespec t) 96 | { 97 | int ret = 0; 98 | ret = pthread_cond_timedwait(&m_cond, m_mutex, &t); 99 | return ret == 0; 100 | } 101 | bool signal() 102 | { 103 | return pthread_cond_signal(&m_cond) == 0; 104 | } 105 | bool broadcast() 106 | { 107 | return pthread_cond_broadcast(&m_cond) == 0; 108 | } 109 | private: 110 | pthread_cond_t m_cond; 111 | }; 112 | 113 | #endif -------------------------------------------------------------------------------- /code/log/blockqueue.h: -------------------------------------------------------------------------------- 1 | /* 阻塞队列 2 | 阻塞队列作为日志缓冲区,内部封装了生产者-消费者模型 3 | 生产者:向队列尾部插入日志信息的线程 4 | 消费者:从队列头部取出日志信息并处理的线程 5 | 6 | 因为涉及到多线程读写,使用互斥锁实现对队列的互斥访问 7 | 同时用两个条件信号实现生产者-消费者模型 8 | 9 | 需要注意的点是插入和删除时,条件变量需要配合互斥锁 10 | 先上锁,然后在while循环内检查条件变量 11 | 当插入一个日志时,唤醒一个消费者线程 12 | 当处理一个日志时,唤醒一个生产者线程 13 | */ 14 | 15 | #ifndef BLOCKQUEUE_H 16 | #define BLOCKQUEUE_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | template 24 | class BlockQueue { 25 | public: 26 | explicit BlockQueue(size_t MaxCapacity = 1000); 27 | 28 | ~BlockQueue(); 29 | 30 | void clear(); 31 | 32 | bool empty(); 33 | 34 | bool full(); 35 | 36 | void close(); 37 | 38 | size_t size(); 39 | 40 | size_t capacity(); 41 | 42 | T front(); 43 | 44 | T back(); 45 | 46 | void push(const T &item); 47 | 48 | bool pop(T &item); 49 | 50 | bool pop(T &item, int timeout); 51 | 52 | void flush(); 53 | 54 | private: 55 | std::queue que; 56 | 57 | size_t capacity_; 58 | 59 | std::mutex mtx; 60 | 61 | bool isClose; 62 | 63 | std::condition_variable condConsumer; 64 | 65 | std::condition_variable condProducer; 66 | }; 67 | 68 | 69 | template 70 | BlockQueue::BlockQueue(size_t maxCapacity) :capacity_(maxCapacity) { 71 | assert(maxCapacity > 0); 72 | isClose = false; 73 | } 74 | 75 | template 76 | BlockQueue::~BlockQueue() { 77 | close(); 78 | }; 79 | 80 | template 81 | void BlockQueue::close() { 82 | { 83 | std::lock_guard locker(mtx); 84 | //queue不支持clear,但可以重新赋值 85 | que = std::queue(); 86 | isClose = true; 87 | } 88 | condProducer.notify_all(); 89 | condConsumer.notify_all(); 90 | }; 91 | 92 | template 93 | void BlockQueue::flush() { 94 | condConsumer.notify_one(); 95 | }; 96 | 97 | template 98 | void BlockQueue::clear() { 99 | std::lock_guard locker(mtx); 100 | que.clear(); 101 | } 102 | 103 | template 104 | T BlockQueue::front() { 105 | std::lock_guard locker(mtx); 106 | return que.front(); 107 | } 108 | 109 | template 110 | T BlockQueue::back() { 111 | std::lock_guard locker(mtx); 112 | return que.back(); 113 | } 114 | 115 | template 116 | size_t BlockQueue::size() { 117 | std::lock_guard locker(mtx); 118 | return que.size(); 119 | } 120 | 121 | template 122 | size_t BlockQueue::capacity() { 123 | std::lock_guard locker(mtx); 124 | return capacity_; 125 | } 126 | 127 | template 128 | void BlockQueue::push(const T &item) { 129 | std::unique_lock locker(mtx); 130 | while(que.size() >= capacity_) { 131 | condProducer.wait(locker); 132 | } 133 | que.push(item); 134 | condConsumer.notify_one(); 135 | } 136 | 137 | template 138 | bool BlockQueue::empty() { 139 | std::lock_guard locker(mtx); 140 | return que.empty(); 141 | } 142 | 143 | template 144 | bool BlockQueue::full(){ 145 | std::lock_guard locker(mtx); 146 | return que.size() >= capacity_; 147 | } 148 | 149 | template 150 | bool BlockQueue::pop(T &item) { 151 | std::unique_lock locker(mtx); 152 | while(que.empty()){ 153 | condConsumer.wait(locker); 154 | if(isClose){ 155 | return false; 156 | } 157 | } 158 | item = que.front(); 159 | que.pop(); 160 | condProducer.notify_one(); 161 | return true; 162 | } 163 | 164 | template 165 | bool BlockQueue::pop(T &item, int timeout) { 166 | std::unique_lock locker(mtx); 167 | while(que.empty()){ 168 | //判断等待时间是否超过timeout 169 | if(condConsumer.wait_for(locker, std::chrono::seconds(timeout)) 170 | == std::cv_status::timeout){ 171 | return false; 172 | } 173 | if(isClose){ 174 | return false; 175 | } 176 | } 177 | item = que.front(); 178 | que.pop(); 179 | condProducer.notify_one(); 180 | return true; 181 | } 182 | 183 | #endif // BLOCKQUEUE_H -------------------------------------------------------------------------------- /code/log/log.cpp: -------------------------------------------------------------------------------- 1 | /* 同步/异步写日志 2 | 同步写日志:线程直接向文件内写入日志,写日志与线程业务是串行的 3 | 异步写日志:线程先将日志放到阻塞队列中,再用专门的线程向文件内写日志 4 | 5 | 为了避免日志混乱,需要用互斥锁实现文件的互斥访问,写日志前需要上锁 6 | 对于异步写日志:用单例模式维护一个阻塞队列,一个写线程,节约资源,减少竞态 7 | 8 | 每次调用写日志方法后,或创建新日志文件前(日期改变,行数超限),都要调用flush将缓冲区内数据全部写入文件中 9 | 如果是同步写日志,fflush(fp)即可,如果是异步,还要先保证阻塞队列被清空 10 | 11 | */ 12 | 13 | #include "log.h" 14 | 15 | using namespace std; 16 | 17 | Log::Log() { 18 | lineCount = 0; 19 | isAsync = false; 20 | writeThread = nullptr; 21 | que = nullptr; 22 | today = 0; 23 | fp = nullptr; 24 | } 25 | 26 | Log::~Log() { 27 | if(writeThread && writeThread->joinable()) { 28 | //循环将阻塞队列内的日志处理完毕后再关闭 29 | while(!que->empty()) 30 | { 31 | que->flush(); 32 | }; 33 | que->close(); 34 | //回收写线程资源 35 | writeThread->join(); 36 | } 37 | //缓存写入文件后再关闭指针 38 | if(fp) { 39 | lock_guard locker(mtx); 40 | flush(); 41 | fclose(fp); 42 | } 43 | } 44 | 45 | int Log::getLevel() { 46 | lock_guard locker(mtx); 47 | return level_; 48 | } 49 | 50 | void Log::setLevel(int level) { 51 | lock_guard locker(mtx); 52 | level_ = level; 53 | } 54 | 55 | /* 初始化:阻塞队列,写线程,写缓冲区,日志文件指针 */ 56 | void Log::init(int level = 1, const char* path, const char* suffix, 57 | int maxQueueSize) { 58 | isOpen_ = true; 59 | level_ = level; 60 | if(maxQueueSize > 0) 61 | { 62 | isAsync = true; 63 | if(!que) 64 | { 65 | //初始化阻塞队列和写日志线程 66 | unique_ptr> newQueue(new BlockQueue); 67 | que = move(newQueue); 68 | 69 | std::unique_ptr NewThread(new thread(flushLogThread)); 70 | writeThread = move(NewThread); 71 | } 72 | } 73 | else 74 | { 75 | isAsync = false; 76 | } 77 | 78 | lineCount = 0; 79 | 80 | //初始化文件名 81 | time_t timer = time(nullptr); 82 | struct tm *sysTime = localtime(&timer); 83 | struct tm t = *sysTime; 84 | path_ = path; 85 | suffix_ = suffix; 86 | char fileName[LOG_NAME_LEN] = {0}; 87 | snprintf(fileName, LOG_NAME_LEN - 1, "%s/%04d_%02d_%02d%s", 88 | path_, t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, suffix_); 89 | today = t.tm_mday; 90 | 91 | //初始化文件指针 92 | { 93 | lock_guard locker(mtx); 94 | buffer.retrieveAll(); 95 | if(fp) { 96 | flush(); 97 | fclose(fp); 98 | } 99 | 100 | fp = fopen(fileName, "a"); 101 | if(fp == nullptr) { 102 | mkdir(path_, 0777); 103 | fp = fopen(fileName, "a"); 104 | } 105 | assert(fp != nullptr); 106 | } 107 | } 108 | 109 | /* 写日志 */ 110 | void Log::write(int level, const char *format, ...) { 111 | struct timeval now = {0, 0}; 112 | gettimeofday(&now, nullptr); 113 | time_t tSec = now.tv_sec; 114 | struct tm *sysTime = localtime(&tSec); 115 | struct tm t = *sysTime; 116 | va_list vaList; 117 | 118 | //日志日期改变 或 日志行数为最大行数的倍数, 创建新的log文件 119 | if (today != t.tm_mday || (lineCount && (lineCount % MAX_LINES) == 0)) 120 | { 121 | unique_lock locker(mtx); 122 | locker.unlock(); 123 | 124 | char newFile[LOG_NAME_LEN]; 125 | char tail[36] = {0}; 126 | snprintf(tail, 36, "%04d_%02d_%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday); 127 | 128 | if (today != t.tm_mday) 129 | { 130 | snprintf(newFile, LOG_NAME_LEN - 72, "%s/%s%s", path_, tail, suffix_); 131 | today = t.tm_mday; 132 | lineCount = 0; 133 | } 134 | else { 135 | snprintf(newFile, LOG_NAME_LEN - 72, "%s/%s-%d%s", path_, tail, (lineCount / MAX_LINES), suffix_); 136 | } 137 | 138 | locker.lock(); 139 | flush(); //保证上一日期日志已经写完毕 140 | fclose(fp); 141 | fp = fopen(newFile, "a"); 142 | assert(fp != nullptr); 143 | } 144 | 145 | //向文件中互斥写日志 146 | { 147 | unique_lock locker(mtx); 148 | lineCount++; 149 | int n = snprintf(buffer.beginWrite(), 128, "%d-%02d-%02d %02d:%02d:%02d.%06ld ", 150 | t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, 151 | t.tm_hour, t.tm_min, t.tm_sec, now.tv_usec); 152 | 153 | buffer.hasWritten(n); 154 | appendLogLevelTitle(level); 155 | 156 | va_start(vaList, format); 157 | int m = vsnprintf(buffer.beginWrite(), buffer.writableBytes(), format, vaList); 158 | va_end(vaList); 159 | 160 | buffer.hasWritten(m); 161 | buffer.append("\n\0", 2); 162 | 163 | if(isAsync && que && !que->full()) { 164 | //先将日志写入阻塞队列,之后再异步读写 165 | que->push(buffer.retrieveAllToStr()); 166 | } else { 167 | //直接同步写日志 168 | fputs(buffer.peek(), fp); 169 | } 170 | buffer.retrieveAll(); 171 | } 172 | } 173 | 174 | /* 添加日志等级信息 */ 175 | void Log::appendLogLevelTitle(int level) { 176 | switch(level) { 177 | case 0: 178 | buffer.append("[debug]: ", 9); 179 | break; 180 | case 1: 181 | buffer.append("[info] : ", 9); 182 | break; 183 | case 2: 184 | buffer.append("[warn] : ", 9); 185 | break; 186 | case 3: 187 | buffer.append("[error]: ", 9); 188 | break; 189 | default: 190 | buffer.append("[info] : ", 9); 191 | break; 192 | } 193 | } 194 | 195 | /* 将缓存数据立即写入文件中 */ 196 | void Log::flush() { 197 | //如果是异步,唤醒写日志线程,保证阻塞队列内的日志信息完全被清空 198 | //写日志线程通过加锁保证在队列清空前,文件指针不会被关闭 199 | if(isAsync) 200 | { 201 | que->flush(); 202 | } 203 | //将缓存立即写入文件中 204 | fflush(fp); 205 | } 206 | 207 | /* 异步持续写日志 */ 208 | //每个日志完成前,将阻塞队列加锁,避免读写混乱 209 | void Log::asyncWrite() { 210 | string str = ""; 211 | while(que->pop(str)) { 212 | lock_guard locker(mtx); 213 | fputs(str.c_str(), fp); 214 | } 215 | } 216 | 217 | /* 单例模式唯一实例 */ 218 | Log* Log::instance() { 219 | static Log obj; 220 | return &obj; 221 | } 222 | 223 | /* 写线程回调函数,异步写日志 */ 224 | void Log::flushLogThread() { 225 | Log::instance()->asyncWrite(); 226 | } -------------------------------------------------------------------------------- /code/log/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include // vastart va_end 10 | #include 11 | #include //mkdir 12 | #include "blockqueue.h" 13 | #include "../buffer/buffer.h" 14 | 15 | class Log { 16 | public: 17 | //日志等级,日志路径,日志后缀,异步日志队列最大长度 18 | void init(int level, const char* path = "./log", 19 | const char* suffix =".log", 20 | int maxQueueCapacity = 1024); 21 | 22 | static Log* instance(); 23 | static void flushLogThread(); 24 | 25 | void write(int level, const char *format,...); 26 | void flush(); 27 | 28 | int getLevel(); 29 | void setLevel(int level); 30 | bool isOpen() { return isOpen_; } 31 | 32 | private: 33 | Log(); 34 | void appendLogLevelTitle(int level); 35 | virtual ~Log(); 36 | void asyncWrite(); 37 | 38 | private: 39 | static const int LOG_PATH_LEN = 256; 40 | static const int LOG_NAME_LEN = 256; 41 | static const int MAX_LINES = 50000; 42 | 43 | const char* path_; 44 | const char* suffix_; 45 | 46 | int MAX_LINES_; 47 | 48 | int lineCount; 49 | int today; 50 | 51 | bool isOpen_; 52 | 53 | Buffer buffer; 54 | int level_; 55 | bool isAsync; 56 | 57 | FILE* fp; 58 | std::unique_ptr> que; 59 | std::unique_ptr writeThread; 60 | std::mutex mtx; 61 | }; 62 | 63 | //日志等级level要给定 64 | //可变参数宏:__VA_ARGS__ 65 | //调用日志写,并立刻将缓冲区内数据写入文件 66 | #define LOG_BASE(level, format, ...) \ 67 | do {\ 68 | Log* log = Log::instance();\ 69 | if (log->isOpen() && log->getLevel() <= level) {\ 70 | log->write(level, format, ##__VA_ARGS__); \ 71 | log->flush();\ 72 | }\ 73 | } while(0); 74 | 75 | //分别对应不同level,调用LOG_BASE宏 76 | //定义四个宏,方便在其他文件中使用 77 | #define LOG_DEBUG(format, ...) do {LOG_BASE(0, format, ##__VA_ARGS__)} while(0); 78 | #define LOG_INFO(format, ...) do {LOG_BASE(1, format, ##__VA_ARGS__)} while(0); 79 | #define LOG_WARN(format, ...) do {LOG_BASE(2, format, ##__VA_ARGS__)} while(0); 80 | #define LOG_ERROR(format, ...) do {LOG_BASE(3, format, ##__VA_ARGS__)} while(0); 81 | 82 | #endif //LOG_H -------------------------------------------------------------------------------- /code/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "server/webserver.h" 3 | 4 | /* 5 | 响应模式 6 | 0:连接和监听都是LT 7 | 1:连接ET,监听LT 8 | 2:连接LT,监听ET 9 | 3:连接和监听都是ET 10 | 日志等级 11 | 0:DEBUG 12 | 1:INFO 13 | 2:WARN 14 | 3:ERROR 15 | */ 16 | int main() { 17 | 18 | WebServer server( 19 | 80, 3, 60000, false, // 客户端监听端口, ET触发模式, 连接计时1分钟, 优雅退出 20 | 3306, "root", "666666", "webdb", // 数据库相关,数据库监听端口,用户名,密码,数据库名 21 | 12, 6, 10000, true, 1, 1024); // 数据库连接池数量,线程池数量,最大连接数,日志开关,日志等级,日志异步队列容量 22 | server.start(); 23 | } -------------------------------------------------------------------------------- /code/server/epoller.cpp: -------------------------------------------------------------------------------- 1 | #include "epoller.h" 2 | 3 | Epoller::Epoller(int maxEvent):epollfd(epoll_create(512)), events(maxEvent) 4 | { 5 | assert(epollfd >= 0 && events.size() > 0); 6 | } 7 | 8 | Epoller::~Epoller() 9 | { 10 | close(epollfd); 11 | } 12 | 13 | bool Epoller::addfd(int fd, uint32_t events) 14 | { 15 | if (fd < 0) return false; 16 | epoll_event ev = {0}; 17 | ev.data.fd = fd; 18 | ev.events = events; 19 | return epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == 0; 20 | } 21 | 22 | bool Epoller::modfd(int fd, uint32_t events) 23 | { 24 | if (fd < 0) return false; 25 | epoll_event ev = {0}; 26 | ev.data.fd = fd; 27 | ev.events = events; 28 | return epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev) == 0; 29 | } 30 | 31 | bool Epoller::delfd(int fd) 32 | { 33 | if (fd < 0) return false; 34 | epoll_event ev = {0}; 35 | ev.data.fd = fd; 36 | return epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev) == 0; 37 | } 38 | 39 | int Epoller::wait(int timeoutMs) 40 | { 41 | return epoll_wait(epollfd, &events[0], (int)events.size(), timeoutMs); 42 | } 43 | 44 | int Epoller::getEventfd(size_t i) const 45 | { 46 | assert(i < events.size() && i >= 0); 47 | return events[i].data.fd; 48 | } 49 | 50 | uint32_t Epoller::getEvents(size_t i) const 51 | { 52 | assert(i < events.size() && i >= 0); 53 | return events[i].events; 54 | } -------------------------------------------------------------------------------- /code/server/epoller.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLLER_H 2 | #define EPOLLER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | class Epoller 14 | { 15 | public: 16 | explicit Epoller(int maxEvent = 1024); 17 | ~Epoller(); 18 | 19 | bool addfd(int fd, uint32_t events); 20 | bool modfd(int fd, uint32_t events); 21 | bool delfd(int fd); 22 | 23 | int wait(int timeoutsMs = -1); 24 | 25 | int getEventfd(size_t i) const; 26 | uint32_t getEvents(size_t i) const; 27 | private: 28 | int epollfd; 29 | 30 | vector events; 31 | }; 32 | 33 | #endif -------------------------------------------------------------------------------- /code/server/webserver.cpp: -------------------------------------------------------------------------------- 1 | #include "webserver.h" 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | //服务器相关参数包括连接参数、数据库参数、线程池参数、日志参数 7 | WebServer::WebServer( 8 | int port, int trigMode, int timeoutMs, bool optLinger, 9 | int sqlPort, const char* sqlUser, const char* sqlPwd, 10 | const char* dbName, int connPoolNum, 11 | int threadNum, int maxRequests, 12 | bool openLog, int logLevel, int logQueSize): 13 | port(port), openLinger(optLinger), timeoutMs(timeoutMs), shutdown(false), 14 | timer(new HeapTimer()), epoller(new Epoller()) 15 | { 16 | // 17 | std::cout<init(threadNum, maxRequests); 26 | //连接池唯一实例初始化 27 | SqlConnPool::instance()->init("localhost", sqlPort, sqlUser, sqlPwd, dbName, connPoolNum); 28 | //设置不同套接字的触发模式 29 | initEventMode(trigMode); 30 | if (!initSocket()) shutdown = true; 31 | 32 | //初始化日志实例 33 | if(openLog) { 34 | Log::instance()->init(logLevel, "./log", ".log", logQueSize); 35 | if(shutdown) { LOG_ERROR("========== Server init error!=========="); } 36 | else { 37 | LOG_INFO("========== Server init =========="); 38 | LOG_INFO("Port:%d, OpenLinger: %s", port, optLinger? "true":"false"); 39 | LOG_INFO("Listen Mode: %s, OpenConn Mode: %s", 40 | (listenEvent & EPOLLET ? "ET": "LT"), 41 | (connEvent & EPOLLET ? "ET": "LT")); 42 | LOG_INFO("LogSys level: %d", logLevel); 43 | LOG_INFO("srcDir: %s", HttpConnect::srcDir); 44 | LOG_INFO("SqlConnPool num: %d, ThreadPool num: %d", connPoolNum, threadNum); 45 | } 46 | } 47 | } 48 | 49 | WebServer::~WebServer() 50 | { 51 | //回收监听套接字 52 | close(listenfd); 53 | shutdown = true; 54 | //回收路径动态缓存 55 | free(srcDir); 56 | //关闭数据库连接池 57 | SqlConnPool::instance()->destroy(); 58 | } 59 | 60 | /* 设置不同套接字的触发模式 */ 61 | void WebServer::initEventMode(int trigMode) 62 | { 63 | //监听事件:仅为了初始化,无作用 64 | listenEvent = EPOLLRDHUP; 65 | //连接事件:对端断开,并设置oneshot 66 | connEvent = EPOLLONESHOT | EPOLLRDHUP; 67 | switch (trigMode) 68 | { 69 | case 0: 70 | break; 71 | case 1: 72 | connEvent |= EPOLLET; 73 | break; 74 | case 2: 75 | listenEvent |= EPOLLET; 76 | break; 77 | case 3: 78 | listenEvent |= EPOLLET; 79 | connEvent |= EPOLLET; 80 | break; 81 | default: 82 | listenEvent |= EPOLLET; 83 | connEvent |= EPOLLET; 84 | break; 85 | } 86 | //用&判断连接套接字是否为ET触发模式 87 | HttpConnect::isET = (connEvent & EPOLLET); 88 | } 89 | 90 | /* epoll循环监听事件,根据事件类型调用相应方法 */ 91 | //延迟计算思想,方法及参数会被打包放到线程池的任务队列中 92 | void WebServer::start() 93 | { 94 | int timeMs = -1; 95 | if (!shutdown) {LOG_INFO("========= Server start =========");} 96 | while (!shutdown) 97 | { 98 | //返回下一个计时器超时的时间 99 | if (timeoutMs > 0) 100 | timeMs = timer->getNextTick(); 101 | /* 利用epoll的time_wait实现定时功能 */ 102 | //在计时器超时前唤醒一次epoll,判断是否有新事件到达 103 | //如果没有新事件,下次调用getNextTick时,会将超时的堆顶计时器删除 104 | int eventCnt = epoller->wait(timeMs); 105 | //遍历事件表 106 | for (int i = 0; i < eventCnt; i ++) 107 | { 108 | //事件套接字 109 | int fd = epoller->getEventfd(i); 110 | //事件内容 111 | uint32_t events = epoller->getEvents(i); 112 | //监听套接字只有连接事件 113 | if (fd == listenfd) dealListen(); 114 | //连接套接字几种事件 115 | //对端关闭连接 116 | else if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) 117 | { 118 | assert(users.count(fd) > 0); 119 | closeConnect(&users[fd]); 120 | } 121 | //读事件 122 | else if (events & EPOLLIN) 123 | { 124 | assert(users.count(fd) > 0); 125 | dealRead(&users[fd]); 126 | } 127 | //写事件 128 | else if (events & EPOLLOUT) 129 | { 130 | assert(users.count(fd) > 0); 131 | dealWrite(&users[fd]); 132 | } 133 | else 134 | { 135 | LOG_ERROR("Unexpected event"); 136 | } 137 | } 138 | } 139 | } 140 | 141 | /* 发送错误 */ 142 | void WebServer::sendError(int fd, const char* info) 143 | { 144 | assert(fd > 0); 145 | int ret = send(fd, info, strlen(info), 0); 146 | if (ret < 0) 147 | { 148 | LOG_WARN("send error to client[%d] error!", fd); 149 | } 150 | close(fd); 151 | } 152 | 153 | /* 关闭连接套接字,并从epoll事件表中删除相应事件 */ 154 | void WebServer::closeConnect(HttpConnect* client) 155 | { 156 | assert(client); 157 | LOG_INFO("Client[%d] quit!", client->getfd()); 158 | epoller->delfd(client->getfd()); 159 | client->closeConnect(); 160 | } 161 | 162 | /* 为连接注册事件和设置计时器 */ 163 | void WebServer::addClient(int fd, sockaddr_in addr) 164 | { 165 | assert(fd > 0); 166 | //users是哈希表,套接字是键,HttpConnect对象是值 167 | //将fd和连接地址传入,初始化HttpConnect对象,用client表示 168 | users[fd].init(fd, addr); 169 | //添加计时器,到期关闭连接 170 | if(timeoutMs > 0) 171 | { 172 | timer->add(fd, timeoutMs, bind(&WebServer::closeConnect, this, &users[fd])); 173 | } 174 | epoller->addfd(fd, EPOLLIN | connEvent); 175 | //套接字设置非阻塞 176 | setfdNonblock(fd); 177 | LOG_INFO("Client[%d] in!", users[fd].getfd()); 178 | } 179 | 180 | /* 新建连接套接字,ET模式会一次将连接队列读完 */ 181 | void WebServer::dealListen() 182 | { 183 | struct sockaddr_in addr; 184 | socklen_t len = sizeof(addr); 185 | do 186 | { 187 | int fd = accept(listenfd, (struct sockaddr*)&addr, &len); 188 | if (fd <= 0) return; 189 | if (HttpConnect::userCnt >= MAX_FD) 190 | { 191 | sendError(fd, "Server busy!"); 192 | LOG_WARN("Clients is full!"); 193 | return; 194 | } 195 | addClient(fd, addr); 196 | } while (listenEvent & EPOLLET); 197 | } 198 | 199 | /* 将读函数和参数用std::bind绑定,加入线程池的任务队列 */ 200 | void WebServer::dealRead(HttpConnect* client) 201 | { 202 | assert(client); 203 | extentTime(client); 204 | //非静态成员函数需要传递this指针作为第一个参数 205 | ThreadPool::instance()->addTask(std::bind(&WebServer::onRead, this, client)); 206 | } 207 | 208 | /* 将写吧函数和参数用std::bind绑定,加入线程池的任务队列 */ 209 | void WebServer::dealWrite(HttpConnect* client) 210 | { 211 | assert(client); 212 | extentTime(client); 213 | //非静态成员函数需要传递this指针作为第一个参数 214 | ThreadPool::instance()->addTask(std::bind(&WebServer::onWrite, this, client)); 215 | } 216 | 217 | //重置计时器 218 | void WebServer::extentTime(HttpConnect* client) 219 | { 220 | assert(client); 221 | if (timeoutMs > 0) 222 | timer->adjust(client->getfd(), timeoutMs); 223 | } 224 | 225 | /* 读函数:先接收再处理 */ 226 | void WebServer::onRead(HttpConnect* client) 227 | { 228 | assert(client); 229 | int ret = -1; 230 | int readErrno = 0; 231 | ret = client->read(&readErrno); 232 | //客户端发送EOF 233 | if (ret <= 0 && readErrno != EAGAIN) 234 | { 235 | closeConnect(client); 236 | return; 237 | } 238 | onProcess(client); 239 | } 240 | 241 | /* 处理函数:判断读入的请求报文是否完整,决定是继续监听读还是监听写 */ 242 | //如果请求不完整,继续读,如果请求完整,则根据请求内容生成相应的响应报文,并发送 243 | //oneshot需要再次监听 244 | void WebServer::onProcess(HttpConnect* client) 245 | { 246 | if (client->process()) 247 | { 248 | epoller->modfd(client->getfd(), connEvent | EPOLLOUT); 249 | } 250 | else 251 | { 252 | epoller->modfd(client->getfd(), connEvent | EPOLLIN); 253 | } 254 | } 255 | 256 | /* 写函数:发送响应报文,大文件需要分多次发送 */ 257 | //由于设置了oneshot,需要再次监听读 258 | void WebServer::onWrite(HttpConnect* client) 259 | { 260 | assert(client); 261 | int ret = -1; 262 | int writeErrno = 0; 263 | ret = client->write(&writeErrno); 264 | //发送完毕 265 | if (client->toWriteBytes() == 0) 266 | { 267 | if (client->isKeepAlive()) 268 | { 269 | onProcess(client); 270 | return; 271 | } 272 | } 273 | //发送失败 274 | else if (ret < 0) 275 | { 276 | //缓存满导致的,继续监听写 277 | if (writeErrno == EAGAIN) 278 | { 279 | epoller->modfd(client->getfd(), connEvent | EPOLLOUT); 280 | return; 281 | } 282 | } 283 | //其他原因导致,关闭连接 284 | closeConnect(client); 285 | } 286 | 287 | /* 创建监听套接字,并设置属性,绑定端口,向epoll注册连接事件 */ 288 | bool WebServer::initSocket() 289 | { 290 | int ret; 291 | struct sockaddr_in addr; 292 | //设置可以使用root用户端口 293 | if (port > 65536 ) 294 | { 295 | LOG_ERROR("port: %d error!", port); 296 | return false; 297 | } 298 | assert(port>=0); 299 | addr.sin_family = AF_INET; 300 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 301 | addr.sin_port = htons(port); 302 | //设置optLinger需要的参数 303 | struct linger optLinger = {0}; 304 | if (openLinger) 305 | { 306 | //优雅关闭,最多等待20s接受客户端关闭确认 307 | optLinger.l_onoff = 1; 308 | optLinger.l_linger = 20; 309 | } 310 | 311 | //创建监听套接字 312 | listenfd = socket(AF_INET, SOCK_STREAM, 0); 313 | if (listenfd < 0) 314 | { 315 | LOG_ERROR("port: %d create socket error!", port); 316 | return false; 317 | } 318 | //套接字设置优雅关闭 319 | ret = setsockopt(listenfd, SOL_SOCKET, SO_LINGER, &optLinger, sizeof(optLinger)); 320 | if (ret == -1) 321 | { 322 | close(listenfd); 323 | LOG_ERROR("port: %d init linger error!", port); 324 | return false; 325 | } 326 | 327 | int optval = 1; 328 | //套接字设置端口复用(端口处于TIME_WAIT时,也可以被bind) 329 | ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&optval, sizeof(int)); 330 | if (ret == -1) 331 | { 332 | LOG_ERROR("set socket error!"); 333 | close(listenfd); 334 | return false; 335 | } 336 | //套接字绑定端口 337 | ret = bind(listenfd, (struct sockaddr*)&addr, sizeof(addr)); 338 | if (ret == -1) 339 | { 340 | LOG_ERROR("bind port: %d error!", port); 341 | close(listenfd); 342 | return false; 343 | } 344 | //套接字设为可接受连接状态,并指明请求队列大小 345 | ret = listen(listenfd, 6); 346 | if (ret == -1) 347 | { 348 | LOG_ERROR("listen port: %d error!", port); 349 | close(listenfd); 350 | return false; 351 | } 352 | //向epoll注册监听套接字连接事件 353 | ret = epoller->addfd(listenfd, listenEvent | EPOLLIN); 354 | if (ret == 0) 355 | { 356 | LOG_ERROR("Add listen error!"); 357 | close(listenfd); 358 | return false; 359 | } 360 | //套接字设置非阻塞(优雅关闭还是会导致close阻塞) 361 | setfdNonblock(listenfd); 362 | LOG_INFO("Server port: %d", port); 363 | return true; 364 | } 365 | 366 | /* 套接字设置非阻塞 */ 367 | int WebServer::setfdNonblock(int fd) { 368 | assert(fd > 0); 369 | return fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK); 370 | } -------------------------------------------------------------------------------- /code/server/webserver.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSERVER_H 2 | #define WEBSERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "epoller.h" 13 | #include "../log/log.h" 14 | #include "../sqlconnpool/sql_conn_pool.h" 15 | #include "../threadpool/threadpool.h" 16 | #include "../http/httpconnect.h" 17 | #include "../timer/heaptimer.h" 18 | 19 | using namespace std; 20 | 21 | class WebServer 22 | { 23 | public: 24 | WebServer( 25 | int port, int trigMode, int timeoutMs, bool optLinger, 26 | int sqlPort, const char* sqlUser, const char* sqlPwd, 27 | const char* dbName, int connPoolNum, 28 | int threadNum, int maxRequests, 29 | bool openLog, int logLevel, int logQueSize); 30 | 31 | ~WebServer(); 32 | 33 | void start(); 34 | 35 | private: 36 | bool initSocket(); 37 | void initEventMode(int trigMode); 38 | void addClient(int fd, sockaddr_in addr); 39 | 40 | void dealListen(); 41 | void dealWrite(HttpConnect* client); 42 | void dealRead(HttpConnect* client); 43 | 44 | void sendError(int fd, const char* info); 45 | void extentTime(HttpConnect* client); 46 | void closeConnect(HttpConnect* client); 47 | 48 | void onRead(HttpConnect* client); 49 | void onWrite(HttpConnect* client); 50 | void onProcess(HttpConnect* client); 51 | 52 | static const int MAX_FD = 65536; 53 | static int setfdNonblock(int fd); 54 | 55 | int port; 56 | bool openLinger; 57 | int timeoutMs; 58 | bool shutdown; 59 | int listenfd; 60 | char* srcDir; 61 | 62 | uint32_t listenEvent; 63 | uint32_t connEvent; 64 | 65 | unique_ptr timer; 66 | unique_ptr epoller; 67 | unordered_map users; 68 | }; 69 | 70 | #endif -------------------------------------------------------------------------------- /code/sqlconnpool/sql_conn_pool.cpp: -------------------------------------------------------------------------------- 1 | #include "sql_conn_pool.h" 2 | 3 | using namespace std; 4 | 5 | SqlConnPool::SqlConnPool() 6 | { 7 | useConnCnt = 0; 8 | freeConnCnt = 0; 9 | } 10 | 11 | void SqlConnPool::init( 12 | const char* host, int port, 13 | const char* user, const char* pwd, 14 | const char* dbName, int maxConnCnt = 10) 15 | { 16 | assert(maxConnCnt > 0); 17 | for (int i = 0; i < maxConnCnt; i ++) 18 | { 19 | MYSQL *sql = nullptr; 20 | sql = mysql_init(sql); 21 | if (!sql) 22 | { 23 | LOG_ERROR("MySQL init error!"); 24 | assert(sql); 25 | } 26 | sql = mysql_real_connect(sql, host, user, pwd, dbName, port, nullptr, 0); 27 | if (!sql) 28 | { 29 | LOG_ERROR("MySQL Connect error"); 30 | } 31 | connQue.push(sql); 32 | } 33 | this->freeConnCnt = maxConnCnt; 34 | 35 | mtxPool = new mtx(); 36 | semFree = new sem(maxConnCnt); 37 | } 38 | 39 | void SqlConnPool::destroy() 40 | { 41 | this->mtxPool->lock(); 42 | while (!connQue.empty()) 43 | { 44 | MYSQL* item = connQue.front(); 45 | connQue.pop(); 46 | mysql_close(item); 47 | } 48 | this->mtxPool->unlock(); 49 | delete(mtxPool); 50 | delete(semFree); 51 | mysql_library_end(); 52 | } 53 | 54 | SqlConnPool* SqlConnPool::instance() 55 | { 56 | static SqlConnPool connPool; 57 | return &connPool; 58 | } 59 | 60 | MYSQL* SqlConnPool::getConn() 61 | { 62 | MYSQL *sql = nullptr; 63 | 64 | this->semFree->wait(); 65 | this->mtxPool->lock(); 66 | sql = connQue.front(); 67 | connQue.pop(); 68 | this->useConnCnt++; 69 | this->freeConnCnt--; 70 | this->mtxPool->unlock(); 71 | 72 | return sql; 73 | } 74 | 75 | void SqlConnPool::releaseConn(MYSQL *sql) 76 | { 77 | assert(sql); 78 | this->mtxPool->lock(); 79 | connQue.push(sql); 80 | this->useConnCnt--; 81 | this->freeConnCnt++; 82 | this->mtxPool->unlock(); 83 | this->semFree->post(); 84 | } 85 | 86 | int SqlConnPool::getFreeConnCnt() 87 | { 88 | this->mtxPool->lock(); 89 | return connQue.size(); 90 | this->mtxPool->unlock(); 91 | } 92 | 93 | SqlConnPool::~SqlConnPool() 94 | { 95 | this->destroy(); 96 | } 97 | 98 | SqlConnect::SqlConnect(MYSQL** psql, SqlConnPool *sqlConnPool) 99 | { 100 | assert(sqlConnPool); 101 | this->sql = sqlConnPool->getConn(); 102 | *psql = this->sql; 103 | this->sqlConnPool = sqlConnPool; 104 | } 105 | 106 | SqlConnect::~SqlConnect() 107 | { 108 | if (this->sql) 109 | { 110 | this->sqlConnPool->releaseConn(this->sql); 111 | } 112 | } -------------------------------------------------------------------------------- /code/sqlconnpool/sql_conn_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef SQL_CONN_POOL_H 2 | #define SQL_CONN_POOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "../lock/locker.h" 9 | #include 10 | #include 11 | #include "../log/log.h" 12 | 13 | using namespace std; 14 | 15 | class SqlConnPool 16 | { 17 | public: 18 | void init(const char* host, int port, 19 | const char* user, const char* pwd, 20 | const char* dbName, int maxConnCnt); 21 | 22 | void destroy(); 23 | 24 | static SqlConnPool *instance(); 25 | 26 | MYSQL *getConn(); 27 | void releaseConn(MYSQL *conn); 28 | int getFreeConnCnt(); 29 | 30 | private: 31 | SqlConnPool(); 32 | ~SqlConnPool(); 33 | 34 | int useConnCnt; 35 | int freeConnCnt; 36 | 37 | queue connQue; 38 | mtx *mtxPool; 39 | sem *semFree; 40 | }; 41 | 42 | 43 | class SqlConnect 44 | { 45 | public: 46 | SqlConnect(MYSQL** psql, SqlConnPool *sqlConnPool); 47 | ~SqlConnect(); 48 | 49 | private: 50 | MYSQL *sql; 51 | SqlConnPool *sqlConnPool; 52 | }; 53 | 54 | #endif -------------------------------------------------------------------------------- /code/threadpool/threadpool.h: -------------------------------------------------------------------------------- 1 | #ifndef THREADPOOL_H 2 | #define THREADPOOL_H 3 | 4 | #include "../lock/locker.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | class ThreadPool 14 | { 15 | public: 16 | static ThreadPool* instance() 17 | { 18 | static ThreadPool threadpool; 19 | return &threadpool; 20 | } 21 | 22 | /* 回调函数:从线程池的任务队列中选一个任务处理 */ 23 | static void callback(ThreadPool* pool) 24 | { 25 | while(true) 26 | { 27 | pool->mtxPool.lock(); 28 | while (pool->tasks.empty() && !pool->shutdown) 29 | { 30 | pool->condNotEmpty.wait(pool->mtxPool.get()); 31 | } 32 | 33 | if (pool->shutdown) 34 | { 35 | pool->mtxPool.unlock(); 36 | break; 37 | } 38 | 39 | //函数指针需要用move变成右值 40 | auto task = move(pool->tasks.front()); 41 | pool->tasks.pop(); 42 | pool->mtxPool.unlock(); 43 | task(); //这里是用bind打包好的函数及其参数,可直接执行 44 | } 45 | } 46 | 47 | void init (int threadNum = 8, int maxRequests = 10000) 48 | { 49 | this->threadNum = threadNum; 50 | this->maxRequests = maxRequests; 51 | assert(threadNum > 0); 52 | //初始化时开辟所有线程,无任务就阻塞 53 | for (int i = 0; i < threadNum; i ++) 54 | { 55 | //线程分离,主线程不用负责回收子线程资源 56 | thread(callback, this).detach(); 57 | } 58 | } 59 | 60 | /* 添加任务 */ 61 | //传入方法和参数打包后的函数对象,&&表示右值引用 62 | template 63 | void addTask(F&& task) 64 | { 65 | mtxPool.lock(); 66 | if ((int)tasks.size() < maxRequests) 67 | { 68 | //利用forward进行完美转发,保持右值引用属性 69 | tasks.emplace(forward(task)); 70 | condNotEmpty.signal(); 71 | } 72 | mtxPool.unlock(); 73 | } 74 | 75 | private: 76 | mtx mtxPool; 77 | cond condNotEmpty; 78 | int threadNum; 79 | int maxRequests; 80 | bool shutdown; 81 | //function可代替函数指针,可用bind将函数指针与参数绑定 82 | queue> tasks; 83 | 84 | ThreadPool(){} 85 | ~ThreadPool() 86 | { 87 | mtxPool.lock(); 88 | shutdown = true; //诱导子线程自己销毁 89 | mtxPool.unlock(); 90 | condNotEmpty.broadcast(); 91 | } 92 | }; 93 | 94 | #endif -------------------------------------------------------------------------------- /code/timer/heaptimer.cpp: -------------------------------------------------------------------------------- 1 | #include "heaptimer.h" 2 | 3 | /* 数组模拟堆 4 | 堆是一个完全二叉树,每个点都小于等于左右结点,根结点也即堆顶上全部数据的最小值(小根堆) 5 | 如果用0号作为根结点,那么结点x的左结点是 2x+1,右结点是2x+2 6 | 那么结点x的父结点就是 (x-1)/2 7 | 8 | swap操作:交换两个结点,同时更新哈希表内每个定时器的下标 9 | down操作:比左或右结点大,与左右结点的最小值交换 10 | up操作:比父结点小,与父结点交换 11 | 12 | 我们的需求有以下几个: 13 | 增加定时器:查哈希表,如果是新定时器,插在最后,再up,如果不是新定时器,就需要调整定时器 14 | 调整定时器:更新定时后,再执行down和up(实际上只会执行一个) 15 | 删除定时器:与最后一个元素交换,删除末尾元素,然后再down和up(删除任意位置结点,同样也只会执行一个) 16 | */ 17 | void HeapTimer::swapNode(size_t i, size_t j) 18 | { 19 | size_t s = heap.size(); 20 | assert(i >= 0 && i < s); 21 | assert(j >= 0 && j < s); 22 | std::swap(heap[i], heap[j]); 23 | ref[heap[i].id] = i; 24 | ref[heap[j].id] = j; 25 | } 26 | 27 | void HeapTimer::siftup(size_t i) 28 | { 29 | assert(i >= 0 && i < heap.size()); 30 | 31 | size_t j = (i - 1) / 2; 32 | while(j >= 0 && heap[i] < heap[j]) 33 | { 34 | swapNode(i, j); 35 | i = j; 36 | j = (i - 1) / 2; 37 | } 38 | } 39 | 40 | void HeapTimer::siftdown(size_t i) 41 | { 42 | size_t s = heap.size(); 43 | assert(i >= 0 && i < s); 44 | size_t t = i * 2 + 1; 45 | 46 | while (t < s) 47 | { 48 | if (t + 1 < s && heap[t + 1] < heap[t]) t++; 49 | if (heap[i] < heap[t]) break; 50 | swap(heap[i], heap[t]); 51 | i = t, t = i * 2 + 1; 52 | } 53 | } 54 | 55 | void HeapTimer::add(int id, int timeout, const TimeoutCallBack& cb) 56 | { 57 | assert(id >= 0); 58 | size_t i; 59 | if(!ref.count(id)) 60 | { 61 | i = heap.size(); 62 | ref[id] = i; 63 | heap.push_back({id, Clock::now() + MS(timeout), cb}); 64 | siftup(i); 65 | } 66 | else 67 | { 68 | i = ref[id]; 69 | heap[i].expires = Clock::now() + MS(timeout); 70 | heap[i].cb = cb; 71 | siftdown(i); 72 | siftup(i); 73 | } 74 | } 75 | 76 | void HeapTimer::del(size_t i) 77 | { 78 | assert(!heap.empty() && i >= 0 && i < heap.size()); 79 | size_t n = heap.size() - 1; 80 | swapNode(i, n); 81 | 82 | ref.erase(heap.back().id); 83 | heap.pop_back(); 84 | //如果堆空就不用调整了 85 | if (!heap.empty()) 86 | { 87 | siftdown(i); 88 | siftup(i); 89 | } 90 | } 91 | 92 | void HeapTimer::adjust(int id, int timeout) 93 | { 94 | assert(!heap.empty() && ref.count(id)); 95 | heap[ref[id]].expires = Clock::now() + MS(timeout); 96 | siftdown(ref[id]); 97 | siftup(ref[id]); 98 | } 99 | 100 | void HeapTimer::tick() 101 | { 102 | if (heap.empty()) return; 103 | 104 | while (!heap.empty()) 105 | { 106 | TimerNode node = heap.front(); 107 | 108 | if (std::chrono::duration_cast(node.expires - Clock::now()).count() > 0) 109 | { 110 | break; 111 | } 112 | node.cb(); 113 | pop(); 114 | } 115 | } 116 | 117 | void HeapTimer::pop() 118 | { 119 | assert(!heap.empty()); 120 | del(0); 121 | } 122 | 123 | void HeapTimer::clear() 124 | { 125 | ref.clear(); 126 | heap.clear(); 127 | } 128 | 129 | int HeapTimer::getNextTick() 130 | { 131 | //处理堆顶计时器,若超时执行回调再删除 132 | tick(); 133 | size_t res = -1; 134 | if(!heap.empty()) 135 | { 136 | //计算现在堆顶的超时时间,到期时先唤醒一次epoll,判断是否有新事件(即便已经超时) 137 | res = std::chrono::duration_cast(heap.front().expires - Clock::now()).count(); 138 | if(res < 0) { res = 0; } 139 | } 140 | return res; 141 | } -------------------------------------------------------------------------------- /code/timer/heaptimer.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAPTIMER_H 2 | #define HEAPTIMER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "../log/log.h" 13 | 14 | typedef std::function TimeoutCallBack; 15 | typedef std::chrono::high_resolution_clock Clock; 16 | typedef std::chrono::milliseconds MS; 17 | typedef Clock::time_point TimeStamp; 18 | 19 | //定时器结点 20 | struct TimerNode 21 | { 22 | int id; //连接套接字描述符 23 | TimeStamp expires; //到期时间 24 | TimeoutCallBack cb; //回调函数 25 | //重载<,到期时间近的排在前面 26 | bool operator<(const TimerNode& t) 27 | { 28 | return expires < t.expires; 29 | } 30 | }; 31 | 32 | //定时器容器 33 | class HeapTimer 34 | { 35 | public: 36 | //reserve增加capacity,不改变size 37 | HeapTimer() { heap.reserve(64); } 38 | 39 | ~HeapTimer() { clear(); } 40 | 41 | //调整到期时间 42 | void adjust(int id, int newExpires); 43 | 44 | //增加定时器 45 | void add(int id, int timeOut, const TimeoutCallBack& cb); 46 | 47 | //回收空间 48 | void clear(); 49 | 50 | //定时器计时,到期执行回调(从堆顶开始处理) 51 | void tick(); 52 | 53 | //弹出最近的一个定时器 54 | void pop(); 55 | 56 | //返回最近的到期时间 57 | int getNextTick(); 58 | 59 | private: 60 | void del(size_t i); 61 | 62 | void siftup(size_t i); 63 | 64 | void siftdown(size_t i); 65 | 66 | void swapNode(size_t i, size_t j); 67 | 68 | //数组模拟堆 69 | std::vector heap; 70 | 71 | //记录每个定时器的下标 72 | std::unordered_map ref; 73 | }; 74 | 75 | #endif -------------------------------------------------------------------------------- /log/2023_03_24.log: -------------------------------------------------------------------------------- 1 | 2023-03-24 07:06:19.249448 [info] : Client[18](127.0.0.1:56969) in, userCount:1 2 | 2023-03-24 07:06:19.251140 [info] : Client[18] in! 3 | 2023-03-24 07:06:19.286414 [info] : Client[18] quit! 4 | 2023-03-24 07:06:19.286479 [info] : Client[18](127.0.0.1:56969) quit, UserCount:0 5 | 2023-03-24 07:06:19.722431 [info] : Client[18](127.0.0.1:59529) in, userCount:1 6 | 2023-03-24 07:06:19.722582 [info] : Client[18] in! 7 | 2023-03-24 07:06:19.749116 [info] : Client[23](127.0.0.1:62089) in, userCount:2 8 | 2023-03-24 07:06:19.749327 [info] : Client[23] in! 9 | 2023-03-24 07:06:19.750750 [info] : Client[25](127.0.0.1:65161) in, userCount:3 10 | 2023-03-24 07:06:19.750781 [info] : Client[25] in! 11 | 2023-03-24 07:06:19.751088 [info] : Client[26](127.0.0.1:650) in, userCount:4 12 | 2023-03-24 07:06:19.751103 [info] : Client[26] in! 13 | 2023-03-24 07:06:19.751236 [info] : Client[27](127.0.0.1:1162) in, userCount:5 14 | 2023-03-24 07:06:19.751272 [info] : Client[27] in! 15 | 2023-03-24 07:06:19.751277 [info] : Client[24](127.0.0.1:1674) in, userCount:6 16 | 2023-03-24 07:06:19.751280 [info] : Client[24] in! 17 | 2023-03-24 07:06:19.751285 [info] : Client[28](127.0.0.1:3210) in, userCount:7 18 | 2023-03-24 07:06:19.751288 [info] : Client[28] in! 19 | 2023-03-24 07:06:19.751292 [info] : Client[18] quit! 20 | 2023-03-24 07:06:19.751342 [info] : Client[18](127.0.0.1:59529) quit, UserCount:6 21 | 2023-03-24 07:06:19.751347 [info] : Client[25] quit! 22 | 2023-03-24 07:06:19.751354 [info] : Client[25](127.0.0.1:65161) quit, UserCount:5 23 | 2023-03-24 07:06:19.751356 [info] : Client[26] quit! 24 | 2023-03-24 07:06:19.751361 [info] : Client[26](127.0.0.1:650) quit, UserCount:4 25 | 2023-03-24 07:06:19.753092 [info] : Client[26](127.0.0.1:5770) in, userCount:5 26 | 2023-03-24 07:06:19.753122 [info] : Client[26] in! 27 | 2023-03-24 07:06:19.753137 [info] : Client[29](127.0.0.1:7818) in, userCount:6 28 | 2023-03-24 07:06:19.753148 [info] : Client[29] in! 29 | 2023-03-24 07:06:19.753170 [info] : Client[30](127.0.0.1:9866) in, userCount:7 30 | 2023-03-24 07:06:19.753180 [info] : Client[30] in! 31 | 2023-03-24 07:06:19.753196 [info] : Client[31](127.0.0.1:12938) in, userCount:8 32 | 2023-03-24 07:06:19.753207 [info] : Client[31] in! 33 | 2023-03-24 07:06:19.753218 [info] : Client[23] quit! 34 | 2023-03-24 07:06:19.753250 [info] : Client[23](127.0.0.1:62089) quit, UserCount:7 35 | 2023-03-24 07:06:19.753262 [info] : Client[27] quit! 36 | 2023-03-24 07:06:19.753303 [info] : Client[27](127.0.0.1:1162) quit, UserCount:6 37 | 2023-03-24 07:06:19.753317 [info] : Client[29] quit! 38 | 2023-03-24 07:06:19.753331 [info] : Client[29](127.0.0.1:7818) quit, UserCount:5 39 | 2023-03-24 07:06:19.753354 [info] : Client[18](127.0.0.1:16522) in, userCount:6 40 | 2023-03-24 07:06:19.753751 [info] : Client[18] in! 41 | 2023-03-24 07:06:19.753848 [info] : Client[28] quit! 42 | 2023-03-24 07:06:19.753972 [info] : Client[28](127.0.0.1:3210) quit, UserCount:5 43 | 2023-03-24 07:06:19.754088 [info] : Client[24] quit! 44 | 2023-03-24 07:06:19.754124 [info] : Client[24](127.0.0.1:1674) quit, UserCount:4 45 | 2023-03-24 07:06:19.754146 [info] : Client[26] quit! 46 | 2023-03-24 07:06:19.754176 [info] : Client[26](127.0.0.1:5770) quit, UserCount:3 47 | 2023-03-24 07:06:19.754293 [info] : Client[30] quit! 48 | 2023-03-24 07:06:19.754363 [info] : Client[30](127.0.0.1:9866) quit, UserCount:2 49 | 2023-03-24 07:06:19.754702 [info] : Client[31] quit! 50 | 2023-03-24 07:06:19.754732 [info] : Client[31](127.0.0.1:12938) quit, UserCount:1 51 | 2023-03-24 07:06:19.754781 [info] : Client[18] quit! 52 | 2023-03-24 07:06:19.754801 [info] : Client[18](127.0.0.1:16522) quit, UserCount:0 53 | 2023-03-24 07:06:19.755590 [info] : Client[18](127.0.0.1:17546) in, userCount:1 54 | 2023-03-24 07:06:19.755609 [info] : Client[18] in! 55 | 2023-03-24 07:06:19.756274 [info] : Client[18] quit! 56 | 2023-03-24 07:06:19.756302 [info] : Client[18](127.0.0.1:17546) quit, UserCount:0 57 | 2023-03-24 07:06:20.097411 [info] : Client[18](127.0.0.1:20106) in, userCount:1 58 | 2023-03-24 07:06:20.097461 [info] : Client[18] in! 59 | 2023-03-24 07:06:20.098525 [info] : Client[18] quit! 60 | 2023-03-24 07:06:20.098714 [info] : Client[18](127.0.0.1:20106) quit, UserCount:0 61 | -------------------------------------------------------------------------------- /resources/400.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | SORRY 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 | 36 | 60 | 61 |
62 |
63 |
64 | 65 |
66 | about image 68 |
69 |
70 |

400 无法解析请求

71 |
72 |
73 |
74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /resources/403.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | SORRY 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 | 36 | 60 | 61 |
62 |
63 |
64 | 65 |
66 | about image 68 |
69 |
70 |

403 禁止访问

71 |
72 |
73 |
74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /resources/404.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | SORRY 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 | 36 | 60 | 61 |
62 |
63 |
64 | 65 |
66 | about image 68 |
69 |
70 |

404 没有找到该页面

71 |
72 |
73 |
74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /resources/405.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | SORRY 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 | 36 | 60 | 61 |
62 |
63 |
64 | 65 |
66 | about image 68 |
69 |
70 |

404 没有找到该页面

71 |
72 |
73 |
74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /resources/css/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/css/.DS_Store -------------------------------------------------------------------------------- /resources/css/magnific-popup.css: -------------------------------------------------------------------------------- 1 | /* Magnific Popup CSS */ 2 | .mfp-bg { 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 1042; 8 | overflow: hidden; 9 | position: fixed; 10 | background: #0b0b0b; 11 | opacity: 0.8; } 12 | 13 | .mfp-wrap { 14 | top: 0; 15 | left: 0; 16 | width: 100%; 17 | height: 100%; 18 | z-index: 1043; 19 | position: fixed; 20 | outline: none !important; 21 | -webkit-backface-visibility: hidden; } 22 | 23 | .mfp-container { 24 | text-align: center; 25 | position: absolute; 26 | width: 100%; 27 | height: 100%; 28 | left: 0; 29 | top: 0; 30 | padding: 0 8px; 31 | box-sizing: border-box; } 32 | 33 | .mfp-container:before { 34 | content: ''; 35 | display: inline-block; 36 | height: 100%; 37 | vertical-align: middle; } 38 | 39 | .mfp-align-top .mfp-container:before { 40 | display: none; } 41 | 42 | .mfp-content { 43 | position: relative; 44 | display: inline-block; 45 | vertical-align: middle; 46 | margin: 0 auto; 47 | text-align: left; 48 | z-index: 1045; } 49 | 50 | .mfp-inline-holder .mfp-content, 51 | .mfp-ajax-holder .mfp-content { 52 | width: 100%; 53 | cursor: auto; } 54 | 55 | .mfp-ajax-cur { 56 | cursor: progress; } 57 | 58 | .mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close { 59 | cursor: -moz-zoom-out; 60 | cursor: -webkit-zoom-out; 61 | cursor: zoom-out; } 62 | 63 | .mfp-zoom { 64 | cursor: pointer; 65 | cursor: -webkit-zoom-in; 66 | cursor: -moz-zoom-in; 67 | cursor: zoom-in; } 68 | 69 | .mfp-auto-cursor .mfp-content { 70 | cursor: auto; } 71 | 72 | .mfp-close, 73 | .mfp-arrow, 74 | .mfp-preloader, 75 | .mfp-counter { 76 | -webkit-user-select: none; 77 | -moz-user-select: none; 78 | user-select: none; } 79 | 80 | .mfp-loading.mfp-figure { 81 | display: none; } 82 | 83 | .mfp-hide { 84 | display: none !important; } 85 | 86 | .mfp-preloader { 87 | color: #CCC; 88 | position: absolute; 89 | top: 50%; 90 | width: auto; 91 | text-align: center; 92 | margin-top: -0.8em; 93 | left: 8px; 94 | right: 8px; 95 | z-index: 1044; } 96 | .mfp-preloader a { 97 | color: #CCC; } 98 | .mfp-preloader a:hover { 99 | color: #FFF; } 100 | 101 | .mfp-s-ready .mfp-preloader { 102 | display: none; } 103 | 104 | .mfp-s-error .mfp-content { 105 | display: none; } 106 | 107 | button.mfp-close, 108 | button.mfp-arrow { 109 | overflow: visible; 110 | cursor: pointer; 111 | background: transparent; 112 | border: 0; 113 | -webkit-appearance: none; 114 | display: block; 115 | outline: none; 116 | padding: 0; 117 | z-index: 1046; 118 | box-shadow: none; 119 | touch-action: manipulation; } 120 | 121 | button::-moz-focus-inner { 122 | padding: 0; 123 | border: 0; } 124 | 125 | .mfp-close { 126 | width: 44px; 127 | height: 44px; 128 | line-height: 44px; 129 | position: absolute; 130 | right: 0; 131 | top: 0; 132 | text-decoration: none; 133 | text-align: center; 134 | opacity: 0.65; 135 | padding: 0 0 18px 10px; 136 | color: #FFF; 137 | font-style: normal; 138 | font-size: 28px; 139 | font-family: Arial, Baskerville, monospace; } 140 | .mfp-close:hover, 141 | .mfp-close:focus { 142 | opacity: 1; } 143 | .mfp-close:active { 144 | top: 1px; } 145 | 146 | .mfp-close-btn-in .mfp-close { 147 | color: #333; } 148 | 149 | .mfp-image-holder .mfp-close, 150 | .mfp-iframe-holder .mfp-close { 151 | color: #FFF; 152 | right: -6px; 153 | text-align: right; 154 | padding-right: 6px; 155 | width: 100%; } 156 | 157 | .mfp-counter { 158 | position: absolute; 159 | top: 0; 160 | right: 0; 161 | color: #CCC; 162 | font-size: 12px; 163 | line-height: 18px; 164 | white-space: nowrap; } 165 | 166 | .mfp-arrow { 167 | position: absolute; 168 | opacity: 0.65; 169 | margin: 0; 170 | top: 50%; 171 | margin-top: -55px; 172 | padding: 0; 173 | width: 90px; 174 | height: 110px; 175 | -webkit-tap-highlight-color: transparent; } 176 | .mfp-arrow:active { 177 | margin-top: -54px; } 178 | .mfp-arrow:hover, 179 | .mfp-arrow:focus { 180 | opacity: 1; } 181 | .mfp-arrow:before, 182 | .mfp-arrow:after { 183 | content: ''; 184 | display: block; 185 | width: 0; 186 | height: 0; 187 | position: absolute; 188 | left: 0; 189 | top: 0; 190 | margin-top: 35px; 191 | margin-left: 35px; 192 | border: medium inset transparent; } 193 | .mfp-arrow:after { 194 | border-top-width: 13px; 195 | border-bottom-width: 13px; 196 | top: 8px; } 197 | .mfp-arrow:before { 198 | border-top-width: 21px; 199 | border-bottom-width: 21px; 200 | opacity: 0.7; } 201 | 202 | .mfp-arrow-left { 203 | left: 0; } 204 | .mfp-arrow-left:after { 205 | border-right: 17px solid #FFF; 206 | margin-left: 31px; } 207 | .mfp-arrow-left:before { 208 | margin-left: 25px; 209 | border-right: 27px solid #3F3F3F; } 210 | 211 | .mfp-arrow-right { 212 | right: 0; } 213 | .mfp-arrow-right:after { 214 | border-left: 17px solid #FFF; 215 | margin-left: 39px; } 216 | .mfp-arrow-right:before { 217 | border-left: 27px solid #3F3F3F; } 218 | 219 | .mfp-iframe-holder { 220 | padding-top: 40px; 221 | padding-bottom: 40px; } 222 | .mfp-iframe-holder .mfp-content { 223 | line-height: 0; 224 | width: 100%; 225 | max-width: 900px; } 226 | .mfp-iframe-holder .mfp-close { 227 | top: -40px; } 228 | 229 | .mfp-iframe-scaler { 230 | width: 100%; 231 | height: 0; 232 | overflow: hidden; 233 | padding-top: 56.25%; } 234 | .mfp-iframe-scaler iframe { 235 | position: absolute; 236 | display: block; 237 | top: 0; 238 | left: 0; 239 | width: 100%; 240 | height: 100%; 241 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.6); 242 | background: #000; } 243 | 244 | /* Main image in popup */ 245 | img.mfp-img { 246 | width: auto; 247 | max-width: 100%; 248 | height: auto; 249 | display: block; 250 | line-height: 0; 251 | box-sizing: border-box; 252 | padding: 40px 0 40px; 253 | margin: 0 auto; } 254 | 255 | /* The shadow behind the image */ 256 | .mfp-figure { 257 | line-height: 0; } 258 | .mfp-figure:after { 259 | content: ''; 260 | position: absolute; 261 | left: 0; 262 | top: 40px; 263 | bottom: 40px; 264 | display: block; 265 | right: 0; 266 | width: auto; 267 | height: auto; 268 | z-index: -1; 269 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.6); 270 | background: #444; } 271 | .mfp-figure small { 272 | color: #BDBDBD; 273 | display: block; 274 | font-size: 12px; 275 | line-height: 14px; } 276 | .mfp-figure figure { 277 | margin: 0; } 278 | 279 | .mfp-bottom-bar { 280 | margin-top: -36px; 281 | position: absolute; 282 | top: 100%; 283 | left: 0; 284 | width: 100%; 285 | cursor: auto; } 286 | 287 | .mfp-title { 288 | text-align: left; 289 | line-height: 18px; 290 | color: #F3F3F3; 291 | word-wrap: break-word; 292 | padding-right: 36px; } 293 | 294 | .mfp-image-holder .mfp-content { 295 | max-width: 100%; } 296 | 297 | .mfp-gallery .mfp-image-holder .mfp-figure { 298 | cursor: pointer; } 299 | 300 | @media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) { 301 | /** 302 | * Remove all paddings around the image on small screen 303 | */ 304 | .mfp-img-mobile .mfp-image-holder { 305 | padding-left: 0; 306 | padding-right: 0; } 307 | .mfp-img-mobile img.mfp-img { 308 | padding: 0; } 309 | .mfp-img-mobile .mfp-figure:after { 310 | top: 0; 311 | bottom: 0; } 312 | .mfp-img-mobile .mfp-figure small { 313 | display: inline; 314 | margin-left: 5px; } 315 | .mfp-img-mobile .mfp-bottom-bar { 316 | background: rgba(0, 0, 0, 0.6); 317 | bottom: 0; 318 | margin: 0; 319 | top: auto; 320 | padding: 3px 5px; 321 | position: fixed; 322 | box-sizing: border-box; } 323 | .mfp-img-mobile .mfp-bottom-bar:empty { 324 | padding: 0; } 325 | .mfp-img-mobile .mfp-counter { 326 | right: 5px; 327 | top: 3px; } 328 | .mfp-img-mobile .mfp-close { 329 | top: 0; 330 | right: 0; 331 | width: 35px; 332 | height: 35px; 333 | line-height: 35px; 334 | background: rgba(0, 0, 0, 0.6); 335 | position: fixed; 336 | text-align: center; 337 | padding: 0; } } 338 | 339 | @media all and (max-width: 900px) { 340 | .mfp-arrow { 341 | -webkit-transform: scale(0.75); 342 | transform: scale(0.75); } 343 | .mfp-arrow-left { 344 | -webkit-transform-origin: 0; 345 | transform-origin: 0; } 346 | .mfp-arrow-right { 347 | -webkit-transform-origin: 100%; 348 | transform-origin: 100%; } 349 | .mfp-container { 350 | padding-left: 6px; 351 | padding-right: 6px; } } 352 | -------------------------------------------------------------------------------- /resources/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | @import url('https://fonts.googleapis.com/css?family=Muli:200,300,400'); 3 | 4 | body { 5 | background: #ffffff; 6 | font-family: 'Muli', sans-serif; 7 | font-style: normal; 8 | font-weight: 300; 9 | overflow-x: hidden; 10 | } 11 | 12 | html, body { 13 | width: 100%; 14 | height: 100%; 15 | } 16 | 17 | 18 | 19 | /*--------------------------------------- 20 | Typorgraphy 21 | -----------------------------------------*/ 22 | 23 | h1,h2,h3,h4,h5,h6 { 24 | font-style: normal; 25 | font-weight: 200; 26 | letter-spacing: 0px; 27 | } 28 | 29 | h1 { 30 | color: #3d3d3f; 31 | font-size: 50px; 32 | line-height: normal; 33 | } 34 | 35 | h2 { 36 | color: #575757; 37 | font-size: 40px; 38 | line-height: 52px; 39 | margin-top: 0px; 40 | } 41 | 42 | h4 { 43 | color: #797979; 44 | font-size: 18px; 45 | font-weight: normal; 46 | } 47 | 48 | p { 49 | color: #878787; 50 | font-size: 16px; 51 | font-weight: 300; 52 | line-height: 25px; 53 | letter-spacing: 0.2px; 54 | } 55 | 56 | strong, span { 57 | color: #878787; 58 | font-weight: normal; 59 | } 60 | 61 | 62 | 63 | /*--------------------------------------- 64 | Buttons 65 | -----------------------------------------*/ 66 | 67 | .section-btn { 68 | background: #d7b065; 69 | border: none; 70 | border-radius: 50px; 71 | color: #ffffff; 72 | font-size: 13px; 73 | font-weight: bold; 74 | letter-spacing: 1.6px; 75 | padding: 14px 32px 18px 32px; 76 | margin-top: 32px; 77 | -webkit-transition: all ease-in-out 0.4s; 78 | transition: all ease-in-out 0.4s; 79 | } 80 | 81 | .section-btn:focus, 82 | .section-btn:hover { 83 | background: #000000; 84 | color: #ffffff; 85 | } 86 | 87 | .copyrights{ 88 | text-indent:-9999px; 89 | height:0; 90 | line-height:0; 91 | font-size:0; 92 | overflow:hidden; 93 | } 94 | 95 | /*--------------------------------------- 96 | General 97 | -----------------------------------------*/ 98 | 99 | html{ 100 | -webkit-font-smoothing: antialiased; 101 | } 102 | 103 | a { 104 | color: #575757; 105 | -webkit-transition: 0.5s; 106 | transition: 0.5s; 107 | text-decoration: none !important; 108 | } 109 | 110 | a:hover, a:active, a:focus { 111 | color: #000000; 112 | outline: none; 113 | } 114 | 115 | * { 116 | -webkit-box-sizing: border-box; 117 | box-sizing: border-box; 118 | } 119 | 120 | *:before, 121 | *:after { 122 | -webkit-box-sizing: border-box; 123 | box-sizing: border-box; 124 | } 125 | 126 | .section-title { 127 | margin: 0; 128 | padding-bottom: 32px; 129 | } 130 | 131 | #about, #work, 132 | #contact { 133 | position: relative; 134 | padding-top: 80px; 135 | padding-bottom: 80px; 136 | } 137 | 138 | #about img, #team img { 139 | border-radius: 5px; 140 | } 141 | 142 | #work { 143 | border-top: 1px solid #f0f0f0; 144 | border-bottom: 1px solid #f0f0f0; 145 | } 146 | 147 | #contact { 148 | text-align: center; 149 | } 150 | 151 | 152 | 153 | /*--------------------------------------- 154 | Pre loader section 155 | -----------------------------------------*/ 156 | 157 | .preloader { 158 | position: fixed; 159 | top: 0; 160 | left: 0; 161 | width: 100%; 162 | height: 100%; 163 | z-index: 99999; 164 | display: flex; 165 | flex-flow: row nowrap; 166 | justify-content: center; 167 | align-items: center; 168 | background: none repeat scroll 0 0 #ffffff; 169 | } 170 | 171 | .spinner { 172 | border: 1px solid transparent; 173 | border-radius: 5px; 174 | position: relative; 175 | } 176 | 177 | .spinner:before { 178 | content: ''; 179 | box-sizing: border-box; 180 | position: absolute; 181 | top: 50%; 182 | left: 50%; 183 | width: 65px; 184 | height: 65px; 185 | margin-top: -10px; 186 | margin-left: -10px; 187 | border-radius: 50%; 188 | border: 1px solid #000000; 189 | border-top-color: #f9f9f9; 190 | animation: spinner .9s linear infinite; 191 | } 192 | 193 | @-webkit-@keyframes spinner { 194 | to {transform: rotate(360deg);} 195 | } 196 | 197 | @keyframes spinner { 198 | to {transform: rotate(360deg);} 199 | } 200 | 201 | 202 | 203 | /*--------------------------------------- 204 | Navigation section 205 | -----------------------------------------*/ 206 | 207 | .custom-navbar { 208 | border: none; 209 | margin-bottom: 0; 210 | background-color: #ffffff; 211 | padding-top: 22px; 212 | } 213 | 214 | .custom-navbar .navbar-brand { 215 | color: #444; 216 | font-weight: normal; 217 | font-size: 20px; 218 | } 219 | 220 | .custom-navbar .nav li a { 221 | font-size: 12px; 222 | font-weight: normal; 223 | color: #656565; 224 | letter-spacing: 1px; 225 | -webkit-transition: all ease-in-out 0.4s; 226 | transition: all ease-in-out 0.4s; 227 | padding: 0; 228 | margin: 15px; 229 | } 230 | 231 | .custom-navbar .navbar-nav > li > a:hover, 232 | .custom-navbar .navbar-nav > li > a:focus { 233 | background-color: transparent; 234 | color: #454545; 235 | } 236 | 237 | .custom-navbar .navbar-nav li a:after { 238 | content: ""; 239 | position: absolute; 240 | display: block; 241 | width: 0px; 242 | height: 2px; 243 | margin: auto; 244 | background: transparent; 245 | transition: width .3s ease, background-color .3s ease; 246 | } 247 | 248 | .custom-navbar .navbar-nav li a:hover:after, 249 | .custom-navbar .nav li.active > a:after { 250 | background: #000000; 251 | color: #ffffff; 252 | width: 100%; 253 | } 254 | 255 | .custom-navbar .nav li.active > a { 256 | background-color: transparent; 257 | color: #454545; 258 | } 259 | 260 | .custom-navbar .navbar-toggle { 261 | border: none; 262 | padding-top: 12px; 263 | } 264 | 265 | .custom-navbar .navbar-toggle { 266 | background-color: transparent; 267 | } 268 | 269 | .custom-navbar .navbar-toggle .icon-bar { 270 | background: #000000; 271 | border-color: transparent; 272 | } 273 | 274 | @media(min-width:768px) { 275 | .custom-navbar { 276 | border-bottom: 0; 277 | background: 0 0; 278 | } 279 | .custom-navbar.top-nav-collapse { 280 | background: #ffffff; 281 | box-shadow:0 40px 100px rgba(0,0,0,.2); 282 | padding: 10px 0; 283 | } 284 | 285 | } 286 | 287 | 288 | 289 | /*--------------------------------------- 290 | Home section 291 | -----------------------------------------*/ 292 | 293 | #home { 294 | display: -webkit-box; 295 | display: -webkit-flex; 296 | display: -ms-flexbox; 297 | display: flex; 298 | -webkit-box-align: center; 299 | -webkit-align-items: center; 300 | -ms-flex-align: center; 301 | align-items: center; 302 | height: 100vh; 303 | position: relative; 304 | padding-top: 62px; 305 | } 306 | 307 | #home img { 308 | width: 120px; 309 | height: 120px; 310 | } 311 | 312 | 313 | /*--------------------------------------- 314 | About section 315 | -----------------------------------------*/ 316 | 317 | #about .section-title { 318 | padding-bottom: 16px; 319 | } 320 | 321 | #about .col-md-4 a { 322 | width: 100px; 323 | height: 100px; 324 | display: inline-block; 325 | margin: 6px 6px 0px 0; 326 | } 327 | 328 | #about .about-thumb { 329 | margin-top: 22px; 330 | } 331 | 332 | #about .about-thumb strong { 333 | font-weight: normal; 334 | display: block; 335 | padding-top: 4px; 336 | } 337 | 338 | 339 | 340 | /*--------------------------------------- 341 | Skill section 342 | -----------------------------------------*/ 343 | 344 | #skill { 345 | border-top: 1px solid #f0f0f0; 346 | padding-top: 80px; 347 | padding-bottom: 60px; 348 | } 349 | 350 | .skill-thumb strong, 351 | .skill-thumb span { 352 | color: #575757; 353 | font-size: 16px; 354 | padding-bottom: 8px; 355 | display: inline-block; 356 | } 357 | 358 | .skill-thumb .progress { 359 | background: #ffffff; 360 | border-radius: 5px; 361 | box-shadow: none; 362 | height: 4px; 363 | } 364 | 365 | .skill-thumb .progress-bar-primary { 366 | background: #3d3d3f; 367 | } 368 | 369 | 370 | 371 | /*--------------------------------------- 372 | Work section 373 | -----------------------------------------*/ 374 | 375 | #work .work-thumb { 376 | border-radius: 5px; 377 | margin-bottom: 15px; 378 | padding: 0; 379 | overflow: hidden; 380 | position: relative; 381 | top: 0; 382 | -webkit-transition: all ease-in-out 0.4s; 383 | transition: all ease-in-out 0.4s; 384 | } 385 | 386 | #work .work-thumb:hover { 387 | background: #ffffff; 388 | box-shadow: 0px 16px 22px 0px rgba(90, 91, 95, 0.3); 389 | top: -5px; 390 | } 391 | 392 | #work .work-thumb img { 393 | border-radius: 5px; 394 | } 395 | 396 | 397 | 398 | /*--------------------------------------- 399 | Contact section 400 | -----------------------------------------*/ 401 | 402 | #contact .form-control { 403 | border-radius: 0px; 404 | border-color: #f0f0f0; 405 | box-shadow: none; 406 | font-size: 16px; 407 | margin-top: 12px; 408 | margin-bottom: 12px; 409 | -webkit-transition: all ease-in-out 0.4s; 410 | transition: all ease-in-out 0.4s; 411 | } 412 | 413 | #contact .form-control:focus { 414 | border-bottom: 2px solid #999999; 415 | } 416 | 417 | #contact input { 418 | height: 55px; 419 | border: none; 420 | border-bottom: 1px solid #f0f0f0; 421 | } 422 | 423 | #contact button#submit { 424 | background: #000000; 425 | border: none; 426 | border-radius: 50px; 427 | color: #ffffff; 428 | font-weight: 300; 429 | height: 55px; 430 | padding-bottom: 10px; 431 | margin-top: 24px; 432 | } 433 | 434 | #contact button#submit:hover { 435 | background: #d7b065; 436 | color: #ffffff; 437 | } 438 | 439 | 440 | 441 | /*--------------------------------------- 442 | Social icon 443 | -----------------------------------------*/ 444 | 445 | .social-icon { 446 | position: relative; 447 | padding: 0; 448 | margin: 0; 449 | } 450 | 451 | .social-icon li { 452 | display: inline-block; 453 | list-style: none; 454 | } 455 | 456 | .social-icon li a { 457 | background: #292929; 458 | border-radius: 100%; 459 | color: #ffffff; 460 | cursor: pointer; 461 | font-size: 16px; 462 | text-decoration: none; 463 | transition: all 0.4s ease-in-out; 464 | width: 30px; 465 | height: 30px; 466 | line-height: 30px; 467 | text-align: center; 468 | vertical-align: middle; 469 | position: relative; 470 | margin: 20px 6px 10px 6px; 471 | } 472 | 473 | .social-icon li a:hover { 474 | background: #d7b065; 475 | transform: scale(1.1); 476 | } 477 | 478 | 479 | /*--------------------------------------- 480 | Mobile Responsive styles 481 | -----------------------------------------*/ 482 | 483 | @media (min-width: 768px) and (max-width: 1024px) { 484 | #home { 485 | height: 50vh; 486 | } 487 | } 488 | 489 | @media (min-width: 667px) and (max-width: 767px) { 490 | #home { 491 | height: 140vh; 492 | } 493 | } 494 | 495 | @media (min-width: 568px) and (max-width: 665px) { 496 | #home { 497 | height: 190vh; 498 | } 499 | } 500 | 501 | @media (max-width: 980px) { 502 | 503 | h1 {font-size: 33px;} 504 | 505 | #work .work-thumb { 506 | margin-top: 30px; 507 | } 508 | 509 | } 510 | 511 | 512 | @media (max-width: 768px) { 513 | 514 | h1 { 515 | font-size: 30px; 516 | line-height: normal; 517 | } 518 | 519 | h2 {font-size: 30px;} 520 | 521 | .custom-navbar { 522 | background-color: #ffffff; 523 | box-shadow:0 40px 100px rgba(0,0,0,.2); 524 | padding-top: 0px; 525 | padding-bottom: 5px; 526 | } 527 | 528 | .custom-navbar .nav { 529 | padding-bottom: 10px; 530 | } 531 | 532 | .custom-navbar .nav li a { 533 | display: inline-block; 534 | margin-bottom: 5px; 535 | } 536 | 537 | } 538 | 539 | 540 | @media (max-width: 580px) { 541 | 542 | #about .about-thumb { 543 | margin-top: 0px; 544 | } 545 | 546 | .about-thumb .social-icon { 547 | margin-bottom: 15px; 548 | } 549 | 550 | } 551 | 552 | 553 | @media (max-width: 357px) { 554 | 555 | h1 { 556 | font-size: 28px; 557 | } 558 | 559 | #about .col-md-4 a { 560 | width: 85px; 561 | height: 85px; 562 | } 563 | 564 | } 565 | -------------------------------------------------------------------------------- /resources/css/zyupload-1.0.0.min.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8";form{font-size:84%;margin:0;line-height:1.5;color:#333;font-family:Arial,sans-serif}a{color:#34538b;text-decoration:none}a:hover{text-decoration:underline}input,select,textarea{font-size:100%}#header{height:60px;padding:0 0 0 40px}#header .logo{margin-top:12px;overflow:hidden;float:left}#main{width:100%;background:#beceeb;overflow:hidden}#main h1{line-height:40px;margin:0;text-align:center;font-size:1.3em;background:#c1d5eb;font-family:'楷体','微软雅黑';text-shadow:0 1px 0 #f2f2f2}#body{padding:0;overflow:hidden}#body .part{width:50%;min-height:500px;_height:500px;background:white}#code{float:left;margin-left:-1px;margin-bottom:-999em;padding-bottom:999em}#effect{float:right;margin-right:-1px;margin-bottom:-999em;padding-bottom:999em}#body h3{line-height:30px;margin:0;font-size:1.1em;background:#f0f3f9;padding-left:10px;border-bottom:1px solid #ededed;color:#4e4e4e;text-shadow:0 1px 0 white}#footer{line-height:1.3;padding:15px 0;border-top:1px solid #486aaa;font-family:'Lucida Grande',Verdana,Sans-Serif;text-align:center;text-shadow:1px 1px #cad5eb}#footer:before{display:block;height:1px;content:'.';background-color:#909baf;color:#aaa;overflow:hidden;position:relative;top:-15px}#footer img{margin-bottom:-3px}pre{font-family:'simsun'}#ad{width:468px;height:60px;margin:0 auto}.light{background:#f0f3f9}#content{min-height:500px;_height:500px;background:white;border:solid #cad5eb;border-width:0 2px;font-family:'Lucida Grande',Verdana}.article{font-family:Arial;padding:10px 0;font-size:.86em;clear:both}.article_new{width:960px;margin:-33px auto 0;font-family:Arial;padding:10px 0;font-size:.86em;clear:both;text-align:right}#back{margin-top:-25px;position:absolute;right:10px}.upload_box{margin:1em auto}.upload_main{border-width:1px 1px 2px;border-style:solid;border-color:#ccc #ccc #ddd;background-color:#fbfbfb}.upload_choose{padding:1em}.upload_drag_area{display:inline-block;width:63%;padding:4em 0;margin-left:.5em;border:1px dashed #ddd;background:#fff no-repeat 20px center;color:#999;text-align:center;vertical-align:middle}.upload_drag_hover{border-color:#069;box-shadow:inset 2px 2px 4px rgba(0,0,0,.5);color:#333}.upload_preview{border-top:1px solid #d2d2d2;border-bottom:1px solid #bbb;background-color:#fff;overflow:hidden;_zoom:1;width:100%;}.upload_append_list{height:100%;margin:1em;float:left;position:relative}.upload_append_list:hover{cursor:pointer}.upload_delete{margin-left:2em}.upload_image{padding:0}.upload_submit{padding-top:1em;padding-left:1em}.upload_submit_btn{height:32px;font-size:14px;display:none}.upload_progress{padding:5px;border-radius:10px;color:#fff;background-color:rgba(0,0,0,.6);position:absolute;left:25px;top:45px}.andArea{background:url("../images/add_img.png") no-repeat scroll center 5px rgba(0,0,0,0);border:1px dashed #e0e0e0;color:#ccc;font-size:18px;padding-top:77px;position:relative;text-align:center;top:0}.filePicker{background:none repeat scroll 0 0 #00b7ee;border-radius:3px;box-shadow:0 1px 1px rgba(0,0,0,0.1);color:#fff;cursor:pointer;display:inline-block;font-size:16px;height:44px;line-height:44px;width:90%;min-width:120px;margin:0 auto 0;overflow:hidden;transition:background .2s;-moz-transition:background .2s;-webkit-transition:background .2s;-o-transition:background .2s}.filePicker:hover{background:none repeat scroll 0 0 #00a2d4}.zyupload{margin:auto}.convent_choice{float:left;height:130px;width:35%}#fileImage{display:none}.status_bar{border-top:1px solid #dadada;height:45px;line-height:45px;padding:0 10px;position:relative;vertical-align:middle;background-color:#fff}.info{float:left;color:#666;display:inline-block}.btns{position:absolute;right:16px;line-height:30px;top:6px}.webuploader_pick{-moz-user-select:none;background:none repeat scroll 0 0 #fff;border:1px solid #cfcfcf;border-radius:3px;color:#565656;cursor:pointer;display:inline-block;float:left;font-size:14px;margin-left:10px;padding:0 18px;position:relative;text-align:center;line-height:32px;transition:border .2s;-moz-transition:border .2s;-webkit-transition:border .2s;-o-transition:border .2s}.webuploader_pick:hover{border:1px solid #BBB}.upload_btn{background:none repeat scroll 0 0 #00b7ee;border-radius:3px;color:#fff;cursor:pointer;display:inline-block;float:left;font-size:14px;margin-left:10px;padding:0 18px;position:relative;text-align:center;line-height:34px;transition:background .2s;-moz-transition:background .2s;-webkit-transition:background .2s;-o-transition:background .2s}.upload_btn:hover{background:none repeat scroll 0 0 #00a2d4}.file_bar{margin:0;left:0;right:0;position:absolute;top:0;height:0;padding:0;margin:0;opacity:.8;color:#fff;background:none repeat scroll 0 0 #000;transition:all .5s;-moz-transition:all .5s;-webkit-transition:all .5s;-o-transition:all .5s;overflow:hidden}.file_bar a{color:#fff;position:absolute;right:10px} 2 | .file_hover{height:30px;cursor:pointer}.uploadImg{margin:0}.file_progress{display:none;margin:0;position:absolute;bottom:0;height:8px;left:0;right:0;background:none repeat scroll 0 0 #00b7ee;text-align:center;width:0}.file_failure{display:none;margin:0;position:absolute;bottom:0;height:24px;left:0;right:0;background:none repeat scroll 0 0 red;color:#fff;text-align:center}.file_tailor{display:none;margin:0;position:absolute;bottom:0;height:24px;left:0;right:0;background:none repeat scroll 0 0 #00b7ee;color:#fff;text-align:center}.file_success{display:none;margin:0;position:absolute;bottom:0;height:40px;left:0;right:0;background:url("../images/success.png") no-repeat scroll right bottom transparent}.file_name{margin:0;white-space:nowrap;width:66%;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis;float:left}.file_edit{background:url("../images/edit_white.png") no-repeat scroll 0 0 transparent;width:18px;height:18px;display:inline;-moz-user-select:none;position:absolute;right:22px;margin-top:4px}.file_edit:hover{background:url("../images/edit_blue.png") no-repeat scroll 0 0 transparent}.file_del{background:url("../images/delete_white.png") no-repeat scroll 0 0 transparent;width:18px;height:18px;display:inline;-moz-user-select:none;position:absolute;right:3px;margin-top:4px}.file_del:hover{background:url("../images/delete_blue.png") no-repeat scroll 0 0 transparent}.upload_append_list a{display:table-cell;text-align:center;vertical-align:middle;border:1px solid #dfdfdf;background:url("../images/bg.png") no-repeat scroll center 0 transparent}.upload_append_list img{border:solid 1px #66f;vertical-align:middle}.uploadImg{margin:auto;overflow:hidden}.uploadImg .upload_image{border:0;display:block;width:100%;height:auto!important;margin-top:0;margin-left:0;margin-right:0;margin-bottom:0;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;transform:none;image-orientation:0deg!important}.uploadImg .upload_file{border:0;max-width:100%!important;height:auto!important}.add_upload{height:100%;margin:1em;float:left;position:relative}.add_upload:hover{cursor:pointer}.add_imgBox{border:1px solid #dfdfdf;display:table-cell;text-align:center;vertical-align:middle;transition:border .2s;-moz-transition:border .2s;-webkit-transition:border .2s;-o-transition:border .2s}.add_imgBox:hover{border:1px solid #BBB}.single_main{border-top:0}.jcrop-holder{text-align:left}.jcrop-vline,.jcrop-hline{font-size:0;position:absolute;background:white url('../images/Jcrop.gif') top left repeat}.jcrop-vline{height:100%;width:1px!important}.jcrop-hline{width:100%;height:1px!important}.jcrop-handle{font-size:1px;width:7px!important;height:7px!important;border:1px #eee solid;background-color:#333;*width:9px;*height:9px}.jcrop-tracker{width:100%;height:100%}.custom .jcrop-vline,.custom .jcrop-hline{background:yellow}.custom .jcrop-handle{border-color:black;background-color:#c7bb00;-moz-border-radius:3px;-webkit-border-radius:3px}#zoom{z-index:99990;position:fixed;top:0;left:0;display:none;width:100%;height:100%;background:rgba(0,0,0,0.8);filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"}#zoom .content{z-index:99991;position:absolute;top:50%;left:50%;width:200px;height:200px;background:#fff no-repeat 50% 50%;padding:0;margin:-100px 0 0 -100px;box-shadow:-20px 20px 20px rgba(0,0,0,0.3);border-radius:4px}#zoom .content.loading{background-image:url('../images/loading.gif')}#zoom img{display:block;max-width:none;background:#ececec;box-shadow:0 1px 3px rgba(0,0,0,0.25);border-radius:4px}#zoom .close{z-index:99993;position:absolute;top:0;right:0;width:49px;height:49px;cursor:pointer;background:transparent url('../images/close.png') no-repeat 50% 50%;opacity:1;filter:alpha(opacity=100);border-radius:0 0 0 4px}#zoom .close:hover{background-color:#da4f49}#zoom .finish{z-index:99993;position:absolute;top:0;right:49px;width:49px;height:49px;cursor:pointer;background:transparent url('../images/finish.png') no-repeat 50% 50%;opacity:1;filter:alpha(opacity=100);border-radius:0 0 0 4px}#zoom .finish:hover{background-color:#da4f49} -------------------------------------------------------------------------------- /resources/download.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | DOWNLOAD 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 | 44 | 45 | 72 | 73 | 83 |
84 |
85 |
86 |
87 |

文件列表

88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | 96 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /resources/error.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | SORRY-error 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 | 36 | 61 | 62 | 63 | 64 |
65 | 66 |
67 |
68 | 69 |
70 | about image 72 |
73 | 74 |
75 |

错误!

76 | 77 |
78 | 79 |
80 |
81 |
82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /resources/files/1.txt: -------------------------------------------------------------------------------- 1 | 11111111111 2 | 11111111111 3 | 11111111111 4 | 11111111111 5 | 11111111111 6 | 11111111111 7 | 11111111111 8 | 11111111111 9 | 11111111111 10 | 11111111111 11 | 11111111111 12 | 11111111111 13 | 11111111111 14 | 11111111111 15 | 11111111111 16 | 11111111111 17 | 11111111111 18 | 11111111111 19 | -------------------------------------------------------------------------------- /resources/files/test.txt: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /resources/files/测试.txt: -------------------------------------------------------------------------------- 1 | 测试 -------------------------------------------------------------------------------- /resources/files/进来吧.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/files/进来吧.jpg -------------------------------------------------------------------------------- /resources/files/项目介绍.md: -------------------------------------------------------------------------------- 1 | #### 项目介绍 2 | 3 | ##### 为什么要做这样一个项目? 4 | 5 | ​ 6 | 7 | ``` 8 | 9 | 10 | ``` 11 | 12 | 13 | 14 | ##### 介绍下你的项目 15 | 16 | ``` 17 | 18 | 19 | ``` 20 | 21 | 22 | 23 | #### 线程池相关 24 | 25 | ##### 手写线程池: 26 | 27 | 先写一个线程同步机制封装类locker.h 28 | 29 | ```cpp 30 | #ifndef LOCKER_H 31 | #define LOCKER_H 32 | 33 | #include 34 | #include 35 | #include 36 | //互斥锁类 37 | class locker{ 38 | private: 39 | pthread_mutex_t m_mutex; 40 | public: 41 | locker(){ 42 | //创建互斥锁 43 | if(pthread_mutex_init(&m_mutex,NULL)){ 44 | throw std::exception(); 45 | } 46 | } 47 | ~locker(){ 48 | //销毁互斥锁 49 | (pthread_mutex_destroy(&m_mutex); 50 | } 51 | bool lock(){ 52 | return pthread_mutex_lock(&m_mutex)==0; 53 | } 54 | bool unlock(){ 55 | return pthread_mutex_unlock(&m_mutex)==0; 56 | } 57 | pthread_mutex_t *get(){ 58 | return &m_mutex; 59 | } 60 | }; 61 | 62 | //条件变量类 63 | class cond{ 64 | private: 65 | pthread_cond_t m_cond; 66 | public: 67 | cond(){ 68 | if(pthread_cond_init(&m_cond,NULL)) 69 | throw std::exception(); 70 | } 71 | ~cond{ 72 | pthread_cond_destroy(&m_cond); 73 | } 74 | bool wait(pthread_mutex_t *mutex){ 75 | return pthread_cond_wait(&m_cond,mutex)==0; 76 | } 77 | bool timedwait(pthread_mutex_t *mutex,struct timespec t){ 78 | return pthread_cond_timedwait(&m_cond,mutex,&t)==0; 79 | } 80 | bool signal(){ 81 | return pthread_cond_signal(&m_cond)==0; 82 | } 83 | bool broadcast(){ 84 | return pthread_cond_broadcast(&m_cond)==0; 85 | } 86 | }; 87 | 88 | //信号量类 89 | class sem{ 90 | private: 91 | sem_t m_sem; 92 | public: 93 | sem(){ 94 | if(sem_init(&m_sem,0,0)){ 95 | throw std::exception(); 96 | } 97 | } 98 | sem(int num){ 99 | if(sem_init(&m_sem,0,num)){ 100 | throw std::exception(); 101 | } 102 | } 103 | ~sem(){ 104 | sem_destroy(&m_sem); 105 | } 106 | //等待信号量 107 | bool wait(){ 108 | return sem_wait(&m_sem)==0; 109 | } 110 | //增加信号量 111 | bool post(){ 112 | return sem_post(); 113 | } 114 | }; 115 | #endif 116 | ``` 117 | 118 | 然后写线程池类 119 | 120 | ```cpp 121 | #ifndef THREADPOOL_H 122 | #define THREADPOOL_H 123 | 124 | #include 125 | #include"locker.h" 126 | #include 127 | //定义模板类为了代码复用 128 | template 129 | class threadpool{ 130 | private: 131 | //线程数量 132 | int m_thread_number; 133 | //线程池数组,大小m_thread_number 134 | pthread_t *m_threads; 135 | //请求队列中最多等待数量 136 | int m_max_requests; 137 | //请求队列 138 | std::list workqueue; 139 | //互斥锁 140 | locker m_queuelocker; 141 | //信号量判断是否有任务需要处理 142 | sem m_queuestat; 143 | //是否结束线程 144 | bool m_stop; 145 | private: 146 | static void * worker(void *arg); 147 | void run(); 148 | public: 149 | threadpool(int thread_number=8,int max_requests=10000); 150 | ~threadpool(); 151 | bool append(T*request); 152 | }; 153 | 154 | template 155 | threadpool::threadpool(int thread_number=8,int max_request=10000):m_thread_number(thread_number),m_max_requests(max_requests) 156 | ,m_stop(flase),m_threads(NULL){ 157 | if(thread_number<=0||max_request<=0){ 158 | throw std::exception(); 159 | } 160 | mthreads = new pthread_t[m_thread_number]; 161 | if(!mthread){ 162 | throw std::exception(); 163 | } 164 | 165 | for(int i=0;i 179 | ~threadpool::threadpool(){ 180 | delete []m_threads; 181 | m_stop=true; 182 | } 183 | 184 | template 185 | bool threadpool::append(T *request){ 186 | m_queuelocker.lock(); 187 | if(m_workqueue.size()>m_max_request){ 188 | m_queuelocker.unlock(); 189 | return false; 190 | } 191 | m_workqueue.push_back(request); 192 | m_queuelock.unlock(); 193 | m_queuestat.post(); 194 | return true; 195 | } 196 | template 197 | void *threadpool::worker(void *arg){ 198 | threadpool *pool = (threadpool *)arg; 199 | pool->run(); 200 | return pool; 201 | } 202 | template 203 | void threadpool::run(){ 204 | while(!m_stop){ 205 | m_queuestat.wait();//没有值就阻塞,有值就不阻塞,判断是否有任务需要处理 206 | m_queuelocker.lock(); 207 | if(m_workqueue.empty()){ 208 | m_queuelocker.unlock(); 209 | continue; 210 | } 211 | T *request=m_workqueue.front(); 212 | m_workqueue.pop_front(); 213 | m_queuelocker.unlock(); 214 | if(!request){ 215 | continue; 216 | } 217 | request->process();//这是你自定义的请求中的任务 218 | 219 | } 220 | } 221 | #endif 222 | ``` 223 | 224 | - 线程的同步机制有哪些? 225 | 226 | ``` 227 | 最流行的四种 228 | 1.互斥量 即mutex,为协调对一个对一个共享资源的单独访问而设计。只有得到互斥锁的线程才能去访问对应资源,因为互斥对象只有一个,所以能保证资源不会同时被多个线程访问 229 | 230 | 2.信号量:为控制一个具有有限数量的用户资源而设计。他允许多个线程在同一时刻去访问同一个资源,但一般需要限制同时访问该资源的最大线程数 231 | 232 | ``` 233 | 234 | 3.临界区:通过多线程的互串行访问公共资源或一段代码,速度快,适合控制数据访问 235 | 236 | 4:事件:用来通知线程有一些事件已发生,从而启动后继任务的开始 237 | 238 | 条件变量 239 | ``` 240 | 241 | 242 | 243 | ``` 244 | 互斥量,条件变量,信号量, 245 | pthread_mutex_t m_mutex; 246 | pthread_cond_t m_cond; 247 | sem_t m_sem; 248 | 249 | 250 | 251 | 252 | - 线程池中的工作线程是一直等待吗? 253 | 254 | ``` 255 | 线程池中的工作线程是一直处于等待且阻塞的状态下的。因为在创建线程池是,通过循环调用pthread_create往线程池中创建了8个工作线程,工作线程处理函数接口为pthread_create的第三个函数worker函数(自定义的),然后调用线程池类成员函数run(自定义)...至于为什么不直接就将第三个参数直接指向run函数,是因为,C++中规定第三个函数参数worker时静态成员函数,静态成员函数只能访问静态成员变量,为了能访问非静态成员变量,我们可以通过在worker中调用run这个非静态成员变量来达到这一要求 256 | ``` 257 | 258 | ``` 259 | C++类中的成员函数其实默认在参数中包含有一个this指针,这样成员函数才知道应该对哪个实例作用。而线程函数必须接受一个void指针作为参数,所以导致了矛盾。为了解决矛盾,我们可以使用static函数,它独立于实例,参数中将不会有this指针,所以可以用于打开线程 260 | ``` 261 | 262 | 263 | 264 | 265 | - 你的线程池工作线程处理完一个任务后的状态是什么? 266 | 267 | ``` 268 | 这里分两种情况考虑 269 | 1.当处理完任务请求队列为空,则这个线程重新回到阻塞等待的状态 270 | 2.当处理完任务请求队列不为空,那么这个线程将处于与其他线程竞争资源的状态,谁获得了锁就获得了处理事件得资格 271 | ``` 272 | 273 | 274 | 275 | - 如果同时1000个客户端进行访问请求,线程数不多,怎么能及时响应处理每一个呢? 276 | 277 | ``` 278 | 即等同与服务器如何解决高并发的问题 279 | 答:本项目是通过子线程循环调用来解决高并发问题的 280 | 具体实现过程: 281 | 我们在创建线程之后就用pthread_datach进行线程分离,这样就不用单独对工作线程进行回收,但是一般情况下只要我们设置了分离属性,那么这个线程在处理完任务后,也就是子线程结束后,资源会被自动回收。那这种情况下基本就只能处理8个请求事件了(线程池里只有8个线程),那么怎么实现高并发的请求呢?一般想的自然是增加线程池中的线程数量,但是这样线程数量过大会导致更多的线程切换,占用过多内存,这显然不合理 282 | 为此,我们使用另一种方法,我们知道利用pthread_datach进行线程分离得线程只有在结束时系统才会回收他的资源,因此利用这个特性,我们通过子线程的run函数进行while循环,让每一个线程池中的线程都不会终止,即让他处理完当前任务就去处理下一个,没有任务就一直阻塞在那里等待,这样就能达到服务器高并发的要求,同一时刻8个线程都在处理请求任务,处理完之后接着处理,直到请求队列为空表示任务全部处理完成 283 | ``` 284 | 285 | 286 | 287 | - 如果一个客户请求需要占用线程很久的时间,会不会影响接下来的客户请求呢,有什么好的策略呢? 288 | 289 | ``` 290 | 会影响接下来的客户请求,因为线程池内线程的数量是有限的,如果客户占用线程时间太久会影响处理请求的效率,当请求处理过慢时会造成后续接受的请求只能在请求队列中等待被处理,从而影响接下来的客户请求 291 | 解决办法: 292 | 我们可以为线程处理对象设置处理超时时间,超过时间发送信号告知线程处理超时,然后设定一个时间间隔在次检测 ,若此时这个请求还占用线程则直接将其断开连接 293 | ``` 294 | 295 | 296 | 297 | #### **并发模型相关** 298 | 299 | - 简单说一下服务器使用的并发模型? 300 | - reactor、proactor、主从reactor模型的区别? 301 | - 你用了epoll,说一下为什么用epoll,还有其他复用方式吗?区别是什么? 302 | 303 | #### **HTTP报文解析相关** 304 | 305 | - 用了状态机啊,为什么要用状态机? 306 | 307 | ``` 308 | 在解析HTTP请求时能根据不同的状态进行不同的操作 309 | 因为传统应用程序的控制流程基本是按顺序执行的:遵循事先设定的逻辑,从头到尾的执行。简单来说如果想在不同状态下实现代码跳转时,就需要破坏一些代码或者编写重复性很大的代码,这样会很复杂 310 | 有限状态机则能有效解决这个问题。每个状态都有一系列的转移,每个转移和输入与另一状态相关。当输入进来,如果他与当前状态的某个转移想匹配,机器转换为所指的状态,然后执行相应的代码 311 | ``` 312 | 313 | 314 | 315 | - 状态机的转移图画一下 316 | ![img](file:///C:/Users/19252/Desktop/web%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%A1%B9%E7%9B%AE%E9%83%A8%E5%88%86%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB%20-%20%E7%9F%A5%E4%B9%8E_files/v2-f68bbb38668193d52866c590fd82ad6e_720w.jpg) 317 | 318 | - https协议为什么安全? 319 | 320 | ``` 321 | https=http + TLS/SSL 322 | TSL/SSL协议位于应用层协议和tcp之间,由TCP协议保证数据传输可靠性,任何数据到达TCP之前,都经过TLS/SSL处理。 323 | https是加密传输协议,可以保证客户端到服务器端的传输数据安全,用户通过http协议访问网站时,浏览器和服务器之间是明文传输,这就意味着用户填写的密码,账号等机密信息都是明文,随时可能被泄露、窃取、篡改。安装SSL证书后,使用https加密协议访问网站,可激活客户端浏览器到服务器之间的"SSL加密通道“,实现高强度双向加密传输,防止传输数据被泄露、篡改 324 | ``` 325 | 326 | 327 | 328 | - https的ssl连接过程 329 | 330 | ``` 331 | 1.客户端提交HTTPS请求 332 | 2.服务端响应客户并把证书公钥发给客户端 333 | 3.客户端验证证书公钥的有效性 334 | 4.有效后,会生成一个会话密钥 335 | 5.用证书公钥加密这个会话密钥后,发送给服务器 336 | 6. 服务器收到公钥加密的会话密钥后,用私钥解密,回去会话密钥 337 | 7. 客户端与服务器双方利用这个会话密钥加密要传输的数据进行通信 338 | ``` 339 | 340 | 341 | 342 | - GET和POST的区别 343 | ![img](file:///C:/Users/19252/Desktop/web%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%A1%B9%E7%9B%AE%E9%83%A8%E5%88%86%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB%20-%20%E7%9F%A5%E4%B9%8E_files/v2-1b4c918e253c1a6617eca63aafe19075_720w.jpg) 344 | 345 | #### **数据库登录注册相关** 346 | 347 | - 登录说一下? 348 | - 你这个保存状态了吗?如果要保存,你会怎么做?(cookie和session) 349 | - 登录中的用户名和密码你是load到本地,然后使用map匹配的,如果有10亿数据,即使load到本地后hash,也是很耗时的,你要怎么优化? 350 | - 用的mysql啊,redis了解吗?用过吗? 351 | 352 | #### **定时器相关** 353 | 354 | - 为什么要用定时器? 355 | - 说一下定时器的工作原理 356 | - 双向链表啊,删除和添加的时间复杂度说一下?还可以优化吗? 357 | - 最小堆优化?说一下时间复杂度和工作原理 358 | 359 | #### **日志相关** 360 | 361 | - 说下你的日志系统的运行机制? 362 | - 为什么要异步?和同步的区别是什么? 363 | - 现在你要监控一台服务器的状态,输出监控日志,请问如何将该日志分发到不同的机器上?(消息队列) 364 | 365 | #### **压测相关** 366 | 367 | - 服务器并发量测试过吗?怎么测试的? 368 | - webbench是什么?介绍一下原理 369 | - 测试的时候有没有遇到问题? 370 | 371 | #### **综合能力** 372 | 373 | - 你的项目解决了哪些其他同类项目没有解决的问题? 374 | - 说一下前端发送请求后,服务器处理的过程,中间涉及哪些协议? -------------------------------------------------------------------------------- /resources/fonts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/fonts/.DS_Store -------------------------------------------------------------------------------- /resources/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /resources/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /resources/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /resources/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /resources/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /resources/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/.DS_Store -------------------------------------------------------------------------------- /resources/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/1.jpg -------------------------------------------------------------------------------- /resources/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/2.jpg -------------------------------------------------------------------------------- /resources/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/3.jpg -------------------------------------------------------------------------------- /resources/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/4.jpg -------------------------------------------------------------------------------- /resources/images/add_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/add_img.png -------------------------------------------------------------------------------- /resources/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/bg.png -------------------------------------------------------------------------------- /resources/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/close.png -------------------------------------------------------------------------------- /resources/images/del.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/del.png -------------------------------------------------------------------------------- /resources/images/delete_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/delete_blue.png -------------------------------------------------------------------------------- /resources/images/delete_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/delete_white.png -------------------------------------------------------------------------------- /resources/images/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/edit.png -------------------------------------------------------------------------------- /resources/images/edit_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/edit_blue.png -------------------------------------------------------------------------------- /resources/images/edit_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/edit_white.png -------------------------------------------------------------------------------- /resources/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/favicon.ico -------------------------------------------------------------------------------- /resources/images/fileType/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/file.png -------------------------------------------------------------------------------- /resources/images/fileType/pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/pdf.png -------------------------------------------------------------------------------- /resources/images/fileType/ppt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/ppt.png -------------------------------------------------------------------------------- /resources/images/fileType/psd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/psd.png -------------------------------------------------------------------------------- /resources/images/fileType/rar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/rar.png -------------------------------------------------------------------------------- /resources/images/fileType/swf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/swf.png -------------------------------------------------------------------------------- /resources/images/fileType/ttf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/ttf.png -------------------------------------------------------------------------------- /resources/images/fileType/txt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/txt.png -------------------------------------------------------------------------------- /resources/images/fileType/xls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/xls.png -------------------------------------------------------------------------------- /resources/images/fileType/zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/fileType/zip.png -------------------------------------------------------------------------------- /resources/images/finish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/finish.png -------------------------------------------------------------------------------- /resources/images/instagram-image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/instagram-image1.jpg -------------------------------------------------------------------------------- /resources/images/instagram-image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/instagram-image2.jpg -------------------------------------------------------------------------------- /resources/images/instagram-image3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/instagram-image3.jpg -------------------------------------------------------------------------------- /resources/images/instagram-image4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/instagram-image4.jpg -------------------------------------------------------------------------------- /resources/images/instagram-image5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/instagram-image5.jpg -------------------------------------------------------------------------------- /resources/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/loading.gif -------------------------------------------------------------------------------- /resources/images/profile-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/profile-image.jpg -------------------------------------------------------------------------------- /resources/images/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/images/success.png -------------------------------------------------------------------------------- /resources/img/waoku.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/img/waoku.jpg -------------------------------------------------------------------------------- /resources/img/wuwu.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/img/wuwu.jpeg -------------------------------------------------------------------------------- /resources/img/yy.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | 12 |

13 | 14 | 15 |

16 |

17 | 18 |
-------------------------------------------------------------------------------- /resources/index.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | WELCOME 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 | 36 | 62 | 63 |
64 |
65 |
66 | 67 |
68 | about image 70 |
71 |
72 |

你好,这是首页。

73 |
74 |
75 |
76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /resources/js/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wustghj/SimpleServer/dc4c7e672f0a3c64dda010a7a6be80b879828aad/resources/js/.DS_Store -------------------------------------------------------------------------------- /resources/js/custom.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | "use strict"; 4 | 5 | // PRE loader 6 | $(window).load(function(){ 7 | $('.preloader').fadeOut(1000); // set duration in brackets 8 | }); 9 | 10 | 11 | //Navigation Section 12 | $('.navbar-collapse a').on('click',function(){ 13 | $(".navbar-collapse").collapse('hide'); 14 | }); 15 | 16 | $(window).scroll(function() { 17 | if ($(".navbar").offset().top > 50) { 18 | $(".navbar-fixed-top").addClass("top-nav-collapse"); 19 | } else { 20 | $(".navbar-fixed-top").removeClass("top-nav-collapse"); 21 | } 22 | }); 23 | 24 | 25 | // Smoothscroll js 26 | $(function() { 27 | $('.custom-navbar a, #home a').bind('click', function(event) { 28 | var $anchor = $(this); 29 | $('html, body').stop().animate({ 30 | scrollTop: $($anchor.attr('href')).offset().top - 49 31 | }, 1000); 32 | event.preventDefault(); 33 | }); 34 | }); 35 | 36 | 37 | // WOW Animation js 38 | new WOW({ mobile: false }).init(); 39 | 40 | })(jQuery); 41 | -------------------------------------------------------------------------------- /resources/js/magnific-popup-options.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | // MagnificPopup 3 | var magnifPopup = function() { 4 | $('.image-popup').magnificPopup({ 5 | type: 'image', 6 | removalDelay: 300, 7 | mainClass: 'mfp-with-zoom', 8 | gallery:{ 9 | enabled:true 10 | }, 11 | zoom: { 12 | enabled: true, // By default it's false, so don't forget to enable it 13 | 14 | duration: 300, // duration of the effect, in milliseconds 15 | easing: 'ease-in-out', // CSS transition easing function 16 | 17 | // The "opener" function should return the element from which popup will be zoomed in 18 | // and to which popup will be scaled down 19 | // By defailt it looks for an image tag: 20 | opener: function(openerElement) { 21 | // openerElement is the element on which popup was initialized, in this case its tag 22 | // you don't need to add "opener" option if this code matches your needs, it's defailt one. 23 | return openerElement.is('img') ? openerElement : openerElement.find('img'); 24 | } 25 | } 26 | }); 27 | }; 28 | 29 | 30 | // Call the functions 31 | magnifPopup(); 32 | 33 | }); -------------------------------------------------------------------------------- /resources/js/smoothscroll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SmoothScroll 3 | * This helper script created by DWUser.com. Copyright 2013 DWUser.com. 4 | * Dual-licensed under the GPL and MIT licenses. 5 | * All individual scripts remain property of their copyrighters. 6 | * Date: 10-Sep-2013 7 | * Version: 1.0.1 8 | */ 9 | if (!window['jQuery']) alert('The jQuery library must be included before the smoothscroll.js file. The plugin will not work propery.'); 10 | 11 | /** 12 | * jQuery.ScrollTo - Easy element scrolling using jQuery. 13 | * Copyright (c) 2007-2013 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com 14 | * Dual licensed under MIT and GPL. 15 | * @author Ariel Flesler 16 | * @version 1.4.3.1 17 | */ 18 | ;(function($){var h=$.scrollTo=function(a,b,c){$(window).scrollTo(a,b,c)};h.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1,limit:true};h.window=function(a){return $(window)._scrollable()};$.fn._scrollable=function(){return this.map(function(){var a=this,isWin=!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!isWin)return a;var b=(a.contentWindow||a).document||a.ownerDocument||a;return/webkit/i.test(navigator.userAgent)||b.compatMode=='BackCompat'?b.body:b.documentElement})};$.fn.scrollTo=function(e,f,g){if(typeof f=='object'){g=f;f=0}if(typeof g=='function')g={onAfter:g};if(e=='max')e=9e9;g=$.extend({},h.defaults,g);f=f||g.duration;g.queue=g.queue&&g.axis.length>1;if(g.queue)f/=2;g.offset=both(g.offset);g.over=both(g.over);return this._scrollable().each(function(){if(e==null)return;var d=this,$elem=$(d),targ=e,toff,attr={},win=$elem.is('html,body');switch(typeof targ){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=$(targ,this);if(!targ.length)return;case'object':if(targ.is||targ.style)toff=(targ=$(targ)).offset()}$.each(g.axis.split(''),function(i,a){var b=a=='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,old=d[key],max=h.max(d,a);if(toff){attr[key]=toff[pos]+(win?0:old-$elem.offset()[pos]);if(g.margin){attr[key]-=parseInt(targ.css('margin'+b))||0;attr[key]-=parseInt(targ.css('border'+b+'Width'))||0}attr[key]+=g.offset[pos]||0;if(g.over[pos])attr[key]+=targ[a=='x'?'width':'height']()*g.over[pos]}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)=='%'?parseFloat(c)/100*max:c}if(g.limit&&/^\d+$/.test(attr[key]))attr[key]=attr[key]<=0?0:Math.min(attr[key],max);if(!i&&g.queue){if(old!=attr[key])animate(g.onAfterFirst);delete attr[key]}});animate(g.onAfter);function animate(a){$elem.animate(attr,f,g.easing,a&&function(){a.call(this,e,g)})}}).end()};h.max=function(a,b){var c=b=='x'?'Width':'Height',scroll='scroll'+c;if(!$(a).is('html,body'))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,html=a.ownerDocument.documentElement,body=a.ownerDocument.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery); 19 | 20 | /** 21 | * jQuery.LocalScroll 22 | * Copyright (c) 2007-2010 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com 23 | * Dual licensed under MIT and GPL. 24 | * Date: 05/31/2010 25 | * @author Ariel Flesler 26 | * @version 1.2.8b 27 | **/ 28 | ;(function(b){function g(a,e,d){var h=e.hash.slice(1),f=document.getElementById(h)||document.getElementsByName(h)[0];if(f){a&&a.preventDefault();var c=b(d.target);if(!(d.lock&&c.is(":animated")||d.onBefore&&!1===d.onBefore(a,f,c))){d.stop&&c._scrollable().stop(!0);if(d.hash){var a=f.id==h?"id":"name",g=b(" ").attr(a,h).css({position:"absolute",top:b(window).scrollTop(),left:b(window).scrollLeft()});f[a]="";b("body").prepend(g);location=e.hash;g.remove();f[a]=h}c.scrollTo(f,d).trigger("notify.serialScroll", 29 | [f])}}}var i=location.href.replace(/#.*/,""),c=b.localScroll=function(a){b("body").localScroll(a)};c.defaults={duration:1E3,axis:"y",event:"click",stop:!0,target:window,reset:!0};c.hash=function(a){if(location.hash){a=b.extend({},c.defaults,a);a.hash=!1;if(a.reset){var e=a.duration;delete a.duration;b(a.target).scrollTo(0,a);a.duration=e}g(0,location,a)}};b.fn.localScroll=function(a){function e(){return!!this.href&&!!this.hash&&this.href.replace(this.hash,"")==i&&(!a.filter||b(this).is(a.filter))} 30 | a=b.extend({},c.defaults,a);return a.lazy?this.bind(a.event,function(d){var c=b([d.target,d.target.parentNode]).filter(e)[0];c&&g(d,c,a)}):this.find("a,area").filter(e).bind(a.event,function(b){g(b,this,a)}).end().end()}})(jQuery); 31 | 32 | // Initialize all .smoothScroll links 33 | jQuery(function($){ $.localScroll({filter:'.smoothScroll'}); }); 34 | -------------------------------------------------------------------------------- /resources/js/wow.min.js: -------------------------------------------------------------------------------- 1 | /*! WOW - v1.0.2 - 2014-10-28 2 | * Copyright (c) 2014 Matthieu Aussaguel; Licensed MIT */(function(){var a,b,c,d,e,f=function(a,b){return function(){return a.apply(b,arguments)}},g=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};b=function(){function a(){}return a.prototype.extend=function(a,b){var c,d;for(c in b)d=b[c],null==a[c]&&(a[c]=d);return a},a.prototype.isMobile=function(a){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(a)},a.prototype.addEvent=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):null!=a.attachEvent?a.attachEvent("on"+b,c):a[b]=c},a.prototype.removeEvent=function(a,b,c){return null!=a.removeEventListener?a.removeEventListener(b,c,!1):null!=a.detachEvent?a.detachEvent("on"+b,c):delete a[b]},a.prototype.innerHeight=function(){return"innerHeight"in window?window.innerHeight:document.documentElement.clientHeight},a}(),c=this.WeakMap||this.MozWeakMap||(c=function(){function a(){this.keys=[],this.values=[]}return a.prototype.get=function(a){var b,c,d,e,f;for(f=this.keys,b=d=0,e=f.length;e>d;b=++d)if(c=f[b],c===a)return this.values[b]},a.prototype.set=function(a,b){var c,d,e,f,g;for(g=this.keys,c=e=0,f=g.length;f>e;c=++e)if(d=g[c],d===a)return void(this.values[c]=b);return this.keys.push(a),this.values.push(b)},a}()),a=this.MutationObserver||this.WebkitMutationObserver||this.MozMutationObserver||(a=function(){function a(){"undefined"!=typeof console&&null!==console&&console.warn("MutationObserver is not supported by your browser."),"undefined"!=typeof console&&null!==console&&console.warn("WOW.js cannot detect dom mutations, please call .sync() after loading new content.")}return a.notSupported=!0,a.prototype.observe=function(){},a}()),d=this.getComputedStyle||function(a){return this.getPropertyValue=function(b){var c;return"float"===b&&(b="styleFloat"),e.test(b)&&b.replace(e,function(a,b){return b.toUpperCase()}),(null!=(c=a.currentStyle)?c[b]:void 0)||null},this},e=/(\-([a-z]){1})/g,this.WOW=function(){function e(a){null==a&&(a={}),this.scrollCallback=f(this.scrollCallback,this),this.scrollHandler=f(this.scrollHandler,this),this.start=f(this.start,this),this.scrolled=!0,this.config=this.util().extend(a,this.defaults),this.animationNameCache=new c}return e.prototype.defaults={boxClass:"wow",animateClass:"animated",offset:0,mobile:!0,live:!0},e.prototype.init=function(){var a;return this.element=window.document.documentElement,"interactive"===(a=document.readyState)||"complete"===a?this.start():this.util().addEvent(document,"DOMContentLoaded",this.start),this.finished=[]},e.prototype.start=function(){var b,c,d,e;if(this.stopped=!1,this.boxes=function(){var a,c,d,e;for(d=this.element.querySelectorAll("."+this.config.boxClass),e=[],a=0,c=d.length;c>a;a++)b=d[a],e.push(b);return e}.call(this),this.all=function(){var a,c,d,e;for(d=this.boxes,e=[],a=0,c=d.length;c>a;a++)b=d[a],e.push(b);return e}.call(this),this.boxes.length)if(this.disabled())this.resetStyle();else for(e=this.boxes,c=0,d=e.length;d>c;c++)b=e[c],this.applyStyle(b,!0);return this.disabled()||(this.util().addEvent(window,"scroll",this.scrollHandler),this.util().addEvent(window,"resize",this.scrollHandler),this.interval=setInterval(this.scrollCallback,50)),this.config.live?new a(function(a){return function(b){var c,d,e,f,g;for(g=[],e=0,f=b.length;f>e;e++)d=b[e],g.push(function(){var a,b,e,f;for(e=d.addedNodes||[],f=[],a=0,b=e.length;b>a;a++)c=e[a],f.push(this.doSync(c));return f}.call(a));return g}}(this)).observe(document.body,{childList:!0,subtree:!0}):void 0},e.prototype.stop=function(){return this.stopped=!0,this.util().removeEvent(window,"scroll",this.scrollHandler),this.util().removeEvent(window,"resize",this.scrollHandler),null!=this.interval?clearInterval(this.interval):void 0},e.prototype.sync=function(){return a.notSupported?this.doSync(this.element):void 0},e.prototype.doSync=function(a){var b,c,d,e,f;if(null==a&&(a=this.element),1===a.nodeType){for(a=a.parentNode||a,e=a.querySelectorAll("."+this.config.boxClass),f=[],c=0,d=e.length;d>c;c++)b=e[c],g.call(this.all,b)<0?(this.boxes.push(b),this.all.push(b),this.stopped||this.disabled()?this.resetStyle():this.applyStyle(b,!0),f.push(this.scrolled=!0)):f.push(void 0);return f}},e.prototype.show=function(a){return this.applyStyle(a),a.className=""+a.className+" "+this.config.animateClass},e.prototype.applyStyle=function(a,b){var c,d,e;return d=a.getAttribute("data-wow-duration"),c=a.getAttribute("data-wow-delay"),e=a.getAttribute("data-wow-iteration"),this.animate(function(f){return function(){return f.customStyle(a,b,d,c,e)}}(this))},e.prototype.animate=function(){return"requestAnimationFrame"in window?function(a){return window.requestAnimationFrame(a)}:function(a){return a()}}(),e.prototype.resetStyle=function(){var a,b,c,d,e;for(d=this.boxes,e=[],b=0,c=d.length;c>b;b++)a=d[b],e.push(a.style.visibility="visible");return e},e.prototype.customStyle=function(a,b,c,d,e){return b&&this.cacheAnimationName(a),a.style.visibility=b?"hidden":"visible",c&&this.vendorSet(a.style,{animationDuration:c}),d&&this.vendorSet(a.style,{animationDelay:d}),e&&this.vendorSet(a.style,{animationIterationCount:e}),this.vendorSet(a.style,{animationName:b?"none":this.cachedAnimationName(a)}),a},e.prototype.vendors=["moz","webkit"],e.prototype.vendorSet=function(a,b){var c,d,e,f;f=[];for(c in b)d=b[c],a[""+c]=d,f.push(function(){var b,f,g,h;for(g=this.vendors,h=[],b=0,f=g.length;f>b;b++)e=g[b],h.push(a[""+e+c.charAt(0).toUpperCase()+c.substr(1)]=d);return h}.call(this));return f},e.prototype.vendorCSS=function(a,b){var c,e,f,g,h,i;for(e=d(a),c=e.getPropertyCSSValue(b),i=this.vendors,g=0,h=i.length;h>g;g++)f=i[g],c=c||e.getPropertyCSSValue("-"+f+"-"+b);return c},e.prototype.animationName=function(a){var b;try{b=this.vendorCSS(a,"animation-name").cssText}catch(c){b=d(a).getPropertyValue("animation-name")}return"none"===b?"":b},e.prototype.cacheAnimationName=function(a){return this.animationNameCache.set(a,this.animationName(a))},e.prototype.cachedAnimationName=function(a){return this.animationNameCache.get(a)},e.prototype.scrollHandler=function(){return this.scrolled=!0},e.prototype.scrollCallback=function(){var a;return!this.scrolled||(this.scrolled=!1,this.boxes=function(){var b,c,d,e;for(d=this.boxes,e=[],b=0,c=d.length;c>b;b++)a=d[b],a&&(this.isVisible(a)?this.show(a):e.push(a));return e}.call(this),this.boxes.length||this.config.live)?void 0:this.stop()},e.prototype.offsetTop=function(a){for(var b;void 0===a.offsetTop;)a=a.parentNode;for(b=a.offsetTop;a=a.offsetParent;)b+=a.offsetTop;return b},e.prototype.isVisible=function(a){var b,c,d,e,f;return c=a.getAttribute("data-wow-offset")||this.config.offset,f=window.pageYOffset,e=f+Math.min(this.element.clientHeight,this.util().innerHeight())-c,d=this.offsetTop(a),b=d+a.clientHeight,e>=d&&b>=f},e.prototype.util=function(){return null!=this._util?this._util:this._util=new b},e.prototype.disabled=function(){return!this.config.mobile&&this.util().isMobile(navigator.userAgent)},e}()}).call(this); -------------------------------------------------------------------------------- /resources/list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "filename" : "项目介绍.md" 4 | }, 5 | { 6 | "filename" : "测试.txt" 7 | }, 8 | { 9 | "filename" : "1.txt" 10 | }, 11 | { 12 | "filename" : "进来吧.jpg" 13 | }, 14 | { 15 | "filename" : "test.txt" 16 | }, 17 | { 18 | "filename" : "周杰伦 - 青花瓷.mp3" 19 | }, 20 | { 21 | "filename" : "hanser-Cyberangel.mp3" 22 | }, 23 | { 24 | "filename" : "郭顶 - 水星记.mp3" 25 | } 26 | ] -------------------------------------------------------------------------------- /resources/login.css: -------------------------------------------------------------------------------- 1 | * { 2 | /* 去除浏览器默认内外边距 */ 3 | margin: 0; 4 | padding: 0; 5 | box-sizing: border-box; 6 | } 7 | 8 | /* 去除input的轮廓 */ 9 | input { 10 | outline: none; 11 | } 12 | 13 | html, 14 | body { 15 | height: 100%; 16 | } 17 | 18 | body { 19 | /* 溢出隐藏 */ 20 | overflow-x: hidden; 21 | display: flex; 22 | /* 渐变方向从左到右 */ 23 | background: linear-gradient(to right, rgb(247, 209, 215), rgb(191, 227, 241)); 24 | } 25 | 26 | span { 27 | position: absolute; 28 | z-index: 0; 29 | bottom: 0; 30 | border-radius: 50%; 31 | /* 径向渐变 */ 32 | background: radial-gradient(circle at 72% 28%, #fff 3px, #ff7edf 8%, #5b5b5b, #aad7f9 100%); 33 | /* 泡泡内阴影 */ 34 | box-shadow: inset 0 0 6px #fff, 35 | inset 3px 0 6px #eaf5fc, 36 | inset 2px -2px 10px #efcde6, 37 | inset 0 0 60px #f9f6de, 38 | 0 0 20px #fff; 39 | /* 动画 */ 40 | animation: myMove 4s linear infinite; 41 | } 42 | 43 | 44 | @keyframes myMove { 45 | 0% { 46 | transform: translateY(0%); 47 | opacity: 1; 48 | } 49 | 50 | 50% { 51 | transform: translate(10%, -1000%); 52 | } 53 | 54 | 75% { 55 | transform: translate(-20%, -1200%); 56 | } 57 | 58 | 99% { 59 | opacity: .9; 60 | } 61 | 62 | 100% { 63 | transform: translateY(-1800%) scale(1.5); 64 | opacity: 0; 65 | } 66 | } 67 | 68 | /* 最外层的大盒子 */ 69 | .box { 70 | width: 1050px; 71 | height: 600px; 72 | display: flex; 73 | /* 相对定位 */ 74 | position: relative; 75 | z-index: 2; 76 | margin: auto; 77 | /* 设置圆角 */ 78 | border-radius: 8px; 79 | /* 设置边框 */ 80 | border: 1px solid rgba(255, 255, 255, .6); 81 | /* 设置盒子阴影 */ 82 | box-shadow: 2px 1px 19px rgba(0, 0, 0, .1); 83 | } 84 | 85 | /* 滑动的盒子 */ 86 | .pre-box { 87 | /* 宽度为大盒子的一半 */ 88 | width: calc(1050px / 2); 89 | height: 100%; 90 | /* 绝对定位 */ 91 | position: absolute; 92 | /* 距离大盒子左侧为0 */ 93 | left: 0; 94 | /* 距离大盒子顶部为0 */ 95 | top: 0; 96 | z-index: 99; 97 | border-radius: 4px; 98 | background-color: #edd4dc; 99 | box-shadow: 2px 1px 19px rgba(0, 0, 0, .1); 100 | /* 动画过渡,先加速再减速 */ 101 | transition: 0.5s ease-in-out; 102 | } 103 | 104 | /* 滑动盒子的标题 */ 105 | .pre-box h1 { 106 | margin-top: 150px; 107 | text-align: center; 108 | /* 文字间距 */ 109 | letter-spacing: 5px; 110 | color: white; 111 | /* 禁止选中 */ 112 | user-select: none; 113 | /* 文字阴影 */ 114 | text-shadow: 4px 4px 3px rgba(0, 0, 0, .1); 115 | } 116 | 117 | /* 滑动盒子的文字 */ 118 | .pre-box p { 119 | height: 30px; 120 | line-height: 30px; 121 | text-align: center; 122 | margin: 20px 0; 123 | /* 禁止选中 */ 124 | user-select: none; 125 | font-weight: bold; 126 | color: white; 127 | text-shadow: 4px 4px 3px rgba(0, 0, 0, .1); 128 | } 129 | 130 | /* 图片盒子 */ 131 | .img-box { 132 | width: 200px; 133 | height: 200px; 134 | margin: 20px auto; 135 | /* 设置为圆形 */ 136 | border-radius: 50%; 137 | /* 设置用户禁止选中 */ 138 | user-select: none; 139 | overflow: hidden; 140 | box-shadow: 4px 4px 3px rgba(0, 0, 0, .1); 141 | } 142 | 143 | /* 图片 */ 144 | .img-box img { 145 | width: 100%; 146 | transition: 0.5s; 147 | } 148 | 149 | /* 登录和注册盒子 */ 150 | .login-form, 151 | .register-form { 152 | flex: 1; 153 | height: 100%; 154 | } 155 | 156 | /* 标题盒子 */ 157 | .title-box { 158 | height: 300px; 159 | line-height: 500px; 160 | 161 | } 162 | 163 | /* 标题 */ 164 | .title-box h1 { 165 | text-align: center; 166 | color: white; 167 | /* 禁止选中 */ 168 | user-select: none; 169 | letter-spacing: 5px; 170 | text-shadow: 4px 4px 3px rgba(0, 0, 0, .1); 171 | 172 | } 173 | 174 | /* 输入框盒子 */ 175 | .input-box { 176 | display: flex; 177 | /* 纵向布局 */ 178 | flex-direction: column; 179 | /* 水平居中 */ 180 | align-items: center; 181 | } 182 | 183 | /* 输入框 */ 184 | input { 185 | width: 60%; 186 | height: 40px; 187 | margin-bottom: 20px; 188 | text-indent: 10px; 189 | border: 1px solid #fff; 190 | background-color: rgba(255, 255, 255, 0.3); 191 | border-radius: 120px; 192 | /* 增加磨砂质感 */ 193 | backdrop-filter: blur(10px); 194 | } 195 | 196 | input:focus { 197 | /* 光标颜色 */ 198 | color: #b0cfe9; 199 | 200 | } 201 | 202 | /* 聚焦时隐藏文字 */ 203 | input:focus::placeholder { 204 | opacity: 0; 205 | } 206 | 207 | /* 按钮盒子 */ 208 | .btn-box { 209 | display: flex; 210 | justify-content: center; 211 | } 212 | 213 | /* 按钮 */ 214 | button { 215 | width: 100px; 216 | height: 30px; 217 | margin: 0 7px; 218 | line-height: 30px; 219 | border: none; 220 | border-radius: 4px; 221 | background-color: #69b3f0; 222 | color: white; 223 | } 224 | 225 | /* 按钮悬停时 */ 226 | button:hover { 227 | /* 鼠标小手 */ 228 | cursor: pointer; 229 | /* 透明度 */ 230 | opacity: .8; 231 | } 232 | 233 | /* 按钮文字 */ 234 | .btn-box p { 235 | height: 30px; 236 | line-height: 30px; 237 | /* 禁止选中 */ 238 | user-select: none; 239 | font-size: 14px; 240 | color: white; 241 | 242 | } 243 | 244 | .btn-box p:hover { 245 | cursor: pointer; 246 | border-bottom: 1px solid white; 247 | } -------------------------------------------------------------------------------- /resources/login.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | LOGIN 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 | 36 | 62 | 63 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 登录 91 | 92 | 93 | 94 |
95 | 96 |
97 |

WELCOME

98 |

JOIN US!

99 |
100 | 101 |
102 |
103 | 104 |
105 | 106 |
107 |

注册

108 |
109 | 110 |
111 |
112 | 113 | 114 |
115 | 116 |
117 | 118 | 119 | 120 |

已有账号?去登录

121 |
122 |
123 | 124 | 144 |
145 | 166 | 191 |
192 | 193 |
194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /resources/picture.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | SAKURA-图片 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 | 61 | 62 | 63 |
64 |
65 |
66 | 67 |
68 | about image 70 |
71 | 72 |
73 |

图片

74 |
75 | 76 |
77 |
78 |
79 |
80 |
81 | 84 |
85 | 86 |
87 |
88 | 89 |
90 |
91 | 92 |
93 |
94 | 95 |
96 |
97 |
98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /resources/register.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | REGISTER 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 | 30 | 31 | 32 | 58 | 59 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 登录 87 | 88 | 89 | 90 |
91 | 92 |
93 |

WELCOME

94 |

JOIN US!

95 |
96 | 97 |
98 |
99 | 100 | 101 | 121 | 122 |
123 | 124 |
125 |

注册

126 |
127 | 128 |
129 |
130 | 131 | 132 |
133 | 134 |
135 | 136 | 137 | 138 |

已有账号?去登录

139 |
140 |
141 |
142 | 163 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /resources/response.txt: -------------------------------------------------------------------------------- 1 | ./resources/files/项目介绍.md -------------------------------------------------------------------------------- /resources/upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UPLOAD 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 26 |
27 |
28 | 29 | 30 | 31 | 57 | 58 | 59 |
60 |
61 |
62 |
63 |

文件上传

64 |
65 |
66 |
67 | 68 |
69 | 70 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /resources/video.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SAKURA-视频 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 | 21 |
22 |
23 | 24 | 48 | 49 |
50 |
51 |
52 |
53 | about image 55 |
56 |
57 |

视频

58 |
59 |
60 |
61 |
62 | 63 |
64 |
65 |
66 |
69 |
70 |
71 |
74 |
75 |
76 |
79 |
80 |
81 |
82 |
83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /resources/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SAKURA-欢迎 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 | 23 |
24 |
25 | 26 | 27 | 28 | 54 | 55 |
56 | 57 |
58 |
59 | 60 |
61 | about image 63 |
64 | 65 |
66 |

欢迎您!

67 | 68 |
69 | 70 |
71 |
72 |
73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | sudo ./bin/simpleserver --------------------------------------------------------------------------------