├── CGImysql ├── sql_connection_pool.cpp └── sql_connection_pool.h ├── Debug.md ├── LICENSE ├── README.md ├── README_local.md ├── TinyWebServer.drawio ├── bin ├── skiplist_stress_test └── skiplist_test ├── build.sh ├── config.cpp ├── config.h ├── http ├── http_conn.cpp └── http_conn.h ├── locker └── locker.h ├── log ├── block_queue.h ├── log.cpp └── log.h ├── main.cpp ├── makefile ├── resource ├── TinyWebServer.mp4 ├── TinyWebServer.png ├── TinyWebServer0.png ├── TinyWebServer1.png ├── TinyWebServer2.png ├── TinyWebServer3.png ├── skiplist.png ├── skiplist_test1.png └── skiplist_test2.png ├── root ├── awei.html ├── awei.png ├── judge.html ├── log.html ├── logError.html ├── picture.html ├── pricture.png ├── register.html ├── registerError.html ├── video.html ├── video.mp4 └── welcome.html ├── skiplist └── skiplist.h ├── store └── dumpFile ├── test ├── makefile ├── skiplist_stress_test.cpp ├── skiplist_stress_test.sh └── skiplist_test.cpp ├── threadpool └── threadpool.h ├── timer ├── list_timer.cpp └── list_timer.h ├── webserver.cpp └── webserver.h /CGImysql/sql_connection_pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "sql_connection_pool.h" 11 | 12 | using namespace std; 13 | 14 | Connection_Pool::Connection_Pool() 15 | { 16 | m_CurrentConnections = 0; 17 | m_FreeConnections = 0; 18 | } 19 | 20 | Connection_Pool::~Connection_Pool() 21 | { 22 | DestroyPool(); 23 | } 24 | 25 | // RAII 26 | Connection_Pool *Connection_Pool::get_Instance() 27 | { 28 | static Connection_Pool connection_pool; 29 | return &connection_pool; 30 | } 31 | 32 | // Initialize the connection pool 33 | void Connection_Pool::init(string url,string user,string password, 34 | string DataBaseName,int port,int maxConnections,int close_log) 35 | { 36 | // Initializing the Data Base 37 | m_url = url; 38 | m_user = user; 39 | m_password = password; 40 | m_DataBaseName = DataBaseName; 41 | m_port = port; 42 | m_close_log = close_log; 43 | 44 | // Creating the max connection of sql 45 | for( int i = 0; i < maxConnections; i++ ) 46 | { 47 | MYSQL *con = NULL; 48 | con = mysql_init(con); 49 | 50 | if( con == NULL ) 51 | { 52 | LOG_ERROR("MySQL Error: Unable to create connection"); 53 | exit(1); 54 | } 55 | 56 | con = mysql_real_connect(con, url.c_str(),user.c_str(),password.c_str(), 57 | DataBaseName.c_str(),port,NULL,0); 58 | 59 | if( con == NULL ) 60 | { 61 | LOG_ERROR("MySQL Error: Unable to create connection"); 62 | exit(1); 63 | } 64 | 65 | // Updating the connection pool 66 | connection_list.push_back(con); 67 | m_FreeConnections++; 68 | } 69 | 70 | // Updating the max connections 71 | reserve = sem(m_FreeConnections); 72 | m_MaxConnections = m_FreeConnections; 73 | 74 | 75 | } 76 | 77 | // Getting a free connection from the pool 78 | MYSQL *Connection_Pool::GetConnection() 79 | { 80 | MYSQL *con = NULL; 81 | 82 | if( connection_list.size() == 0 ) 83 | { 84 | return NULL; 85 | } 86 | 87 | reserve.wait(); 88 | 89 | lock.lock(); 90 | 91 | con = connection_list.front(); 92 | connection_list.pop_front(); 93 | 94 | --m_FreeConnections; 95 | ++m_CurrentConnections; 96 | 97 | lock.unlock(); 98 | 99 | return con; 100 | } 101 | 102 | // Release a connection back to the pool 103 | bool Connection_Pool::ReleaseConnection(MYSQL* con) 104 | { 105 | if( con == NULL ) 106 | { 107 | return false; 108 | } 109 | 110 | lock.lock(); 111 | 112 | connection_list.push_back(con); 113 | ++m_FreeConnections; 114 | --m_CurrentConnections; 115 | 116 | lock.unlock(); 117 | 118 | // Post + 1 119 | reserve.post(); 120 | return true; 121 | } 122 | 123 | // Destroying the connection pool 124 | void Connection_Pool::DestroyPool() 125 | { 126 | lock.lock(); 127 | 128 | if( connection_list.size() > 0 ) 129 | { 130 | list::iterator it; 131 | for( it = connection_list.begin(); it != connection_list.end(); ++it) 132 | { 133 | MYSQL *connection = *it; 134 | mysql_close(connection); 135 | } 136 | 137 | m_CurrentConnections = 0; 138 | m_FreeConnections = 0; 139 | connection_list.clear(); 140 | } 141 | 142 | lock.unlock(); 143 | 144 | } 145 | 146 | // Getting the number of free connections 147 | int Connection_Pool::GetFreeConnections() 148 | { 149 | return this->m_FreeConnections; 150 | } 151 | 152 | // *!* Using RAII to release/get the connection 153 | 154 | Connection_RAII::Connection_RAII(MYSQL **SQL, Connection_Pool *connection_pool) 155 | { 156 | *SQL = connection_pool->GetConnection(); 157 | 158 | con_RAII = *SQL; 159 | pool_RAII = connection_pool; 160 | } 161 | 162 | Connection_RAII::~Connection_RAII() 163 | { 164 | pool_RAII->ReleaseConnection(con_RAII); 165 | } 166 | 167 | 168 | -------------------------------------------------------------------------------- /CGImysql/sql_connection_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef SQL_CONNECTION_POOL_H 2 | #define SQL_CONNECTION_POOL_H 3 | 4 | // *!* This project is divided into two parts: Connection Pool and Sign/Register function 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../locker/locker.h" 15 | #include "../log/log.h" 16 | 17 | 18 | using namespace std; 19 | class Connection_Pool 20 | { 21 | public: 22 | MYSQL *GetConnection(); // Get the connection 23 | bool ReleaseConnection(MYSQL *connection); // Release the connection 24 | int GetFreeConnections(); // Get the free connection 25 | void DestroyPool(); // Destroy the connection pool 26 | 27 | // Single model 28 | static Connection_Pool *get_Instance(); // Get the instance 29 | 30 | // Initialize the connection pool 31 | void init(string url,string user,string password, 32 | string DataBaseName,int port,int maxConnections,int close_log); 33 | 34 | private: 35 | Connection_Pool(); 36 | ~Connection_Pool(); 37 | 38 | int m_MaxConnections; // Max number of connections 39 | int m_CurrentConnections; // Current number of connections 40 | int m_FreeConnections; // Number of free connections 41 | 42 | locker lock; 43 | 44 | list connection_list; // List of connections 45 | sem reserve; 46 | 47 | public: 48 | string m_url; // URL of the connection pool 49 | string m_user; // Username of the connection pool 50 | string m_port; // Port of the connection pool 51 | string m_DataBaseName; // Data base name 52 | string m_password; // Password of the connection pool 53 | 54 | int m_close_log; // Close connection flag 55 | 56 | }; 57 | 58 | class Connection_RAII 59 | { 60 | public: 61 | Connection_RAII(MYSQL **SQL, Connection_Pool *connection_pool); 62 | ~Connection_RAII(); 63 | 64 | private: 65 | MYSQL *con_RAII; 66 | Connection_Pool *pool_RAII; 67 | 68 | }; 69 | 70 | 71 | #endif /* SQL_CONNECTION_POOL_H */ 72 | -------------------------------------------------------------------------------- /Debug.md: -------------------------------------------------------------------------------- 1 | ```cpp 2 | bool http_conn::write() 3 | 2{ 4 | 3    int temp=0; 5 | 4    int bytes_have_send=0; 6 | 5    int bytes_to_send=m_write_idx; 7 | 6    if(bytes_to_send==0) 8 | 7    { 9 | 8        modfd(m_epollfd,m_sockfd,EPOLLIN); 10 | 9        init(); 11 | 10        return true; 12 | 11    } 13 | 12    while(1) 14 | 13    { 15 | 14        temp=writev(m_sockfd,m_iv,m_iv_count); 16 | 15        if(temp<=-1) 17 | 16        { 18 | 17            if(errno==EAGAIN) 19 | 18            { 20 | 19                modfd(m_epollfd,m_sockfd,EPOLLOUT); 21 | 20                return true; 22 | 21            } 23 | 22            unmap(); 24 | 23            return false; 25 | 24        } 26 | 25        bytes_to_send-=temp; 27 | 26        bytes_have_send+=temp; 28 | 27        if(bytes_to_send<=bytes_have_send) 29 | 28        { 30 | 29            unmap(); 31 | 30            if(m_linger) 32 | 31            { 33 | 32                init(); 34 | 33                modfd(m_epollfd,m_sockfd,EPOLLIN); 35 | 34                return true; 36 | 35            } 37 | 36            else 38 | 37            { 39 | 38                modfd(m_epollfd,m_sockfd,EPOLLIN); 40 | 39                return false; 41 | 40            } 42 | 41        } 43 | 42    } 44 | 43} 45 | 46 | ``` 47 | 48 | ```cpp 49 | bool http_conn::write() 50 | 2{ 51 | 3    int temp = 0; 52 | 4 53 | 5    int newadd = 0; 54 | 6 55 | 7    if (bytes_to_send == 0) 56 | 8    { 57 | 9        modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); 58 | 10        init(); 59 | 11        return true; 60 | 12    } 61 | 13 62 | 14    while (1) 63 | 15    { 64 | 16        temp = writev(m_sockfd, m_iv, m_iv_count); 65 | 17 66 | 18        if (temp >= 0) 67 | 19        { 68 | 20            bytes_have_send += temp; 69 | 21            newadd = bytes_have_send - m_write_idx; 70 | 22        } 71 | 23        else 72 | 24        { 73 | 25            if (errno == EAGAIN) 74 | 26            { 75 | 27                if (bytes_have_send >= m_iv[0].iov_len) 76 | 28                { 77 | 29                    m_iv[0].iov_len = 0; 78 | 30                    m_iv[1].iov_base = m_file_address + newadd; 79 | 31                    m_iv[1].iov_len = bytes_to_send; 80 | 32                } 81 | 33                else 82 | 34                { 83 | 35                    m_iv[0].iov_base = m_write_buf + bytes_have_send; 84 | 36                    m_iv[0].iov_len = m_iv[0].iov_len - bytes_have_send; 85 | 37                } 86 | 38                modfd(m_epollfd, m_sockfd, EPOLLOUT, m_TRIGMode); 87 | 39                return true; 88 | 40            } 89 | 41            unmap(); 90 | 42            return false; 91 | 43        } 92 | 44        bytes_to_send -= temp; 93 | 45        if (bytes_to_send <= 0) 94 | 46 95 | 47        { 96 | 48            unmap(); 97 | 49            modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); 98 | 50 99 | 51            if (m_linger) 100 | 52            { 101 | 53                init(); 102 | 54                return true; 103 | 55            } 104 | 56            else 105 | 57            { 106 | 58                return false; 107 | 59            } 108 | 60        } 109 | 61    } 110 | 62} 111 | 112 | 113 | 114 | 115 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 awei-lwj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyWebServer 2 | 3 | ## Introduction 4 | 5 | Linux下的一个C++轻量级Web服务器。 6 | 7 | **Key Points**: 8 | 9 | - 使用 **线程池** + **非阻塞socket** + **epoll(ET与LT均实现)** + **事件处理(Reactor + 模拟Proactor)** 的并发模型 10 | - 使用 **状态机**解析HTTP请求报文,支持解析**GET和POST请求** 11 | - 使用 priority queue 实现的**最小堆结构管理定时器**,使用标记删除,以支持惰性删除,提高性能 12 | - 使用 **RAII**手法封装互斥器(pthread_mutex_t)、 条件变量(pthread_cond_t)等线程同步互斥机制,管理文件描述符等资源,避免锁争用 13 | - 访问**服务器数据库**实现web端**用户注册、登录功能**,可以**请求服务器图片和视频文件** 14 | - 实现**同步/异步日志系统**,记录服务器运行状态 15 | - 内置一个基于跳表实现的**轻量级key-value型存储引擎**,使用C++实现。可以实现的**操作**有:插入数据、删除数据、查询数据、文件落盘、文件加载数据以及数据库显示大小 16 | 17 | **Tests**: 18 | 19 | - 经过WebBench压力测试可以实现**上万的并发连接数据交换** 20 | - 随机读写的情况下,所实现的轻量型key-value存储引擎,每秒可处理的的**写请求数量(QPS)**:12.2819 W,每秒可处理的**读请求数目(QPS)**: 14.5819 W。 21 | 22 | **TODO**: 23 | 24 | - WebServer以及内置的key-value存储引擎的测试不是全自动化的 25 | - 加上一致性协议,提供分布式的存储服务 26 | - 实现循环缓冲区来实现同步/异步日志系统 27 | - 实现类 MutexLockGuard,避免锁争用问题 28 | - 完善eventlooppool进行事件管理和使用C++11的shared_ptr的智能指针进行内存管理 29 | - 完善skiplist来代替mysql数据库提供分布式服务 30 | 31 | ## Function Demonstration 32 | 33 | ### 1. Key-Value存储引擎 34 | 35 | Key-Value存储引擎的stress test在下文,这里演示Key-Value相应的功能 36 | 37 | SkipList API: 38 | 39 | - [x] insert_element(进行element插入) 40 | - [x] delete_element(进行element删除) 41 | - [x] serach_element(进行element查找) 42 | - [x] display_list(进行skiplist展示) 43 | - [x] dump_file(数据落盘) 44 | - [x] load_file(数据加载) 45 | - [x] size(返回数据落盘) 46 | 47 | -------- 48 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/86aad683aaec4c3c9216abefda439f34.png#pic_center)![在这里插入图片描述](https://img-blog.csdnimg.cn/66a6e138c372428c9de4365a59b30f7d.png#pic_center) 49 | 50 | 51 | ### 2.TinyWebServer 52 | 53 | - [x] 注册 54 | - [x] 已有用户 55 | - [x] 登录 56 | - [x] 密码错误 57 | - [x] 图片测试(海绵宝宝11.8M),视频测试(Kruskal算法和Prim算法,22.5M) 58 | - [x] Making friends with awei 59 | 60 | 61 | [video(video-ixKOhPg5-1674019344246)(type-csdn)(url-https://live.csdn.net/v/embed/270761)(image-https://video-community.csdnimg.cn/vod-84deb4/36ba9c7096e571edbfc76723a78f0102/snapshots/27141e426ea947aaa88c564a0a3def70-00005.jpg?auth_key=4827614704-0-0-e15be31db2d61fc71dc7b0194682568b)(title-TinyWebServer)] 62 | 63 | ## Framework 64 | 65 | ### 1.Key-Vaule Storage Enginee based on the skip list 66 | 67 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/035de4884e9b404dab9784a48e8382a7.png#pic_center) 68 | 69 | 70 | ### 2. TinyWebServer 71 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/feb99cf6dcba4d65ad5aa5ef6dea7750.png#pic_center) 72 | 73 | ## Configuration 74 | 75 | - VMare Workstaion + Ubuntu 22.10 76 | - C++ 11 Configuration 77 | - MySQL 78 | - make + cmake + shell 79 | - Webbench 80 | 81 | ## Build 82 | 83 | ### 1. Key-Value Storgae Enginee 84 | 85 | Stress Tests: 86 | 87 | ~~~shell 88 | cd test/ 89 | sh skiplist_stress_test.sh // 数据量一开始设定为一百万 90 | ~~~ 91 | 92 | Key-Value enginee operation mode 93 | 94 | ~~~shell 95 | make 96 | ../bin/skiplist_test 97 | ~~~ 98 | 99 | ### 2.TinyWebServer 100 | 101 | - 虚拟机测试环境 102 | - Ubuntu 22.10 103 | - mysql Ver 8.0.31-0ubuntu2 for Linux on x86_64 ((Ubuntu)) 104 | 105 | - 游览器测试环境 106 | - Linux/Windows 107 | - Chrone/FireFox 108 | - 其他游览器没有测试 109 | 110 | - 在进行**游览器测试**之前请先确保已经安装好MySQL以及相应的MySQL库 111 | 112 | ~~~shell 113 | // Install mysql and mysql dependency library 114 | sudo apt install mysql-server -y 115 | sudo apt install libmysqlclient-dev -y 116 | ~~~ 117 | 118 | ~~~shell 119 | // 建立yourdb库 120 | create database yourdb; 121 | 122 | // 创建user表 123 | USE yourdb; 124 | CREATE TABLE user( 125 | username char(50) NULL, 126 | passwd char(50) NULL 127 | )ENGINE=InnoDB; 128 | 129 | // 添加数据 130 | INSERT INTO user(username, passwd) VALUES('name', 'passwd'); 131 | ~~~ 132 | 133 | - 修改main.cpp中的数据库的初始化信息 134 | 135 | ~~~shell 136 | //数据库登录名,密码,库名 137 | string user = "root"; 138 | string passwd = "root"; 139 | string databasename = "yourdb"; 140 | ~~~ 141 | 142 | - build以及启动server 143 | 144 | ~~~shell 145 | sh ./build.sh 146 | ./server 147 | ~~~ 148 | 149 | - 用ifconfig查找本机的ip地址,然后在游览器端口处进行相应的IP地址访问 150 | 151 | ~~~shell 152 | ifconfig 153 | 154 | // 游览器端 155 | ip:9096 156 | ~~~ 157 | 158 | - 在进行**Webbench测试**之前请先确保已经安装好Webbench以及相应的依赖库 159 | 160 | ~~~shell 161 | sudo apt install ctags 162 | cd test/Webbench 163 | make 164 | ~~~ 165 | 166 | ## Command for server 167 | 168 | ~~~shell 169 | ./server [-p port] [-l LOGWrite] [-m TRIGMode] [-o OPT_LINGER] [-s sql_num] [-t thread_num] [-c close_log] [-a actor_model] 170 | ~~~ 171 | 172 | 并非全部都要使用,个性化设置即可 173 | 174 | - -p,自定义端口号 175 | - 默认9006 176 | 177 | - -l,选择日志写入方式,默认同步写入 178 | - 0,同步写入 179 | - 1,异步写入 180 | 181 | - -m,listenfd和connfd的模式组合,默认使用LT + LT 182 | - 0,表示使用LT + LT 183 | - 1,表示使用LT + ET 184 | - 2,表示使用ET + LT 185 | - 3,表示使用ET + ET 186 | 187 | - -o,优雅关闭连接,默认不使用 188 | - 0,不使用 189 | - 1,使用 190 | - -s,数据库连接数量 191 | - 默认为8 192 | - -t,线程数量 193 | - 默认为8 194 | - -c,关闭日志,默认打开 195 | - 0, 打开日志 196 | - 1,关闭日志 197 | - -a,选择反应堆模型,默认Proactor 198 | - 0,Proactor模型 199 | - 1,Reactor模型 200 | 201 | ## Tests 202 | 203 | ### 1.Key-Value Storgae Enginee 204 | 205 | Insert element operation 206 | | 插入的数据规模(万条)| 耗费时间 | 207 | |---|---| 208 | | 1 | 0.0625142 | 209 | | 10| 0.688054 | 210 | | 15| 1.2213 | 211 | | 20| 1.77769 | 212 | | 50| 5.33906 | 213 | | 100| 11.5311 | 214 | 215 | 每秒可以处理的写请求数目(QPS):12.2819 W 216 | 217 | Get element operation 218 | | 读取的数据规模(万条)| 耗费时间 | 219 | |---|---| 220 | | 1 | 0.0443168 | 221 | | 10| 0.96824 | 222 | | 15| 1.12037 | 223 | | 20| 1.17522 | 224 | | 50| 4.14332 | 225 | | 100| 9.7482 | 226 | 227 | 每秒可以处理的读请求数目(QPS):14.5819 W 228 | 229 | ### 2.WebServer 230 | 231 | 在关闭服务器日志之后,使用Webbench对服务器进行压力测试,对listen_fd和connection_fd分别采用ET或LT模式,均可以实现近万的并发连接以下列出组合后的测试结果 232 | 233 | - Proactor,TRIGMode = 0, Listen_fd = LT, connection_fd = LT,QPS = 202927 234 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/712a247abb1a43148fbaeb819a895b87.png#pic_center) 235 | - Proactor,TRIGMode = 1, Listen_fd = LT, connection_fd = ET, QPS = 216614 236 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/fbf2d5ff8e8d493aa8a280634bd160f5.png#pic_center) 237 | - Proactor,TRIGMode = 2, Listen_fd = ET, connection_fd = LT, QPS = 124588 238 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/5a04c90fb2744ab19c636f9f7595e8bb.png#pic_center) 239 | 240 | - Proactor,TRIGMode = 3, Listen_fd = ET, connection_fd = ET, QPS = 180345 241 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/460cc98e84974c8584a391dfee005d7e.png#pic_center) 242 | 243 | 244 | ## Debug过程中的一些难点 245 | 246 | - [x] 小的bug不多说 247 | - [x] 由于并发模型构建的不完善,seg implement在查看了游侠的高性能网络编程以及学会了利用GDB调试来进行查看线程安全问题 248 | - [x] 书上的http对大文件进行读取存在问题,在查阅资料和问了大佬之后得以修复 249 | 250 | ~~~cpp 251 | // 原先代码 252 | bool http_conn::write() 253 | { 254 | int temp=0; 255 | int bytes_have_send=0; 256 | int bytes_to_send=m_write_idx; 257 | if(bytes_to_send==0) 258 | { 259 | modfd(m_epollfd,m_sockfd,EPOLLIN); 260 | init(); 261 | return true; 262 | } 263 | while(1) 264 | { 265 | temp=writev(m_sockfd,m_iv,m_iv_count); 266 | if(temp<=-1) 267 | { 268 | if(errno==EAGAIN) 269 | { 270 | modfd(m_epollfd,m_sockfd,EPOLLOUT); 271 | return true; 272 | } 273 | unmap(); 274 | return false; 275 | } 276 | bytes_to_send-=temp; 277 | bytes_have_send+=temp; 278 | if(bytes_to_send<=bytes_have_send) 279 | { 280 | unmap(); 281 | if(m_linger) 282 | { 283 | init(); 284 | modfd(m_epollfd,m_sockfd,EPOLLIN); 285 | return true; 286 | } 287 | else 288 | { 289 | modfd(m_epollfd,m_sockfd,EPOLLIN); 290 | return false; 291 | } 292 | } 293 | } 294 | } 295 | 296 | ~~~ 297 | 298 | --- 299 | 300 | ~~~cpp 301 | // Debug 302 | bool http_conn::write() 303 | { 304 | int temp = 0; 305 | 306 | int newadd = 0; 307 | 308 | if (bytes_to_send == 0) 309 | { 310 | modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); 311 | init(); 312 | return true; 313 | } 314 | 315 | while (1) 316 | { 317 | temp = writev(m_sockfd, m_iv, m_iv_count); 318 | 319 | if (temp >= 0) 320 | { 321 | bytes_have_send += temp; 322 | newadd = bytes_have_send - m_write_idx; 323 | } 324 | else 325 | { 326 | if (errno == EAGAIN) 327 | { 328 | if (bytes_have_send >= m_iv[0].iov_len) 329 | { 330 | m_iv[0].iov_len = 0; 331 | m_iv[1].iov_base = m_file_address + newadd; 332 | m_iv[1].iov_len = bytes_to_send; 333 | } 334 | else 335 | { 336 | m_iv[0].iov_base = m_write_buf + bytes_have_send; 337 | m_iv[0].iov_len = m_iv[0].iov_len - bytes_have_send; 338 | } 339 | modfd(m_epollfd, m_sockfd, EPOLLOUT, m_TRIGMode); 340 | return true; 341 | } 342 | unmap(); 343 | return false; 344 | } 345 | bytes_to_send -= temp; 346 | if (bytes_to_send <= 0) 347 | { 348 | unmap(); 349 | modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); 350 | 351 | if (m_linger) 352 | { 353 | init(); 354 | return true; 355 | } 356 | else 357 | { 358 | return false; 359 | } 360 | } 361 | } 362 | } 363 | 364 | 365 | ~~~ 366 | -------------------------------------------------------------------------------- /README_local.md: -------------------------------------------------------------------------------- 1 | # TinyWebServer 2 | 3 | ## Introduction 4 | 5 | Linux下的一个C++轻量级Web服务器。 6 | 7 | **Key Points**: 8 | 9 | - 使用 **线程池** + **非阻塞socket** + **epoll(ET与LT均实现)** + **事件处理(Reactor + 模拟Proactor)** 的并发模型 10 | - 使用 **状态机**解析HTTP请求报文,支持解析**GET和POST请求** 11 | - 使用 priority queue 实现的**最小堆结构管理定时器**,使用标记删除,以支持惰性删除,提高性能 12 | - 使用 **RAII**手法封装线程连接池 13 | - 使用**单例模式**进行资源管理 14 | - 访问**服务器数据库**实现web端**用户注册、登录功能**,可以**请求服务器图片和视频文件** 15 | - 实现**同步/异步日志系统**,记录服务器运行状态 16 | - 内置一个基于跳表实现的**轻量级key-value型存储引擎**,使用C++实现。可以实现的**操作**有:插入数据、删除数据、查询数据、文件落盘、文件加载数据以及数据库显示大小 17 | 18 | **Tests**: 19 | 20 | - 经过WebBench压力测试可以实现**上万的并发连接数据交换** 21 | - 随机读写的情况下,所实现的轻量型key-value存储引擎,每秒可处理的的**写请求数量(QPS)**:24.39W,每秒可处理的**读请求数目(QPS)**: 18.41W。 22 | 23 | **TODO**: 24 | 25 | - WebServer以及内置的key-value存储引擎的测试不是全自动化的 26 | - 加上一致性协议,提供分布式的存储服务 27 | - 实现循环缓冲区来实现同步/异步日志系统 28 | - 实现类 MutexLockGuard,避免锁争用问题 29 | - 完善eventlooppool进行事件管理和使用C++11的shared_ptr的智能指针进行内存管理 30 | - 完善skiplist来代替mysql数据库提供分布式服务 31 | 32 | ## Function Demonstration 33 | 34 | ### 1. Key-Value存储引擎 35 | 36 | Key-Value存储引擎的stress test在下文,这里演示Key-Value相应的功能 37 | 38 | SkipList API: 39 | 40 | - [x] insert_element(进行element插入) 41 | - [x] delete_element(进行element删除) 42 | - [x] serach_element(进行element查找) 43 | - [x] display_list(进行skiplist展示) 44 | - [x] dump_file(数据落盘) 45 | - [x] load_file(数据加载) 46 | - [x] size(返回数据落盘) 47 | 48 | -------- 49 | 50 | ![skiplist1](./resource/skiplist_test1.png) 51 | 52 | 删除key5/7并且展示跳表 53 | 54 | ![skiplist2](./resource/skiplist_test2.png) 55 | 56 | ### 2.TinyWebServer 57 | 58 | - [x] 注册 59 | - [x] 已有用户 60 | - [x] 登录 61 | - [x] 密码错误 62 | - [x] 图片测试(海绵宝宝11.8M),视频测试(Kruskal算法和Prim算法,22.5M) 63 | - [x] Making friends with awei 64 | 65 | ~~~cpp 66 | // TODO: