├── .vscode ├── c_cpp_properties.json └── settings.json ├── CGIRedis ├── README.md ├── redis_connection_pool.cpp └── redis_connection_pool.h ├── CGImysql ├── README.md ├── sql_connection_pool.cpp └── sql_connection_pool.h ├── LICENSE ├── README.md ├── build.sh ├── config.cpp ├── config.h ├── hiredis ├── hiredis.c └── hiredis.h ├── http ├── README.md ├── http_conn.cpp └── http_conn.h ├── lock ├── README.md └── locker.h ├── log ├── README.md ├── block_queue.h ├── log.cpp └── log.h ├── main.cpp ├── makefile ├── root ├── README.md ├── fans.html ├── favicon.ico ├── frame.jpg ├── judge.html ├── log.html ├── logError.html ├── login.gif ├── loginnew.gif ├── picture.gif ├── picture.html ├── register.gif ├── register.html ├── registerError.html ├── registernew.gif ├── repeated.html ├── video.gif ├── video.html ├── welcome.html ├── xxx.jpg └── xxx.mp4 ├── test_presure ├── README.md └── webbench-1.5 │ ├── COPYRIGHT │ ├── ChangeLog │ ├── Makefile │ ├── debian │ ├── changelog │ ├── control │ ├── copyright │ ├── dirs │ └── rules │ ├── socket.c │ ├── tags │ ├── webbench │ ├── webbench.1 │ ├── webbench.c │ └── webbench.o ├── threadpool ├── README.md └── threadpool.h ├── timer ├── README.md ├── lst_timer.cpp └── lst_timer.h ├── webserver.cpp └── webserver.h /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "_DEBUG", 10 | "UNICODE", 11 | "_UNICODE" 12 | ], 13 | "compilerPath": "C:\\MinGW\\bin\\g++.exe", 14 | "cStandard": "c17", 15 | "cppStandard": "c++17", 16 | "intelliSenseMode": "windows-gcc-x86" 17 | } 18 | ], 19 | "version": 4 20 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.tcc": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /CGIRedis/README.md: -------------------------------------------------------------------------------- 1 | 校验 & 数据库连接池 2 | =============== 3 | Redis数据库连接池 4 | 登陆校验与超时连接设置 5 | 连接池单例模式,静态大小 6 | Redis连接类 7 | RAII控制连接资源 -------------------------------------------------------------------------------- /CGIRedis/redis_connection_pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "redis_connection_pool.h" 10 | 11 | using namespace std; 12 | 13 | RedisConnectionPool::RedisConnectionPool() 14 | { 15 | this->m_FreeConn=0; 16 | this->m_CurConn=0; 17 | } 18 | 19 | CacheConn::CacheConn(){ 20 | this->m_last_connect_time=0; 21 | this->m_pContext=NULL; 22 | } 23 | 24 | CacheConn::~CacheConn(){ 25 | if(this->m_pContext) 26 | { 27 | redisFree(this->m_pContext); 28 | this->m_pContext = NULL; 29 | } 30 | LOG_ERROR("redis content from list close"); 31 | } 32 | 33 | int CacheConn::Init(string Url, int Port, int LogCtl, string r_PassWord) 34 | { 35 | //重连 36 | time_t cur_time = time(NULL); 37 | if(cur_time < m_last_connect_time +4){ 38 | return 1; 39 | } 40 | 41 | m_last_connect_time = cur_time; 42 | 43 | struct timeval timeout 44 | { 45 | 0,200000 46 | }; 47 | 48 | m_pContext = redisConnectWithTimeout(Url.c_str(), Port, timeout); 49 | m_close_log = LogCtl; 50 | R_password = r_PassWord; 51 | 52 | if(!m_pContext || m_pContext->err) 53 | { 54 | if(m_pContext) 55 | { 56 | redisFree(m_pContext); 57 | m_pContext = NULL; 58 | } 59 | LOG_ERROR("redis connect failed"); 60 | return 1; 61 | } 62 | 63 | redisReply* reply; 64 | //登陆验证: 65 | if(R_password != "") 66 | { 67 | reply = (redisReply *)redisCommand(m_pContext, "AUTH %s", R_password.c_str()); 68 | if(!reply || reply->type == REDIS_REPLY_ERROR) 69 | { 70 | if(reply){ 71 | freeReplyObject(reply); 72 | } 73 | return -1; 74 | } 75 | freeReplyObject(reply); 76 | } 77 | 78 | reply = (redisReply *)redisCommand(m_pContext, "SELECT %d", 0); 79 | if (reply && (reply->type == REDIS_REPLY_STATUS) && (strncmp(reply->str, "OK", 2) == 0)) 80 | { 81 | freeReplyObject(reply); 82 | return 0; 83 | } 84 | else 85 | { 86 | if (reply) 87 | LOG_ERROR("select cache db failed:%s\n", reply->str); 88 | return 2; 89 | } 90 | 91 | } 92 | 93 | RedisConnectionPool *RedisConnectionPool::RedisPoolInstance() 94 | { 95 | static RedisConnectionPool ConPool; 96 | return &ConPool; 97 | } 98 | 99 | void RedisConnectionPool::init(string url, string User, string PassWord, string DBName, int Port, int MaxConn, int close_log) 100 | { 101 | m_Url = url; 102 | m_Port = Port; 103 | m_User = User; 104 | m_PassWord = PassWord; 105 | m_DatabaseName = DBName; 106 | m_close_log = close_log; 107 | 108 | for (int i = 0; i < MaxConn; i++) 109 | { 110 | CacheConn *con = NULL; 111 | con = new CacheConn; 112 | 113 | int r = con->Init(m_Url, Port, close_log, m_PassWord); 114 | if( r != 0 || con == NULL) 115 | { 116 | if(r == 1) 117 | { 118 | delete con; 119 | } 120 | LOG_ERROR("Redis Error"); 121 | exit(1); 122 | } 123 | LOG_INFO("redis con in pool init res: %lu", r); 124 | connList.push_back(con); 125 | ++m_FreeConn; 126 | } 127 | 128 | reserve = sem(m_FreeConn); 129 | m_MaxConn = m_FreeConn; 130 | 131 | LOG_ERROR("cache pool: %s, list size: %lu", m_DatabaseName.c_str(), connList.size()); 132 | } 133 | 134 | //当有请求时,从数据库连接池中返回一个可用连接,更新使用和空闲连接数 135 | CacheConn *RedisConnectionPool::GetRedisConnection() 136 | { 137 | CacheConn *con = NULL; 138 | 139 | if (0 == connList.size()) 140 | { 141 | LOG_ERROR("redis con queue is empty"); 142 | return NULL; 143 | } 144 | 145 | reserve.wait(); 146 | 147 | lock.lock(); 148 | 149 | con = connList.front(); 150 | connList.pop_front(); 151 | 152 | --m_FreeConn; 153 | ++m_CurConn; 154 | 155 | lock.unlock(); 156 | return con; 157 | } 158 | 159 | //释放当前使用的连接 160 | bool RedisConnectionPool::RedisDisconnection(CacheConn *con) 161 | { 162 | if (NULL == con) 163 | return false; 164 | 165 | lock.lock(); 166 | 167 | list::iterator it; 168 | for (it = connList.begin(); it != connList.end(); ++it) 169 | { 170 | if(*it == con){ 171 | break; 172 | } 173 | } 174 | if(it == connList.end()) 175 | { 176 | connList.push_back(con); 177 | } 178 | ++m_FreeConn; 179 | --m_CurConn; 180 | LOG_INFO("redis con back to list RAII"); 181 | 182 | lock.unlock(); 183 | 184 | reserve.post(); 185 | return true; 186 | } 187 | 188 | //销毁数据库连接池 189 | void RedisConnectionPool::DestroyRedisPool() 190 | { 191 | lock.lock(); 192 | if (connList.size() > 0) 193 | { 194 | list::iterator it; 195 | for (it = connList.begin(); it != connList.end(); ++it) 196 | { 197 | CacheConn *con = *it; 198 | //redisFree(con->m_pContext); 199 | con->~CacheConn(); 200 | } 201 | m_CurConn = 0; 202 | m_FreeConn = 0; 203 | connList.clear(); 204 | } 205 | 206 | lock.unlock(); 207 | } 208 | 209 | //Redis当前空闲的连接数 210 | int RedisConnectionPool::GetFreeRedisConnection() 211 | { 212 | return this->m_FreeConn; 213 | } 214 | 215 | RedisConnectionPool::~RedisConnectionPool() 216 | { 217 | DestroyRedisPool(); 218 | } 219 | 220 | RedisConnectionRAII::RedisConnectionRAII(CacheConn **Con, RedisConnectionPool *ConPool){//static 221 | *Con = ConPool->GetRedisConnection(); 222 | 223 | conRAII = *Con; 224 | poolRAII = ConPool; 225 | } 226 | 227 | RedisConnectionRAII::~RedisConnectionRAII(){ 228 | poolRAII->RedisDisconnection(conRAII); 229 | } -------------------------------------------------------------------------------- /CGIRedis/redis_connection_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef _REDIS_POOL_ 2 | #define _REDIS_POOL_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "../lock/locker.h" 12 | #include "../log/log.h" 13 | 14 | using namespace std; 15 | 16 | class CacheConn 17 | { 18 | public: 19 | int Init(string Url, int Port, int LogCtl, string r_PassWord); 20 | 21 | CacheConn(); 22 | ~CacheConn(); 23 | public: 24 | redisContext* m_pContext; 25 | int m_close_log; 26 | private: 27 | int m_last_connect_time; 28 | string R_password; 29 | }; 30 | 31 | class RedisConnectionPool 32 | { 33 | public: 34 | CacheConn* GetRedisConnection(); //遍历list 35 | void init(string url, string User, string PassWord, string DataBaseName, int Port, int MaxConn, int close_log); 36 | 37 | static RedisConnectionPool *RedisPoolInstance(); 38 | int GetFreeRedisConnection(); 39 | bool RedisDisconnection(CacheConn* Conn); 40 | void DestroyRedisPool(); 41 | 42 | private: 43 | RedisConnectionPool(); 44 | ~RedisConnectionPool(); 45 | 46 | int m_MaxConn; //最大连接数 47 | int m_CurConn; //当前已使用的连接数 48 | int m_FreeConn; //当前空闲的连接数 49 | locker lock; 50 | list connList; //连接池 51 | sem reserve; 52 | 53 | public: 54 | friend class CacheConn; 55 | string m_Url; //主机地址 56 | string m_Port; //数据库端口号 57 | string m_User; //登陆数据库用户名 58 | string m_PassWord; //登陆数据库密码 59 | string m_DatabaseName; //使用数据库名 60 | // CacheConn* pm_rct; //redis结构体 61 | int m_close_log; //日志开关 62 | }; 63 | 64 | class RedisConnectionRAII{ 65 | public: 66 | RedisConnectionRAII(CacheConn **con, RedisConnectionPool *connPool); 67 | ~RedisConnectionRAII(); 68 | 69 | private: 70 | CacheConn *conRAII; 71 | RedisConnectionPool *poolRAII; 72 | //mapm_cache_redisPool_Map; 73 | }; 74 | #endif -------------------------------------------------------------------------------- /CGImysql/README.md: -------------------------------------------------------------------------------- 1 | 2 | 校验 & 数据库连接池 3 | =============== 4 | 数据库连接池 5 | > * 单例模式,保证唯一 6 | > * list实现连接池 7 | > * 连接池为静态大小 8 | > * 互斥锁实现线程安全 9 | 10 | 校验 11 | > * HTTP请求采用POST方式 12 | > * 登录用户名和密码校验 13 | > * 用户注册及多线程注册安全 14 | -------------------------------------------------------------------------------- /CGImysql/sql_connection_pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "sql_connection_pool.h" 10 | 11 | using namespace std; 12 | 13 | connection_pool::connection_pool() 14 | { 15 | m_CurConn = 0; 16 | m_FreeConn = 0; 17 | } 18 | 19 | connection_pool *connection_pool::GetInstance() 20 | { 21 | static connection_pool connPool; 22 | return &connPool; 23 | } 24 | 25 | //构造初始化 26 | void connection_pool::init(string url, string User, string PassWord, string DBName, int Port, int MaxConn, int close_log) 27 | { 28 | m_url = url; 29 | m_Port = Port; 30 | m_User = User; 31 | m_PassWord = PassWord; 32 | m_DatabaseName = DBName; 33 | m_close_log = close_log; 34 | 35 | for (int i = 0; i < MaxConn; i++) 36 | { 37 | MYSQL *con = NULL; 38 | con = mysql_init(con); 39 | 40 | if (con == NULL) 41 | { 42 | LOG_ERROR("MySQL Error"); 43 | exit(1); 44 | } 45 | con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0); 46 | 47 | if (con == NULL) 48 | { 49 | LOG_ERROR("MySQL Error"); 50 | exit(1); 51 | } 52 | connList.push_back(con); 53 | ++m_FreeConn; 54 | } 55 | 56 | reserve = sem(m_FreeConn); 57 | 58 | m_MaxConn = m_FreeConn; 59 | } 60 | 61 | 62 | //当有请求时,从数据库连接池中返回一个可用连接,更新使用和空闲连接数 63 | MYSQL *connection_pool::GetConnection() 64 | { 65 | MYSQL *con = NULL; 66 | 67 | if (0 == connList.size()) 68 | return NULL; 69 | 70 | reserve.wait(); 71 | 72 | lock.lock(); 73 | 74 | con = connList.front(); 75 | connList.pop_front(); 76 | 77 | --m_FreeConn; 78 | ++m_CurConn; 79 | 80 | lock.unlock(); 81 | return con; 82 | } 83 | 84 | //释放当前使用的连接 85 | bool connection_pool::ReleaseConnection(MYSQL *con) 86 | { 87 | if (NULL == con) 88 | return false; 89 | 90 | lock.lock(); 91 | 92 | connList.push_back(con); 93 | ++m_FreeConn; 94 | --m_CurConn; 95 | 96 | lock.unlock(); 97 | 98 | reserve.post(); 99 | return true; 100 | } 101 | 102 | //销毁数据库连接池 103 | void connection_pool::DestroyPool() 104 | { 105 | 106 | lock.lock(); 107 | if (connList.size() > 0) 108 | { 109 | list::iterator it; 110 | for (it = connList.begin(); it != connList.end(); ++it) 111 | { 112 | MYSQL *con = *it; 113 | mysql_close(con); 114 | } 115 | m_CurConn = 0; 116 | m_FreeConn = 0; 117 | connList.clear(); 118 | } 119 | 120 | lock.unlock(); 121 | } 122 | 123 | //当前空闲的连接数 124 | int connection_pool::GetFreeConn() 125 | { 126 | return this->m_FreeConn; 127 | } 128 | 129 | connection_pool::~connection_pool() 130 | { 131 | DestroyPool(); 132 | } 133 | 134 | connectionRAII::connectionRAII(MYSQL **SQL, connection_pool *connPool){ 135 | *SQL = connPool->GetConnection(); 136 | 137 | conRAII = *SQL; 138 | poolRAII = connPool; 139 | } 140 | 141 | connectionRAII::~connectionRAII(){ 142 | poolRAII->ReleaseConnection(conRAII); 143 | } -------------------------------------------------------------------------------- /CGImysql/sql_connection_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONNECTION_POOL_ 2 | #define _CONNECTION_POOL_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "../lock/locker.h" 12 | #include "../log/log.h" 13 | 14 | using namespace std; 15 | 16 | class connection_pool 17 | { 18 | public: 19 | MYSQL *GetConnection(); //获取数据库连接 20 | bool ReleaseConnection(MYSQL *conn); //释放连接 21 | int GetFreeConn(); //获取连接 22 | void DestroyPool(); //销毁所有连接 23 | 24 | //单例模式 25 | static connection_pool *GetInstance(); 26 | 27 | void init(string url, string User, string PassWord, string DataBaseName, int Port, int MaxConn, int close_log); 28 | 29 | private: 30 | connection_pool(); 31 | ~connection_pool(); 32 | 33 | int m_MaxConn; //最大连接数 34 | int m_CurConn; //当前已使用的连接数 35 | int m_FreeConn; //当前空闲的连接数 36 | locker lock; 37 | list connList; //连接池 38 | sem reserve; 39 | 40 | public: 41 | string m_url; //主机地址 42 | string m_Port; //数据库端口号 43 | string m_User; //登陆数据库用户名 44 | string m_PassWord; //登陆数据库密码 45 | string m_DatabaseName; //使用数据库名 46 | int m_close_log; //日志开关 47 | }; 48 | 49 | class connectionRAII{ 50 | 51 | public: 52 | connectionRAII(MYSQL **con, connection_pool *connPool); 53 | ~connectionRAII(); 54 | 55 | private: 56 | MYSQL *conRAII; 57 | connection_pool *poolRAII; 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TinyWebServer with Redis Connection pool and Token-check simulations. sh ./build.sh 2 | ./server [-p port] [-l LOGWrite] [-m TRIGMode] [-o OPT_LINGER] [-s sql_num] [-r redis_num] [-t thread_num] [-c close_log] [-a actor_model] 3 | client default port:port:9006 4 | 5 | //ready to update various types DB connection version. 6 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | make server -------------------------------------------------------------------------------- /config.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | Config::Config(){ 4 | //端口号,默认9006 5 | PORT = 9006; 6 | 7 | //日志写入方式,默认同步 8 | LOGWrite = 0; 9 | 10 | //触发组合模式,默认listenfd LT + connfd LT 11 | TRIGMode = 0; 12 | 13 | //listenfd触发模式,默认LT 14 | LISTENTrigmode = 0; 15 | 16 | //connfd触发模式,默认LT 17 | CONNTrigmode = 0; 18 | 19 | //优雅关闭链接,默认不使用 20 | OPT_LINGER = 0; 21 | 22 | //数据库连接池数量,默认8 23 | sql_num = 8; 24 | redis_num = 8; 25 | 26 | //线程池内的线程数量,默认8 27 | thread_num = 8; 28 | 29 | //关闭日志,默认不关闭 30 | close_log = 0; 31 | 32 | //并发模型,默认是proactor 33 | actor_model = 0; 34 | } 35 | 36 | void Config::parse_arg(int argc, char*argv[]){ 37 | int opt; 38 | const char *str = "p:l:m:o:s:r:t:c:a:"; 39 | while ((opt = getopt(argc, argv, str)) != -1) 40 | { 41 | switch (opt) 42 | { 43 | case 'p': 44 | { 45 | PORT = atoi(optarg); 46 | break; 47 | } 48 | case 'l': 49 | { 50 | LOGWrite = atoi(optarg); 51 | break; 52 | } 53 | case 'm': 54 | { 55 | TRIGMode = atoi(optarg); 56 | break; 57 | } 58 | case 'o': 59 | { 60 | OPT_LINGER = atoi(optarg); 61 | break; 62 | } 63 | case 's': 64 | { 65 | sql_num = atoi(optarg); 66 | break; 67 | } 68 | case 'r': 69 | { 70 | redis_num = atoi(optarg); 71 | } 72 | case 't': 73 | { 74 | thread_num = atoi(optarg); 75 | break; 76 | } 77 | case 'c': 78 | { 79 | close_log = atoi(optarg); 80 | break; 81 | } 82 | case 'a': 83 | { 84 | actor_model = atoi(optarg); 85 | break; 86 | } 87 | default: 88 | break; 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #include "webserver.h" 5 | 6 | using namespace std; 7 | 8 | class Config 9 | { 10 | public: 11 | Config(); 12 | ~Config(){}; 13 | 14 | void parse_arg(int argc, char*argv[]); 15 | 16 | //端口号 17 | int PORT; 18 | 19 | //日志写入方式 20 | int LOGWrite; 21 | 22 | //触发组合模式 23 | int TRIGMode; 24 | 25 | //listenfd触发模式 26 | int LISTENTrigmode; 27 | 28 | //connfd触发模式 29 | int CONNTrigmode; 30 | 31 | //优雅关闭链接 32 | int OPT_LINGER; 33 | 34 | //数据库连接池数量 35 | int sql_num; 36 | 37 | int redis_num; 38 | 39 | //线程池内的线程数量 40 | int thread_num; 41 | 42 | //是否关闭日志 43 | int close_log; 44 | 45 | //并发模型选择 46 | int actor_model; 47 | }; 48 | 49 | #endif -------------------------------------------------------------------------------- /hiredis/hiredis.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2011, Salvatore Sanfilippo 3 | * Copyright (c) 2010-2014, Pieter Noordhuis 4 | * Copyright (c) 2015, Matt Stancliff , 5 | * Jan-Erik Rediger 6 | * 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * * Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * * Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * * Neither the name of Redis nor the names of its contributors may be used 18 | * to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "fmacros.h" 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "hiredis.h" 43 | #include "net.h" 44 | #include "sds.h" 45 | 46 | static redisReply *createReplyObject(int type); 47 | static void *createStringObject(const redisReadTask *task, char *str, size_t len); 48 | static void *createArrayObject(const redisReadTask *task, int elements); 49 | static void *createIntegerObject(const redisReadTask *task, long long value); 50 | static void *createNilObject(const redisReadTask *task); 51 | 52 | /* Default set of functions to build the reply. Keep in mind that such a 53 | * function returning NULL is interpreted as OOM. */ 54 | static redisReplyObjectFunctions defaultFunctions = { 55 | createStringObject, 56 | createArrayObject, 57 | createIntegerObject, 58 | createNilObject, 59 | freeReplyObject 60 | }; 61 | 62 | /* Create a reply object */ 63 | static redisReply *createReplyObject(int type) { 64 | redisReply *r = calloc(1,sizeof(*r)); 65 | 66 | if (r == NULL) 67 | return NULL; 68 | 69 | r->type = type; 70 | return r; 71 | } 72 | 73 | /* Free a reply object */ 74 | void freeReplyObject(void *reply) { 75 | redisReply *r = reply; 76 | size_t j; 77 | 78 | if (r == NULL) 79 | return; 80 | 81 | switch(r->type) { 82 | case REDIS_REPLY_INTEGER: 83 | break; /* Nothing to free */ 84 | case REDIS_REPLY_ARRAY: 85 | if (r->element != NULL) { 86 | for (j = 0; j < r->elements; j++) 87 | if (r->element[j] != NULL) 88 | freeReplyObject(r->element[j]); 89 | free(r->element); 90 | } 91 | break; 92 | case REDIS_REPLY_ERROR: 93 | case REDIS_REPLY_STATUS: 94 | case REDIS_REPLY_STRING: 95 | if (r->str != NULL) 96 | free(r->str); 97 | break; 98 | } 99 | free(r); 100 | } 101 | 102 | static void *createStringObject(const redisReadTask *task, char *str, size_t len) { 103 | redisReply *r, *parent; 104 | char *buf; 105 | 106 | r = createReplyObject(task->type); 107 | if (r == NULL) 108 | return NULL; 109 | 110 | buf = malloc(len+1); 111 | if (buf == NULL) { 112 | freeReplyObject(r); 113 | return NULL; 114 | } 115 | 116 | assert(task->type == REDIS_REPLY_ERROR || 117 | task->type == REDIS_REPLY_STATUS || 118 | task->type == REDIS_REPLY_STRING); 119 | 120 | /* Copy string value */ 121 | memcpy(buf,str,len); 122 | buf[len] = '\0'; 123 | r->str = buf; 124 | r->len = len; 125 | 126 | if (task->parent) { 127 | parent = task->parent->obj; 128 | assert(parent->type == REDIS_REPLY_ARRAY); 129 | parent->element[task->idx] = r; 130 | } 131 | return r; 132 | } 133 | 134 | static void *createArrayObject(const redisReadTask *task, int elements) { 135 | redisReply *r, *parent; 136 | 137 | r = createReplyObject(REDIS_REPLY_ARRAY); 138 | if (r == NULL) 139 | return NULL; 140 | 141 | if (elements > 0) { 142 | r->element = calloc(elements,sizeof(redisReply*)); 143 | if (r->element == NULL) { 144 | freeReplyObject(r); 145 | return NULL; 146 | } 147 | } 148 | 149 | r->elements = elements; 150 | 151 | if (task->parent) { 152 | parent = task->parent->obj; 153 | assert(parent->type == REDIS_REPLY_ARRAY); 154 | parent->element[task->idx] = r; 155 | } 156 | return r; 157 | } 158 | 159 | static void *createIntegerObject(const redisReadTask *task, long long value) { 160 | redisReply *r, *parent; 161 | 162 | r = createReplyObject(REDIS_REPLY_INTEGER); 163 | if (r == NULL) 164 | return NULL; 165 | 166 | r->integer = value; 167 | 168 | if (task->parent) { 169 | parent = task->parent->obj; 170 | assert(parent->type == REDIS_REPLY_ARRAY); 171 | parent->element[task->idx] = r; 172 | } 173 | return r; 174 | } 175 | 176 | static void *createNilObject(const redisReadTask *task) { 177 | redisReply *r, *parent; 178 | 179 | r = createReplyObject(REDIS_REPLY_NIL); 180 | if (r == NULL) 181 | return NULL; 182 | 183 | if (task->parent) { 184 | parent = task->parent->obj; 185 | assert(parent->type == REDIS_REPLY_ARRAY); 186 | parent->element[task->idx] = r; 187 | } 188 | return r; 189 | } 190 | 191 | /* Return the number of digits of 'v' when converted to string in radix 10. 192 | * Implementation borrowed from link in redis/src/util.c:string2ll(). */ 193 | static uint32_t countDigits(uint64_t v) { 194 | uint32_t result = 1; 195 | for (;;) { 196 | if (v < 10) return result; 197 | if (v < 100) return result + 1; 198 | if (v < 1000) return result + 2; 199 | if (v < 10000) return result + 3; 200 | v /= 10000U; 201 | result += 4; 202 | } 203 | } 204 | 205 | /* Helper that calculates the bulk length given a certain string length. */ 206 | static size_t bulklen(size_t len) { 207 | return 1+countDigits(len)+2+len+2; 208 | } 209 | 210 | int redisvFormatCommand(char **target, const char *format, va_list ap) { 211 | const char *c = format; 212 | char *cmd = NULL; /* final command */ 213 | int pos; /* position in final command */ 214 | sds curarg, newarg; /* current argument */ 215 | int touched = 0; /* was the current argument touched? */ 216 | char **curargv = NULL, **newargv = NULL; 217 | int argc = 0; 218 | int totlen = 0; 219 | int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */ 220 | int j; 221 | 222 | /* Abort if there is not target to set */ 223 | if (target == NULL) 224 | return -1; 225 | 226 | /* Build the command string accordingly to protocol */ 227 | curarg = sdsempty(); 228 | if (curarg == NULL) 229 | return -1; 230 | 231 | while(*c != '\0') { 232 | if (*c != '%' || c[1] == '\0') { 233 | if (*c == ' ') { 234 | if (touched) { 235 | newargv = realloc(curargv,sizeof(char*)*(argc+1)); 236 | if (newargv == NULL) goto memory_err; 237 | curargv = newargv; 238 | curargv[argc++] = curarg; 239 | totlen += bulklen(sdslen(curarg)); 240 | 241 | /* curarg is put in argv so it can be overwritten. */ 242 | curarg = sdsempty(); 243 | if (curarg == NULL) goto memory_err; 244 | touched = 0; 245 | } 246 | } else { 247 | newarg = sdscatlen(curarg,c,1); 248 | if (newarg == NULL) goto memory_err; 249 | curarg = newarg; 250 | touched = 1; 251 | } 252 | } else { 253 | char *arg; 254 | size_t size; 255 | 256 | /* Set newarg so it can be checked even if it is not touched. */ 257 | newarg = curarg; 258 | 259 | switch(c[1]) { 260 | case 's': 261 | arg = va_arg(ap,char*); 262 | size = strlen(arg); 263 | if (size > 0) 264 | newarg = sdscatlen(curarg,arg,size); 265 | break; 266 | case 'b': 267 | arg = va_arg(ap,char*); 268 | size = va_arg(ap,size_t); 269 | if (size > 0) 270 | newarg = sdscatlen(curarg,arg,size); 271 | break; 272 | case '%': 273 | newarg = sdscat(curarg,"%"); 274 | break; 275 | default: 276 | /* Try to detect printf format */ 277 | { 278 | static const char intfmts[] = "diouxX"; 279 | static const char flags[] = "#0-+ "; 280 | char _format[16]; 281 | const char *_p = c+1; 282 | size_t _l = 0; 283 | va_list _cpy; 284 | 285 | /* Flags */ 286 | while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++; 287 | 288 | /* Field width */ 289 | while (*_p != '\0' && isdigit(*_p)) _p++; 290 | 291 | /* Precision */ 292 | if (*_p == '.') { 293 | _p++; 294 | while (*_p != '\0' && isdigit(*_p)) _p++; 295 | } 296 | 297 | /* Copy va_list before consuming with va_arg */ 298 | va_copy(_cpy,ap); 299 | 300 | /* Integer conversion (without modifiers) */ 301 | if (strchr(intfmts,*_p) != NULL) { 302 | va_arg(ap,int); 303 | goto fmt_valid; 304 | } 305 | 306 | /* Double conversion (without modifiers) */ 307 | if (strchr("eEfFgGaA",*_p) != NULL) { 308 | va_arg(ap,double); 309 | goto fmt_valid; 310 | } 311 | 312 | /* Size: char */ 313 | if (_p[0] == 'h' && _p[1] == 'h') { 314 | _p += 2; 315 | if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { 316 | va_arg(ap,int); /* char gets promoted to int */ 317 | goto fmt_valid; 318 | } 319 | goto fmt_invalid; 320 | } 321 | 322 | /* Size: short */ 323 | if (_p[0] == 'h') { 324 | _p += 1; 325 | if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { 326 | va_arg(ap,int); /* short gets promoted to int */ 327 | goto fmt_valid; 328 | } 329 | goto fmt_invalid; 330 | } 331 | 332 | /* Size: long long */ 333 | if (_p[0] == 'l' && _p[1] == 'l') { 334 | _p += 2; 335 | if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { 336 | va_arg(ap,long long); 337 | goto fmt_valid; 338 | } 339 | goto fmt_invalid; 340 | } 341 | 342 | /* Size: long */ 343 | if (_p[0] == 'l') { 344 | _p += 1; 345 | if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { 346 | va_arg(ap,long); 347 | goto fmt_valid; 348 | } 349 | goto fmt_invalid; 350 | } 351 | 352 | fmt_invalid: 353 | va_end(_cpy); 354 | goto format_err; 355 | 356 | fmt_valid: 357 | _l = (_p+1)-c; 358 | if (_l < sizeof(_format)-2) { 359 | memcpy(_format,c,_l); 360 | _format[_l] = '\0'; 361 | newarg = sdscatvprintf(curarg,_format,_cpy); 362 | 363 | /* Update current position (note: outer blocks 364 | * increment c twice so compensate here) */ 365 | c = _p-1; 366 | } 367 | 368 | va_end(_cpy); 369 | break; 370 | } 371 | } 372 | 373 | if (newarg == NULL) goto memory_err; 374 | curarg = newarg; 375 | 376 | touched = 1; 377 | c++; 378 | } 379 | c++; 380 | } 381 | 382 | /* Add the last argument if needed */ 383 | if (touched) { 384 | newargv = realloc(curargv,sizeof(char*)*(argc+1)); 385 | if (newargv == NULL) goto memory_err; 386 | curargv = newargv; 387 | curargv[argc++] = curarg; 388 | totlen += bulklen(sdslen(curarg)); 389 | } else { 390 | sdsfree(curarg); 391 | } 392 | 393 | /* Clear curarg because it was put in curargv or was free'd. */ 394 | curarg = NULL; 395 | 396 | /* Add bytes needed to hold multi bulk count */ 397 | totlen += 1+countDigits(argc)+2; 398 | 399 | /* Build the command at protocol level */ 400 | cmd = malloc(totlen+1); 401 | if (cmd == NULL) goto memory_err; 402 | 403 | pos = sprintf(cmd,"*%d\r\n",argc); 404 | for (j = 0; j < argc; j++) { 405 | pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j])); 406 | memcpy(cmd+pos,curargv[j],sdslen(curargv[j])); 407 | pos += sdslen(curargv[j]); 408 | sdsfree(curargv[j]); 409 | cmd[pos++] = '\r'; 410 | cmd[pos++] = '\n'; 411 | } 412 | assert(pos == totlen); 413 | cmd[pos] = '\0'; 414 | 415 | free(curargv); 416 | *target = cmd; 417 | return totlen; 418 | 419 | format_err: 420 | error_type = -2; 421 | goto cleanup; 422 | 423 | memory_err: 424 | error_type = -1; 425 | goto cleanup; 426 | 427 | cleanup: 428 | if (curargv) { 429 | while(argc--) 430 | sdsfree(curargv[argc]); 431 | free(curargv); 432 | } 433 | 434 | sdsfree(curarg); 435 | 436 | /* No need to check cmd since it is the last statement that can fail, 437 | * but do it anyway to be as defensive as possible. */ 438 | if (cmd != NULL) 439 | free(cmd); 440 | 441 | return error_type; 442 | } 443 | 444 | /* Format a command according to the Redis protocol. This function 445 | * takes a format similar to printf: 446 | * 447 | * %s represents a C null terminated string you want to interpolate 448 | * %b represents a binary safe string 449 | * 450 | * When using %b you need to provide both the pointer to the string 451 | * and the length in bytes as a size_t. Examples: 452 | * 453 | * len = redisFormatCommand(target, "GET %s", mykey); 454 | * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen); 455 | */ 456 | int redisFormatCommand(char **target, const char *format, ...) { 457 | va_list ap; 458 | int len; 459 | va_start(ap,format); 460 | len = redisvFormatCommand(target,format,ap); 461 | va_end(ap); 462 | 463 | /* The API says "-1" means bad result, but we now also return "-2" in some 464 | * cases. Force the return value to always be -1. */ 465 | if (len < 0) 466 | len = -1; 467 | 468 | return len; 469 | } 470 | 471 | /* Format a command according to the Redis protocol using an sds string and 472 | * sdscatfmt for the processing of arguments. This function takes the 473 | * number of arguments, an array with arguments and an array with their 474 | * lengths. If the latter is set to NULL, strlen will be used to compute the 475 | * argument lengths. 476 | */ 477 | int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv, 478 | const size_t *argvlen) 479 | { 480 | sds cmd; 481 | unsigned long long totlen; 482 | int j; 483 | size_t len; 484 | 485 | /* Abort on a NULL target */ 486 | if (target == NULL) 487 | return -1; 488 | 489 | /* Calculate our total size */ 490 | totlen = 1+countDigits(argc)+2; 491 | for (j = 0; j < argc; j++) { 492 | len = argvlen ? argvlen[j] : strlen(argv[j]); 493 | totlen += bulklen(len); 494 | } 495 | 496 | /* Use an SDS string for command construction */ 497 | cmd = sdsempty(); 498 | if (cmd == NULL) 499 | return -1; 500 | 501 | /* We already know how much storage we need */ 502 | cmd = sdsMakeRoomFor(cmd, totlen); 503 | if (cmd == NULL) 504 | return -1; 505 | 506 | /* Construct command */ 507 | cmd = sdscatfmt(cmd, "*%i\r\n", argc); 508 | for (j=0; j < argc; j++) { 509 | len = argvlen ? argvlen[j] : strlen(argv[j]); 510 | cmd = sdscatfmt(cmd, "$%u\r\n", len); 511 | cmd = sdscatlen(cmd, argv[j], len); 512 | cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1); 513 | } 514 | 515 | assert(sdslen(cmd)==totlen); 516 | 517 | *target = cmd; 518 | return totlen; 519 | } 520 | 521 | void redisFreeSdsCommand(sds cmd) { 522 | sdsfree(cmd); 523 | } 524 | 525 | /* Format a command according to the Redis protocol. This function takes the 526 | * number of arguments, an array with arguments and an array with their 527 | * lengths. If the latter is set to NULL, strlen will be used to compute the 528 | * argument lengths. 529 | */ 530 | int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) { 531 | char *cmd = NULL; /* final command */ 532 | int pos; /* position in final command */ 533 | size_t len; 534 | int totlen, j; 535 | 536 | /* Abort on a NULL target */ 537 | if (target == NULL) 538 | return -1; 539 | 540 | /* Calculate number of bytes needed for the command */ 541 | totlen = 1+countDigits(argc)+2; 542 | for (j = 0; j < argc; j++) { 543 | len = argvlen ? argvlen[j] : strlen(argv[j]); 544 | totlen += bulklen(len); 545 | } 546 | 547 | /* Build the command at protocol level */ 548 | cmd = malloc(totlen+1); 549 | if (cmd == NULL) 550 | return -1; 551 | 552 | pos = sprintf(cmd,"*%d\r\n",argc); 553 | for (j = 0; j < argc; j++) { 554 | len = argvlen ? argvlen[j] : strlen(argv[j]); 555 | pos += sprintf(cmd+pos,"$%zu\r\n",len); 556 | memcpy(cmd+pos,argv[j],len); 557 | pos += len; 558 | cmd[pos++] = '\r'; 559 | cmd[pos++] = '\n'; 560 | } 561 | assert(pos == totlen); 562 | cmd[pos] = '\0'; 563 | 564 | *target = cmd; 565 | return totlen; 566 | } 567 | 568 | void redisFreeCommand(char *cmd) { 569 | free(cmd); 570 | } 571 | 572 | void __redisSetError(redisContext *c, int type, const char *str) { 573 | size_t len; 574 | 575 | c->err = type; 576 | if (str != NULL) { 577 | len = strlen(str); 578 | len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1); 579 | memcpy(c->errstr,str,len); 580 | c->errstr[len] = '\0'; 581 | } else { 582 | /* Only REDIS_ERR_IO may lack a description! */ 583 | assert(type == REDIS_ERR_IO); 584 | __redis_strerror_r(errno, c->errstr, sizeof(c->errstr)); 585 | } 586 | } 587 | 588 | redisReader *redisReaderCreate(void) { 589 | return redisReaderCreateWithFunctions(&defaultFunctions); 590 | } 591 | 592 | static redisContext *redisContextInit(void) { 593 | redisContext *c; 594 | 595 | c = calloc(1,sizeof(redisContext)); 596 | if (c == NULL) 597 | return NULL; 598 | 599 | c->err = 0; 600 | c->errstr[0] = '\0'; 601 | c->obuf = sdsempty(); 602 | c->reader = redisReaderCreate(); 603 | c->tcp.host = NULL; 604 | c->tcp.source_addr = NULL; 605 | c->unix_sock.path = NULL; 606 | c->timeout = NULL; 607 | 608 | if (c->obuf == NULL || c->reader == NULL) { 609 | redisFree(c); 610 | return NULL; 611 | } 612 | 613 | return c; 614 | } 615 | 616 | void redisFree(redisContext *c) { 617 | if (c == NULL) 618 | return; 619 | if (c->fd > 0) 620 | close(c->fd); 621 | if (c->obuf != NULL) 622 | sdsfree(c->obuf); 623 | if (c->reader != NULL) 624 | redisReaderFree(c->reader); 625 | if (c->tcp.host) 626 | free(c->tcp.host); 627 | if (c->tcp.source_addr) 628 | free(c->tcp.source_addr); 629 | if (c->unix_sock.path) 630 | free(c->unix_sock.path); 631 | if (c->timeout) 632 | free(c->timeout); 633 | free(c); 634 | } 635 | 636 | int redisFreeKeepFd(redisContext *c) { 637 | int fd = c->fd; 638 | c->fd = -1; 639 | redisFree(c); 640 | return fd; 641 | } 642 | 643 | int redisReconnect(redisContext *c) { 644 | c->err = 0; 645 | memset(c->errstr, '\0', strlen(c->errstr)); 646 | 647 | if (c->fd > 0) { 648 | close(c->fd); 649 | } 650 | 651 | sdsfree(c->obuf); 652 | redisReaderFree(c->reader); 653 | 654 | c->obuf = sdsempty(); 655 | c->reader = redisReaderCreate(); 656 | 657 | if (c->connection_type == REDIS_CONN_TCP) { 658 | return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, 659 | c->timeout, c->tcp.source_addr); 660 | } else if (c->connection_type == REDIS_CONN_UNIX) { 661 | return redisContextConnectUnix(c, c->unix_sock.path, c->timeout); 662 | } else { 663 | /* Something bad happened here and shouldn't have. There isn't 664 | enough information in the context to reconnect. */ 665 | __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect"); 666 | } 667 | 668 | return REDIS_ERR; 669 | } 670 | 671 | /* Connect to a Redis instance. On error the field error in the returned 672 | * context will be set to the return value of the error function. 673 | * When no set of reply functions is given, the default set will be used. */ 674 | redisContext *redisConnect(const char *ip, int port) { 675 | redisContext *c; 676 | 677 | c = redisContextInit(); 678 | if (c == NULL) 679 | return NULL; 680 | 681 | c->flags |= REDIS_BLOCK; 682 | redisContextConnectTcp(c,ip,port,NULL); 683 | return c; 684 | } 685 | 686 | redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { 687 | redisContext *c; 688 | 689 | c = redisContextInit(); 690 | if (c == NULL) 691 | return NULL; 692 | 693 | c->flags |= REDIS_BLOCK; 694 | redisContextConnectTcp(c,ip,port,&tv); 695 | return c; 696 | } 697 | 698 | redisContext *redisConnectNonBlock(const char *ip, int port) { 699 | redisContext *c; 700 | 701 | c = redisContextInit(); 702 | if (c == NULL) 703 | return NULL; 704 | 705 | c->flags &= ~REDIS_BLOCK; 706 | redisContextConnectTcp(c,ip,port,NULL); 707 | return c; 708 | } 709 | 710 | redisContext *redisConnectBindNonBlock(const char *ip, int port, 711 | const char *source_addr) { 712 | redisContext *c = redisContextInit(); 713 | c->flags &= ~REDIS_BLOCK; 714 | redisContextConnectBindTcp(c,ip,port,NULL,source_addr); 715 | return c; 716 | } 717 | 718 | redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, 719 | const char *source_addr) { 720 | redisContext *c = redisContextInit(); 721 | c->flags &= ~REDIS_BLOCK; 722 | c->flags |= REDIS_REUSEADDR; 723 | redisContextConnectBindTcp(c,ip,port,NULL,source_addr); 724 | return c; 725 | } 726 | 727 | redisContext *redisConnectUnix(const char *path) { 728 | redisContext *c; 729 | 730 | c = redisContextInit(); 731 | if (c == NULL) 732 | return NULL; 733 | 734 | c->flags |= REDIS_BLOCK; 735 | redisContextConnectUnix(c,path,NULL); 736 | return c; 737 | } 738 | 739 | redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { 740 | redisContext *c; 741 | 742 | c = redisContextInit(); 743 | if (c == NULL) 744 | return NULL; 745 | 746 | c->flags |= REDIS_BLOCK; 747 | redisContextConnectUnix(c,path,&tv); 748 | return c; 749 | } 750 | 751 | redisContext *redisConnectUnixNonBlock(const char *path) { 752 | redisContext *c; 753 | 754 | c = redisContextInit(); 755 | if (c == NULL) 756 | return NULL; 757 | 758 | c->flags &= ~REDIS_BLOCK; 759 | redisContextConnectUnix(c,path,NULL); 760 | return c; 761 | } 762 | 763 | redisContext *redisConnectFd(int fd) { 764 | redisContext *c; 765 | 766 | c = redisContextInit(); 767 | if (c == NULL) 768 | return NULL; 769 | 770 | c->fd = fd; 771 | c->flags |= REDIS_BLOCK | REDIS_CONNECTED; 772 | return c; 773 | } 774 | 775 | /* Set read/write timeout on a blocking socket. */ 776 | int redisSetTimeout(redisContext *c, const struct timeval tv) { 777 | if (c->flags & REDIS_BLOCK) 778 | return redisContextSetTimeout(c,tv); 779 | return REDIS_ERR; 780 | } 781 | 782 | /* Enable connection KeepAlive. */ 783 | int redisEnableKeepAlive(redisContext *c) { 784 | if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK) 785 | return REDIS_ERR; 786 | return REDIS_OK; 787 | } 788 | 789 | /* Use this function to handle a read event on the descriptor. It will try 790 | * and read some bytes from the socket and feed them to the reply parser. 791 | * 792 | * After this function is called, you may use redisContextReadReply to 793 | * see if there is a reply available. */ 794 | int redisBufferRead(redisContext *c) { 795 | char buf[1024*16]; 796 | int nread; 797 | 798 | /* Return early when the context has seen an error. */ 799 | if (c->err) 800 | return REDIS_ERR; 801 | 802 | nread = read(c->fd,buf,sizeof(buf)); 803 | if (nread == -1) { 804 | if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { 805 | /* Try again later */ 806 | } else { 807 | __redisSetError(c,REDIS_ERR_IO,NULL); 808 | return REDIS_ERR; 809 | } 810 | } else if (nread == 0) { 811 | __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); 812 | return REDIS_ERR; 813 | } else { 814 | if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { 815 | __redisSetError(c,c->reader->err,c->reader->errstr); 816 | return REDIS_ERR; 817 | } 818 | } 819 | return REDIS_OK; 820 | } 821 | 822 | /* Write the output buffer to the socket. 823 | * 824 | * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was 825 | * successfully written to the socket. When the buffer is empty after the 826 | * write operation, "done" is set to 1 (if given). 827 | * 828 | * Returns REDIS_ERR if an error occurred trying to write and sets 829 | * c->errstr to hold the appropriate error string. 830 | */ 831 | int redisBufferWrite(redisContext *c, int *done) { 832 | int nwritten; 833 | 834 | /* Return early when the context has seen an error. */ 835 | if (c->err) 836 | return REDIS_ERR; 837 | 838 | if (sdslen(c->obuf) > 0) { 839 | nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); 840 | if (nwritten == -1) { 841 | if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { 842 | /* Try again later */ 843 | } else { 844 | __redisSetError(c,REDIS_ERR_IO,NULL); 845 | return REDIS_ERR; 846 | } 847 | } else if (nwritten > 0) { 848 | if (nwritten == (signed)sdslen(c->obuf)) { 849 | sdsfree(c->obuf); 850 | c->obuf = sdsempty(); 851 | } else { 852 | sdsrange(c->obuf,nwritten,-1); 853 | } 854 | } 855 | } 856 | if (done != NULL) *done = (sdslen(c->obuf) == 0); 857 | return REDIS_OK; 858 | } 859 | 860 | /* Internal helper function to try and get a reply from the reader, 861 | * or set an error in the context otherwise. */ 862 | int redisGetReplyFromReader(redisContext *c, void **reply) { 863 | if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { 864 | __redisSetError(c,c->reader->err,c->reader->errstr); 865 | return REDIS_ERR; 866 | } 867 | return REDIS_OK; 868 | } 869 | 870 | int redisGetReply(redisContext *c, void **reply) { 871 | int wdone = 0; 872 | void *aux = NULL; 873 | 874 | /* Try to read pending replies */ 875 | if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) 876 | return REDIS_ERR; 877 | 878 | /* For the blocking context, flush output buffer and read reply */ 879 | if (aux == NULL && c->flags & REDIS_BLOCK) { 880 | /* Write until done */ 881 | do { 882 | if (redisBufferWrite(c,&wdone) == REDIS_ERR) 883 | return REDIS_ERR; 884 | } while (!wdone); 885 | 886 | /* Read until there is a reply */ 887 | do { 888 | if (redisBufferRead(c) == REDIS_ERR) 889 | return REDIS_ERR; 890 | if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) 891 | return REDIS_ERR; 892 | } while (aux == NULL); 893 | } 894 | 895 | /* Set reply object */ 896 | if (reply != NULL) *reply = aux; 897 | return REDIS_OK; 898 | } 899 | 900 | 901 | /* Helper function for the redisAppendCommand* family of functions. 902 | * 903 | * Write a formatted command to the output buffer. When this family 904 | * is used, you need to call redisGetReply yourself to retrieve 905 | * the reply (or replies in pub/sub). 906 | */ 907 | int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) { 908 | sds newbuf; 909 | 910 | newbuf = sdscatlen(c->obuf,cmd,len); 911 | if (newbuf == NULL) { 912 | __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); 913 | return REDIS_ERR; 914 | } 915 | 916 | c->obuf = newbuf; 917 | return REDIS_OK; 918 | } 919 | 920 | int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) { 921 | 922 | if (__redisAppendCommand(c, cmd, len) != REDIS_OK) { 923 | return REDIS_ERR; 924 | } 925 | 926 | return REDIS_OK; 927 | } 928 | 929 | int redisvAppendCommand(redisContext *c, const char *format, va_list ap) { 930 | char *cmd; 931 | int len; 932 | 933 | len = redisvFormatCommand(&cmd,format,ap); 934 | if (len == -1) { 935 | __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); 936 | return REDIS_ERR; 937 | } else if (len == -2) { 938 | __redisSetError(c,REDIS_ERR_OTHER,"Invalid format string"); 939 | return REDIS_ERR; 940 | } 941 | 942 | if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { 943 | free(cmd); 944 | return REDIS_ERR; 945 | } 946 | 947 | free(cmd); 948 | return REDIS_OK; 949 | } 950 | 951 | int redisAppendCommand(redisContext *c, const char *format, ...) { 952 | va_list ap; 953 | int ret; 954 | 955 | va_start(ap,format); 956 | ret = redisvAppendCommand(c,format,ap); 957 | va_end(ap); 958 | return ret; 959 | } 960 | 961 | int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { 962 | sds cmd; 963 | int len; 964 | 965 | len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); 966 | if (len == -1) { 967 | __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); 968 | return REDIS_ERR; 969 | } 970 | 971 | if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { 972 | sdsfree(cmd); 973 | return REDIS_ERR; 974 | } 975 | 976 | sdsfree(cmd); 977 | return REDIS_OK; 978 | } 979 | 980 | /* Helper function for the redisCommand* family of functions. 981 | * 982 | * Write a formatted command to the output buffer. If the given context is 983 | * blocking, immediately read the reply into the "reply" pointer. When the 984 | * context is non-blocking, the "reply" pointer will not be used and the 985 | * command is simply appended to the write buffer. 986 | * 987 | * Returns the reply when a reply was successfully retrieved. Returns NULL 988 | * otherwise. When NULL is returned in a blocking context, the error field 989 | * in the context will be set. 990 | */ 991 | static void *__redisBlockForReply(redisContext *c) { 992 | void *reply; 993 | 994 | if (c->flags & REDIS_BLOCK) { 995 | if (redisGetReply(c,&reply) != REDIS_OK) 996 | return NULL; 997 | return reply; 998 | } 999 | return NULL; 1000 | } 1001 | 1002 | void *redisvCommand(redisContext *c, const char *format, va_list ap) { 1003 | if (redisvAppendCommand(c,format,ap) != REDIS_OK) 1004 | return NULL; 1005 | return __redisBlockForReply(c); 1006 | } 1007 | 1008 | void *redisCommand(redisContext *c, const char *format, ...) { 1009 | va_list ap; 1010 | void *reply = NULL; 1011 | va_start(ap,format); 1012 | reply = redisvCommand(c,format,ap); 1013 | va_end(ap); 1014 | return reply; 1015 | } 1016 | 1017 | void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { 1018 | if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK) 1019 | return NULL; 1020 | return __redisBlockForReply(c); 1021 | } 1022 | -------------------------------------------------------------------------------- /hiredis/hiredis.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2011, Salvatore Sanfilippo 3 | * Copyright (c) 2010-2014, Pieter Noordhuis 4 | * Copyright (c) 2015, Matt Stancliff , 5 | * Jan-Erik Rediger 6 | * 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * * Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * * Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * * Neither the name of Redis nor the names of its contributors may be used 18 | * to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef __HIREDIS_H 35 | #define __HIREDIS_H 36 | #include "read.h" 37 | #include /* for va_list */ 38 | #include /* for struct timeval */ 39 | #include /* uintXX_t, etc */ 40 | #include "sds.h" /* for sds */ 41 | 42 | #define HIREDIS_MAJOR 0 43 | #define HIREDIS_MINOR 13 44 | #define HIREDIS_PATCH 3 45 | #define HIREDIS_SONAME 0.13 46 | 47 | /* Connection type can be blocking or non-blocking and is set in the 48 | * least significant bit of the flags field in redisContext. */ 49 | #define REDIS_BLOCK 0x1 50 | 51 | /* Connection may be disconnected before being free'd. The second bit 52 | * in the flags field is set when the context is connected. */ 53 | #define REDIS_CONNECTED 0x2 54 | 55 | /* The async API might try to disconnect cleanly and flush the output 56 | * buffer and read all subsequent replies before disconnecting. 57 | * This flag means no new commands can come in and the connection 58 | * should be terminated once all replies have been read. */ 59 | #define REDIS_DISCONNECTING 0x4 60 | 61 | /* Flag specific to the async API which means that the context should be clean 62 | * up as soon as possible. */ 63 | #define REDIS_FREEING 0x8 64 | 65 | /* Flag that is set when an async callback is executed. */ 66 | #define REDIS_IN_CALLBACK 0x10 67 | 68 | /* Flag that is set when the async context has one or more subscriptions. */ 69 | #define REDIS_SUBSCRIBED 0x20 70 | 71 | /* Flag that is set when monitor mode is active */ 72 | #define REDIS_MONITORING 0x40 73 | 74 | /* Flag that is set when we should set SO_REUSEADDR before calling bind() */ 75 | #define REDIS_REUSEADDR 0x80 76 | 77 | #define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ 78 | 79 | /* number of times we retry to connect in the case of EADDRNOTAVAIL and 80 | * SO_REUSEADDR is being used. */ 81 | #define REDIS_CONNECT_RETRIES 10 82 | 83 | /* strerror_r has two completely different prototypes and behaviors 84 | * depending on system issues, so we need to operate on the error buffer 85 | * differently depending on which strerror_r we're using. */ 86 | #ifndef _GNU_SOURCE 87 | /* "regular" POSIX strerror_r that does the right thing. */ 88 | #define __redis_strerror_r(errno, buf, len) \ 89 | do { \ 90 | strerror_r((errno), (buf), (len)); \ 91 | } while (0) 92 | #else 93 | /* "bad" GNU strerror_r we need to clean up after. */ 94 | #define __redis_strerror_r(errno, buf, len) \ 95 | do { \ 96 | char *err_str = strerror_r((errno), (buf), (len)); \ 97 | /* If return value _isn't_ the start of the buffer we passed in, \ 98 | * then GNU strerror_r returned an internal static buffer and we \ 99 | * need to copy the result into our private buffer. */ \ 100 | if (err_str != (buf)) { \ 101 | strncpy((buf), err_str, ((len) - 1)); \ 102 | buf[(len)-1] = '\0'; \ 103 | } \ 104 | } while (0) 105 | #endif 106 | 107 | #ifdef __cplusplus 108 | extern "C" { 109 | #endif 110 | 111 | /* This is the reply object returned by redisCommand() */ 112 | typedef struct redisReply { 113 | int type; /* REDIS_REPLY_* */ 114 | long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ 115 | size_t len; /* Length of string */ 116 | char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ 117 | size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ 118 | struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ 119 | } redisReply; 120 | 121 | redisReader *redisReaderCreate(void); 122 | 123 | /* Function to free the reply objects hiredis returns by default. */ 124 | void freeReplyObject(void *reply); 125 | 126 | /* Functions to format a command according to the protocol. */ 127 | int redisvFormatCommand(char **target, const char *format, va_list ap); 128 | int redisFormatCommand(char **target, const char *format, ...); 129 | int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); 130 | int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen); 131 | void redisFreeCommand(char *cmd); 132 | void redisFreeSdsCommand(sds cmd); 133 | 134 | enum redisConnectionType { 135 | REDIS_CONN_TCP, 136 | REDIS_CONN_UNIX 137 | }; 138 | 139 | /* Context for a connection to Redis */ 140 | typedef struct redisContext { 141 | int err; /* Error flags, 0 when there is no error */ 142 | char errstr[128]; /* String representation of error when applicable */ 143 | int fd; 144 | int flags; 145 | char *obuf; /* Write buffer */ 146 | redisReader *reader; /* Protocol reader */ 147 | 148 | enum redisConnectionType connection_type; 149 | struct timeval *timeout; 150 | 151 | struct { 152 | char *host; 153 | char *source_addr; 154 | int port; 155 | } tcp; 156 | 157 | struct { 158 | char *path; 159 | } unix_sock; 160 | 161 | } redisContext; 162 | 163 | redisContext *redisConnect(const char *ip, int port); 164 | redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); 165 | redisContext *redisConnectNonBlock(const char *ip, int port); 166 | redisContext *redisConnectBindNonBlock(const char *ip, int port, 167 | const char *source_addr); 168 | redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, 169 | const char *source_addr); 170 | redisContext *redisConnectUnix(const char *path); 171 | redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); 172 | redisContext *redisConnectUnixNonBlock(const char *path); 173 | redisContext *redisConnectFd(int fd); 174 | 175 | /** 176 | * Reconnect the given context using the saved information. 177 | * 178 | * This re-uses the exact same connect options as in the initial connection. 179 | * host, ip (or path), timeout and bind address are reused, 180 | * flags are used unmodified from the existing context. 181 | * 182 | * Returns REDIS_OK on successful connect or REDIS_ERR otherwise. 183 | */ 184 | int redisReconnect(redisContext *c); 185 | 186 | int redisSetTimeout(redisContext *c, const struct timeval tv); 187 | int redisEnableKeepAlive(redisContext *c); 188 | void redisFree(redisContext *c); 189 | int redisFreeKeepFd(redisContext *c); 190 | int redisBufferRead(redisContext *c); 191 | int redisBufferWrite(redisContext *c, int *done); 192 | 193 | /* In a blocking context, this function first checks if there are unconsumed 194 | * replies to return and returns one if so. Otherwise, it flushes the output 195 | * buffer to the socket and reads until it has a reply. In a non-blocking 196 | * context, it will return unconsumed replies until there are no more. */ 197 | int redisGetReply(redisContext *c, void **reply); 198 | int redisGetReplyFromReader(redisContext *c, void **reply); 199 | 200 | /* Write a formatted command to the output buffer. Use these functions in blocking mode 201 | * to get a pipeline of commands. */ 202 | int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len); 203 | 204 | /* Write a command to the output buffer. Use these functions in blocking mode 205 | * to get a pipeline of commands. */ 206 | int redisvAppendCommand(redisContext *c, const char *format, va_list ap); 207 | int redisAppendCommand(redisContext *c, const char *format, ...); 208 | int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); 209 | 210 | /* Issue a command to Redis. In a blocking context, it is identical to calling 211 | * redisAppendCommand, followed by redisGetReply. The function will return 212 | * NULL if there was an error in performing the request, otherwise it will 213 | * return the reply. In a non-blocking context, it is identical to calling 214 | * only redisAppendCommand and will always return NULL. */ 215 | void *redisvCommand(redisContext *c, const char *format, va_list ap); 216 | void *redisCommand(redisContext *c, const char *format, ...); 217 | void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); 218 | 219 | #ifdef __cplusplus 220 | } 221 | #endif 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /http/README.md: -------------------------------------------------------------------------------- 1 | 2 | http连接处理类 3 | =============== 4 | 根据状态转移,通过主从状态机封装了http连接类。其中,主状态机在内部调用从状态机,从状态机将处理状态和数据传给主状态机 5 | > * 客户端发出http连接请求 6 | > * 从状态机读取数据,更新自身状态和接收数据,传给主状态机 7 | > * 主状态机根据从状态机状态,更新自身状态,决定响应请求还是继续读取 -------------------------------------------------------------------------------- /http/http_conn.cpp: -------------------------------------------------------------------------------- 1 | #include "http_conn.h" 2 | #include "../webserver.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //定义http响应的一些状态信息 11 | const char *ok_200_title = "OK"; 12 | const char *error_400_title = "Bad Request"; 13 | const char *error_400_form = "Your request has bad syntax or is inherently impossible to staisfy.\n"; 14 | const char *error_403_title = "Forbidden"; 15 | const char *error_403_form = "You do not have permission to get file form this server.\n"; 16 | const char *error_404_title = "Not Found"; 17 | const char *error_404_form = "The requested file was not found on this server.\n"; 18 | const char *error_500_title = "Internal Error"; 19 | const char *error_500_form = "There was an unusual problem serving the request file.\n"; 20 | 21 | locker m_lock; 22 | map users; 23 | int m_close_log; 24 | 25 | void http_conn::initmysql_result(connection_pool *connPool) 26 | { 27 | //先从连接池中取一个连接 28 | MYSQL *mysql = NULL; 29 | connectionRAII mysqlcon(&mysql, connPool); 30 | 31 | //在user表中检索username,passwd数据,浏览器端输入 32 | if (mysql_query(mysql, "SELECT username,passwd FROM user")) 33 | { 34 | LOG_ERROR("SELECT error:%s\n", mysql_error(mysql)); 35 | } 36 | 37 | //从表中检索完整的结果集 38 | MYSQL_RES *result = mysql_store_result(mysql); 39 | 40 | //返回结果集中的列数 41 | int num_fields = mysql_num_fields(result); 42 | 43 | //返回所有字段结构的数组 44 | MYSQL_FIELD *fields = mysql_fetch_fields(result); 45 | 46 | //从结果集中获取下一行,将对应的用户名和密码,存入map中 47 | while (MYSQL_ROW row = mysql_fetch_row(result)) 48 | { 49 | string temp1(row[0]); 50 | string temp2(row[1]); 51 | users[temp1] = temp2; 52 | } 53 | } 54 | 55 | void http_conn::initRedis_result(RedisConnectionPool* ConnPool, int log_ctrl) 56 | { 57 | m_close_log = log_ctrl; 58 | redis = NULL; 59 | RedisConnectionRAII rediscon(&redis, ConnPool); 60 | if(redis == NULL) 61 | { 62 | LOG_ERROR("initRedis_result failed"); 63 | } 64 | localRedisConn = redis->m_pContext; 65 | if(localRedisConn == NULL) 66 | { 67 | LOG_ERROR("local redis session lose"); 68 | } 69 | else 70 | { 71 | LOG_ERROR("local redis session INFO: err:%lu,fd:%lu,flag:%lu", localRedisConn->err, localRedisConn->fd, localRedisConn->flags); 72 | 73 | } 74 | 75 | redisReply *ResLen = (redisReply*)redisCommand(redis->m_pContext, "LLEN users_list"); 76 | 77 | for(int i=0;iinteger;i++) 78 | { 79 | redisReply *Res = (redisReply*)redisCommand(redis->m_pContext, "LINDEX users_list %d", i+1); 80 | vectortemp; 81 | string x; 82 | stringstream ss; 83 | ss<str; 84 | while(getline(ss,x,'+')) 85 | { 86 | temp.push_back(x); 87 | } 88 | if(temp.size()>1) 89 | { 90 | users[temp[0]] = temp[1]; 91 | } 92 | else 93 | { 94 | LOG_ERROR("user info in redis invalid"); 95 | } 96 | } 97 | 98 | //全局Tokens存入Redis,并设置过期时间 99 | time_t cur = time(NULL); 100 | string str_time_cur=to_string(cur); 101 | redisReply *ExistToken = (redisReply*)redisCommand(redis->m_pContext, "EXISTS Token_pictrue"); 102 | redisReply *DelTokensRes = NULL; 103 | if(0 == ExistToken->integer) 104 | { 105 | m_Token_picture="xxxpicture"+str_time_cur; 106 | redisReply *SetToken_picture = (redisReply*)redisCommand(redis->m_pContext, "SET Token_pictrue %s", m_Token_picture.c_str()); 107 | if(SetToken_picture->str == "OK") 108 | { 109 | redisCommand(redis->m_pContext, "EXPIRE Token_pictrue 60"); 110 | } 111 | } 112 | else 113 | { 114 | DelTokensRes = (redisReply*)redisCommand(redis->m_pContext, "DEL Token_pictrue"); 115 | if(1 == DelTokensRes->integer) 116 | { 117 | LOG_ERROR("Token_pictrue clear success"); 118 | } 119 | else 120 | { 121 | LOG_ERROR("Token_pictrue clear fail"); 122 | } 123 | } 124 | ExistToken = (redisReply*)redisCommand(redis->m_pContext, "EXISTS Token_video"); 125 | if(0 == ExistToken->integer) 126 | { 127 | m_Token_video="xxxvideo"+str_time_cur; 128 | redisReply *SetToken_video = (redisReply*)redisCommand(redis->m_pContext, "SET Token_video %s", m_Token_video.c_str()); 129 | if(SetToken_video->str == "OK") 130 | { 131 | redisCommand(redis->m_pContext, "EXPIRE Token_video 60"); 132 | } 133 | } 134 | else 135 | { 136 | DelTokensRes = (redisReply*)redisCommand(redis->m_pContext, "DEL Token_video"); 137 | if(1 == DelTokensRes->integer) 138 | { 139 | LOG_ERROR("Token_video clear success"); 140 | } 141 | else 142 | { 143 | LOG_ERROR("Token_video clear fail"); 144 | } 145 | } 146 | ExistToken = (redisReply*)redisCommand(redis->m_pContext, "EXISTS Token_fans"); 147 | if(0 == ExistToken->integer) 148 | { 149 | m_Token_fans="xxxfans"+str_time_cur; 150 | redisReply *SetToken_fans = (redisReply*)redisCommand(redis->m_pContext, "SET Token_fans %s", m_Token_video.c_str()); 151 | if(SetToken_fans->str == "OK") 152 | { 153 | redisCommand(redis->m_pContext, "EXPIRE Token_fans 60"); 154 | } 155 | } 156 | else 157 | { 158 | DelTokensRes = (redisReply*)redisCommand(redis->m_pContext, "DEL Token_video"); 159 | if(1 == DelTokensRes->integer) 160 | { 161 | LOG_ERROR("Token_fans clear success"); 162 | }else 163 | { 164 | LOG_ERROR("Token_fans clear fail"); 165 | } 166 | } 167 | } 168 | 169 | //对文件描述符设置非阻塞 170 | int setnonblocking(int fd) 171 | { 172 | int old_option = fcntl(fd, F_GETFL); 173 | int new_option = old_option | O_NONBLOCK; 174 | fcntl(fd, F_SETFL, new_option); 175 | return old_option; 176 | } 177 | 178 | //将内核事件表注册读事件,ET模式,选择开启EPOLLONESHOT 179 | void addfd(int epollfd, int fd, bool one_shot, int TRIGMode) 180 | { 181 | epoll_event event; 182 | event.data.fd = fd; 183 | 184 | if (1 == TRIGMode) 185 | event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; 186 | else 187 | event.events = EPOLLIN | EPOLLRDHUP; 188 | 189 | if (one_shot) 190 | event.events |= EPOLLONESHOT; 191 | epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event); 192 | setnonblocking(fd); 193 | } 194 | 195 | //从内核时间表删除描述符 196 | void removefd(int epollfd, int fd) 197 | { 198 | epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, 0); 199 | close(fd); 200 | } 201 | 202 | //将事件重置为EPOLLONESHOT 203 | void modfd(int epollfd, int fd, int ev, int TRIGMode) 204 | { 205 | epoll_event event; 206 | event.data.fd = fd; 207 | 208 | if (1 == TRIGMode) 209 | event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP; 210 | else 211 | event.events = ev | EPOLLONESHOT | EPOLLRDHUP; 212 | 213 | epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event); 214 | } 215 | 216 | int http_conn::m_user_count = 0; 217 | int http_conn::m_epollfd = -1; 218 | 219 | //关闭连接,关闭一个连接,客户总量减一 220 | void http_conn::close_conn(bool real_close) 221 | { 222 | if (real_close && (m_sockfd != -1)) 223 | { 224 | printf("close %d\n", m_sockfd); 225 | removefd(m_epollfd, m_sockfd); 226 | m_sockfd = -1; 227 | m_user_count--; 228 | } 229 | } 230 | 231 | //初始化连接,外部调用初始化套接字地址 232 | void http_conn::init(int sockfd, const sockaddr_in &addr, char *root, int TRIGMode, 233 | int close_log, string user, string passwd, string sqlname) 234 | { 235 | m_sockfd = sockfd; 236 | m_address = addr; 237 | 238 | addfd(m_epollfd, sockfd, true, m_TRIGMode); 239 | m_user_count++; 240 | 241 | //当浏览器出现连接重置时,可能是网站根目录出错或http响应格式出错或者访问的文件中内容完全为空 242 | doc_root = root; 243 | m_TRIGMode = TRIGMode; 244 | m_close_log = close_log; 245 | 246 | strcpy(sql_user, user.c_str()); 247 | strcpy(sql_passwd, passwd.c_str()); 248 | strcpy(sql_name, sqlname.c_str()); 249 | 250 | strcpy(redis_user, user.c_str()); 251 | strcpy(redis_passwd, passwd.c_str()); 252 | 253 | init(); 254 | } 255 | 256 | //初始化新接受的连接 257 | //check_state默认为分析请求行状态 258 | void http_conn::init() 259 | { 260 | mysql = NULL; 261 | redis = NULL; 262 | localRedisConn = NULL; 263 | bytes_to_send = 0; 264 | bytes_have_send = 0; 265 | m_check_state = CHECK_STATE_REQUESTLINE; 266 | m_linger = false; 267 | m_method = GET; 268 | m_url = 0; 269 | m_version = 0; 270 | m_content_length = 0; 271 | m_host = 0; 272 | m_start_line = 0; 273 | m_checked_idx = 0; 274 | m_read_idx = 0; 275 | m_write_idx = 0; 276 | cgi = 0; 277 | m_state = 0; 278 | timer_flag = 0; 279 | improv = 0; 280 | 281 | memset(m_read_buf, '\0', READ_BUFFER_SIZE); 282 | memset(m_write_buf, '\0', WRITE_BUFFER_SIZE); 283 | memset(m_real_file, '\0', FILENAME_LEN); 284 | } 285 | 286 | //从状态机,用于分析出一行内容 287 | //返回值为行的读取状态,有LINE_OK,LINE_BAD,LINE_OPEN 288 | http_conn::LINE_STATUS http_conn::parse_line() 289 | { 290 | char temp; 291 | for (; m_checked_idx < m_read_idx; ++m_checked_idx) 292 | { 293 | temp = m_read_buf[m_checked_idx]; 294 | if (temp == '\r') 295 | { 296 | if ((m_checked_idx + 1) == m_read_idx) 297 | return LINE_OPEN; 298 | else if (m_read_buf[m_checked_idx + 1] == '\n') 299 | { 300 | m_read_buf[m_checked_idx++] = '\0'; 301 | m_read_buf[m_checked_idx++] = '\0'; 302 | return LINE_OK; 303 | } 304 | return LINE_BAD; 305 | } 306 | else if (temp == '\n') 307 | { 308 | if (m_checked_idx > 1 && m_read_buf[m_checked_idx - 1] == '\r') 309 | { 310 | m_read_buf[m_checked_idx - 1] = '\0'; 311 | m_read_buf[m_checked_idx++] = '\0'; 312 | return LINE_OK; 313 | } 314 | return LINE_BAD; 315 | } 316 | } 317 | return LINE_OPEN; 318 | } 319 | 320 | //循环读取客户数据,直到无数据可读或对方关闭连接 321 | //非阻塞ET工作模式下,需要一次性将数据读完 322 | bool http_conn::read_once() 323 | { 324 | if (m_read_idx >= READ_BUFFER_SIZE) 325 | { 326 | return false; 327 | } 328 | int bytes_read = 0; 329 | 330 | //LT读取数据 331 | if (0 == m_TRIGMode) 332 | { 333 | bytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0); 334 | m_read_idx += bytes_read; 335 | 336 | if (bytes_read <= 0) 337 | { 338 | return false; 339 | } 340 | 341 | return true; 342 | } 343 | //ET读数据 344 | else 345 | { 346 | while (true) 347 | { 348 | bytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0); 349 | if (bytes_read == -1) 350 | { 351 | if (errno == EAGAIN || errno == EWOULDBLOCK) 352 | break; 353 | return false; 354 | } 355 | else if (bytes_read == 0) 356 | { 357 | return false; 358 | } 359 | m_read_idx += bytes_read; 360 | } 361 | return true; 362 | } 363 | } 364 | 365 | //解析http请求行,获得请求方法,目标url及http版本号 366 | http_conn::HTTP_CODE http_conn::parse_request_line(char *text) 367 | { 368 | m_url = strpbrk(text, " \t"); 369 | if (!m_url) 370 | { 371 | return BAD_REQUEST; 372 | } 373 | *m_url++ = '\0'; 374 | 375 | string Token_req = text; 376 | m_Token_picture=""; 377 | m_Token_video=""; 378 | m_Token_fans=""; 379 | string::size_type idx0=Token_req.find("m_Token_picture"); 380 | if(idx0 == string::npos) 381 | { 382 | //return NO_TOKENS;//updating http_code 383 | } 384 | else 385 | { 386 | string::size_type idx1=Token_req.find("m_Token_video"); 387 | m_Token_picture=Token_req.substr(idx0,idx1-idx0); 388 | string::size_type idx2=Token_req.find("m_Token_fans"); 389 | m_Token_video=Token_req.substr(idx1,idx2-idx1); 390 | m_Token_fans=Token_req.substr(idx2,Token_req.size()-1); 391 | } 392 | 393 | char *method = text; 394 | if (strcasecmp(method, "GET") == 0) 395 | m_method = GET; 396 | else if (strcasecmp(method, "POST") == 0) 397 | { 398 | m_method = POST; 399 | cgi = 1; 400 | } 401 | else 402 | return BAD_REQUEST; 403 | m_url += strspn(m_url, " \t"); 404 | m_version = strpbrk(m_url, " \t"); 405 | if (!m_version) 406 | return BAD_REQUEST; 407 | *m_version++ = '\0'; 408 | m_version += strspn(m_version, " \t"); 409 | if (strcasecmp(m_version, "HTTP/1.1") != 0) 410 | return BAD_REQUEST; 411 | if (strncasecmp(m_url, "http://", 7) == 0) 412 | { 413 | m_url += 7; 414 | m_url = strchr(m_url, '/'); 415 | } 416 | 417 | if (strncasecmp(m_url, "https://", 8) == 0) 418 | { 419 | m_url += 8; 420 | m_url = strchr(m_url, '/'); 421 | } 422 | 423 | if (!m_url || m_url[0] != '/') 424 | return BAD_REQUEST; 425 | //当url为/时,显示判断界面 426 | if (strlen(m_url) == 1) 427 | strcat(m_url, "judge.html"); 428 | m_check_state = CHECK_STATE_HEADER; 429 | return NO_REQUEST; 430 | } 431 | 432 | //解析http请求的一个头部信息 433 | http_conn::HTTP_CODE http_conn::parse_headers(char *text) 434 | { 435 | if (text[0] == '\0') 436 | { 437 | if (m_content_length != 0) 438 | { 439 | m_check_state = CHECK_STATE_CONTENT; 440 | return NO_REQUEST; 441 | } 442 | return GET_REQUEST; 443 | } 444 | else if (strncasecmp(text, "Connection:", 11) == 0) 445 | { 446 | text += 11; 447 | text += strspn(text, " \t"); 448 | if (strcasecmp(text, "keep-alive") == 0) 449 | { 450 | m_linger = true; 451 | } 452 | } 453 | else if (strncasecmp(text, "Content-length:", 15) == 0) 454 | { 455 | text += 15; 456 | text += strspn(text, " \t"); 457 | m_content_length = atol(text); 458 | } 459 | else if (strncasecmp(text, "Host:", 5) == 0) 460 | { 461 | text += 5; 462 | text += strspn(text, " \t"); 463 | m_host = text; 464 | } 465 | else 466 | { 467 | LOG_INFO("oop!unknow header: %s", text); 468 | } 469 | return NO_REQUEST; 470 | } 471 | 472 | //判断http请求是否被完整读入 473 | http_conn::HTTP_CODE http_conn::parse_content(char *text) 474 | { 475 | if (m_read_idx >= (m_content_length + m_checked_idx)) 476 | { 477 | text[m_content_length] = '\0'; 478 | //POST请求中最后为输入的用户名和密码 479 | m_string = text; 480 | return GET_REQUEST; 481 | } 482 | return NO_REQUEST; 483 | } 484 | 485 | http_conn::HTTP_CODE http_conn::process_read() 486 | { 487 | LINE_STATUS line_status = LINE_OK; 488 | HTTP_CODE ret = NO_REQUEST; 489 | char *text = 0; 490 | 491 | while ((m_check_state == CHECK_STATE_CONTENT && line_status == LINE_OK) || ((line_status = parse_line()) == LINE_OK)) 492 | { 493 | text = get_line(); 494 | m_start_line = m_checked_idx; 495 | LOG_INFO("%s", text); 496 | switch (m_check_state) 497 | { 498 | case CHECK_STATE_REQUESTLINE: 499 | { 500 | ret = parse_request_line(text); 501 | if (ret == BAD_REQUEST) 502 | return BAD_REQUEST; 503 | break; 504 | } 505 | case CHECK_STATE_HEADER: 506 | { 507 | ret = parse_headers(text); 508 | if (ret == BAD_REQUEST) 509 | return BAD_REQUEST; 510 | else if (ret == GET_REQUEST) 511 | { 512 | return do_request(); 513 | } 514 | break; 515 | } 516 | case CHECK_STATE_CONTENT: 517 | { 518 | ret = parse_content(text); 519 | if (ret == GET_REQUEST) 520 | return do_request(); 521 | line_status = LINE_OPEN; 522 | break; 523 | } 524 | default: 525 | return INTERNAL_ERROR; 526 | } 527 | } 528 | return NO_REQUEST; 529 | } 530 | 531 | http_conn::HTTP_CODE http_conn::do_request() 532 | { 533 | if(localRedisConn == NULL) 534 | { 535 | LOG_ERROR("local redis session lose"); 536 | } 537 | else 538 | { 539 | LOG_ERROR("local redis session INFO: err:%lu,fd:%lu,flag:%lu", localRedisConn->err, localRedisConn->fd, localRedisConn->flags); 540 | } 541 | strcpy(m_real_file, doc_root); 542 | int len = strlen(doc_root); 543 | //printf("m_url:%s\n", m_url); 544 | const char *p = strrchr(m_url, '/'); 545 | 546 | //处理cgi 547 | if (cgi == 1 && (*(p + 1) == '2' || *(p + 1) == '3')) 548 | { 549 | 550 | //根据标志判断是登录检测还是注册检测 551 | char flag = m_url[1]; 552 | 553 | char *m_url_real = (char *)malloc(sizeof(char) * 200); 554 | strcpy(m_url_real, "/"); 555 | strcat(m_url_real, m_url + 2); 556 | strncpy(m_real_file + len, m_url_real, FILENAME_LEN - len - 1); 557 | free(m_url_real); 558 | 559 | //将用户名和密码提取出来 560 | //user=123&passwd=123 561 | char name[100], password[100]; 562 | int i; 563 | for (i = 5; m_string[i] != '&'; ++i) 564 | name[i - 5] = m_string[i]; 565 | name[i - 5] = '\0'; 566 | 567 | int j = 0; 568 | for (i = i + 10; m_string[i] != '\0'; ++i, ++j) 569 | password[j] = m_string[i]; 570 | password[j] = '\0'; 571 | 572 | if (*(p + 1) == '3') 573 | { 574 | if (users.find(name) == users.end()) 575 | { 576 | m_lock.lock(); 577 | string set_name = name; 578 | string set_pass = password; 579 | string insert2list = (set_name+'+'+set_pass); 580 | redisReply *res = (redisReply*)redisCommand(localRedisConn, "LPUSH users_list %s", insert2list.c_str()); 581 | users.insert(pair(set_name, set_pass)); 582 | m_lock.unlock(); 583 | 584 | if (1 <= res->integer) 585 | { 586 | LOG_INFO("set a new user"); 587 | strcpy(m_url, "/log.html"); 588 | } 589 | else 590 | { 591 | LOG_ERROR("set a new user fail"); 592 | strcpy(m_url, "/registerError.html"); 593 | } 594 | } 595 | else 596 | strcpy(m_url, "/registerError.html"); 597 | } 598 | //如果是登录,直接判断 599 | //若浏览器端输入的用户名和密码在表中可以查找到,返回1,否则返回0 600 | else if (*(p + 1) == '2') 601 | { 602 | if (users.find(name) != users.end() && users[name] == password) 603 | strcpy(m_url, "/welcome.html"); 604 | //生成token,发放token给http::GET 605 | else 606 | strcpy(m_url, "/logError.html"); 607 | } 608 | } 609 | 610 | redisReply *CheckTokens = NULL; 611 | redisReply *Reset_Token = NULL; 612 | if (*(p + 1) == '0') 613 | { 614 | char *m_url_real = (char *)malloc(sizeof(char) * 200); 615 | strcpy(m_url_real, "/register.html"); 616 | strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); 617 | 618 | free(m_url_real); 619 | } 620 | else if (*(p + 1) == '1') 621 | { 622 | char *m_url_real = (char *)malloc(sizeof(char) * 200); 623 | strcpy(m_url_real, "/log.html"); 624 | strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); 625 | 626 | free(m_url_real); 627 | } 628 | else if (*(p + 1) == '5') 629 | { 630 | CheckTokens = (redisReply*)redisCommand(redis->m_pContext, "EXISTS Token_pictrue"); 631 | if(1 == CheckTokens->integer) 632 | { 633 | char *m_url_real = (char *)malloc(sizeof(char) * 200); 634 | strcpy(m_url_real, "/picture.html"); 635 | strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); 636 | 637 | free(m_url_real); 638 | Reset_Token = (redisReply *)redisCommand(redis->m_pContext, "DEL Token_pictrue");//幂等操作,删除 639 | if(1 == Reset_Token->integer) 640 | { 641 | LOG_INFO("Token_pictrue is moved"); 642 | } 643 | } 644 | else 645 | { 646 | char *m_url_real = (char *)malloc(sizeof(char) * 200); 647 | strcpy(m_url_real, "/repeated.html"); 648 | strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); 649 | 650 | free(m_url_real); 651 | LOG_ERROR("Http request about picture is expired"); 652 | } 653 | } 654 | else if (*(p + 1) == '6') 655 | { 656 | CheckTokens = (redisReply*)redisCommand(redis->m_pContext, "EXISTS Token_video"); 657 | if(1 == CheckTokens->integer) 658 | { 659 | char *m_url_real = (char *)malloc(sizeof(char) * 200); 660 | strcpy(m_url_real, "/video.html"); 661 | strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); 662 | 663 | free(m_url_real); 664 | Reset_Token = (redisReply *)redisCommand(redis->m_pContext, "DEL Token_video");//幂等操作,删除 665 | if(1 == Reset_Token->integer) 666 | { 667 | LOG_INFO("Token_video is moved"); 668 | } 669 | } 670 | else 671 | { 672 | char *m_url_real = (char *)malloc(sizeof(char) * 200); 673 | strcpy(m_url_real, "/repeated.html"); 674 | strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); 675 | 676 | free(m_url_real); 677 | LOG_ERROR("Http request about video is expired"); 678 | } 679 | } 680 | else if (*(p + 1) == '7') 681 | { 682 | CheckTokens = (redisReply*)redisCommand(redis->m_pContext, "EXISTS Token_fans"); 683 | if(1 == CheckTokens->integer) 684 | { 685 | char *m_url_real = (char *)malloc(sizeof(char) * 200); 686 | strcpy(m_url_real, "/fans.html"); 687 | strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); 688 | 689 | free(m_url_real); 690 | 691 | Reset_Token = (redisReply *)redisCommand(redis->m_pContext, "DEL Token_fans");//幂等操作,删除 692 | if(1 == Reset_Token->integer) 693 | { 694 | LOG_INFO("Token_fans is moved"); 695 | } 696 | } 697 | else 698 | { 699 | char *m_url_real = (char *)malloc(sizeof(char) * 200); 700 | strcpy(m_url_real, "/repeated.html"); 701 | strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); 702 | 703 | free(m_url_real); 704 | LOG_ERROR("Http request about fans is expired"); 705 | } 706 | } 707 | else 708 | strncpy(m_real_file + len, m_url, FILENAME_LEN - len - 1); 709 | 710 | if (stat(m_real_file, &m_file_stat) < 0) 711 | return NO_RESOURCE; 712 | 713 | if (!(m_file_stat.st_mode & S_IROTH)) 714 | return FORBIDDEN_REQUEST; 715 | 716 | if (S_ISDIR(m_file_stat.st_mode)) 717 | return BAD_REQUEST; 718 | 719 | int fd = open(m_real_file, O_RDONLY); 720 | m_file_address = (char *)mmap(0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 721 | close(fd); 722 | return FILE_REQUEST; 723 | } 724 | 725 | void http_conn::unmap() 726 | { 727 | if (m_file_address) 728 | { 729 | munmap(m_file_address, m_file_stat.st_size); 730 | m_file_address = 0; 731 | } 732 | } 733 | bool http_conn::write() 734 | { 735 | int temp = 0; 736 | 737 | if (bytes_to_send == 0) 738 | { 739 | modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); 740 | init(); 741 | return true; 742 | } 743 | 744 | while (1) 745 | { 746 | temp = writev(m_sockfd, m_iv, m_iv_count); 747 | 748 | if (temp < 0) 749 | { 750 | if (errno == EAGAIN) 751 | { 752 | modfd(m_epollfd, m_sockfd, EPOLLOUT, m_TRIGMode); 753 | return true; 754 | } 755 | unmap(); 756 | return false; 757 | } 758 | 759 | bytes_have_send += temp; 760 | bytes_to_send -= temp; 761 | if (bytes_have_send >= m_iv[0].iov_len) 762 | { 763 | m_iv[0].iov_len = 0; 764 | m_iv[1].iov_base = m_file_address + (bytes_have_send - m_write_idx); 765 | m_iv[1].iov_len = bytes_to_send; 766 | } 767 | else 768 | { 769 | m_iv[0].iov_base = m_write_buf + bytes_have_send; 770 | m_iv[0].iov_len = m_iv[0].iov_len - bytes_have_send; 771 | } 772 | 773 | if (bytes_to_send <= 0) 774 | { 775 | unmap(); 776 | modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); 777 | 778 | if (m_linger) 779 | { 780 | init(); 781 | return true; 782 | } 783 | else 784 | { 785 | return false; 786 | } 787 | } 788 | } 789 | } 790 | bool http_conn::add_response(const char *format, ...) 791 | { 792 | if (m_write_idx >= WRITE_BUFFER_SIZE) 793 | return false; 794 | va_list arg_list; 795 | va_start(arg_list, format); 796 | int len = vsnprintf(m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list); 797 | if (len >= (WRITE_BUFFER_SIZE - 1 - m_write_idx)) 798 | { 799 | va_end(arg_list); 800 | return false; 801 | } 802 | m_write_idx += len; 803 | va_end(arg_list); 804 | 805 | LOG_INFO("request:%s", m_write_buf); 806 | 807 | return true; 808 | } 809 | bool http_conn::add_status_line(int status, const char *title) 810 | { 811 | return add_response("%s %d %s\r\n", "HTTP/1.1", status, title); 812 | } 813 | bool http_conn::add_headers(int content_len) 814 | { 815 | return add_content_length(content_len) && add_linger() && 816 | add_blank_line(); 817 | } 818 | bool http_conn::add_content_length(int content_len) 819 | { 820 | return add_response("Content-Length:%d\r\n", content_len); 821 | } 822 | bool http_conn::add_content_type() 823 | { 824 | return add_response("Content-Type:%s\r\n", "text/html"); 825 | } 826 | bool http_conn::add_linger() 827 | { 828 | return add_response("Connection:%s\r\n", (m_linger == true) ? "keep-alive" : "close"); 829 | } 830 | bool http_conn::add_blank_line() 831 | { 832 | return add_response("%s", "\r\n"); 833 | } 834 | bool http_conn::add_content(const char *content) 835 | { 836 | return add_response("%s", content); 837 | } 838 | bool http_conn::add_Tokens(const char *token) 839 | { 840 | return add_response("%s", token); 841 | } 842 | bool http_conn::process_write(HTTP_CODE ret) 843 | { 844 | switch (ret) 845 | { 846 | case INTERNAL_ERROR: 847 | { 848 | add_status_line(500, error_500_title); 849 | add_headers(strlen(error_500_form)); 850 | if (!add_content(error_500_form)) 851 | return false; 852 | break; 853 | } 854 | case BAD_REQUEST: 855 | { 856 | add_status_line(404, error_404_title); 857 | add_headers(strlen(error_404_form)); 858 | if (!add_content(error_404_form)) 859 | return false; 860 | break; 861 | } 862 | case FORBIDDEN_REQUEST: 863 | { 864 | add_status_line(403, error_403_title); 865 | add_headers(strlen(error_403_form)); 866 | if (!add_content(error_403_form)) 867 | return false; 868 | break; 869 | } 870 | case FILE_REQUEST: 871 | { 872 | add_status_line(200, ok_200_title); 873 | if (m_file_stat.st_size != 0) 874 | { 875 | add_headers(m_file_stat.st_size); 876 | const char *add_t = ((m_Token_picture+m_Token_video)+m_Token_fans).c_str(); 877 | add_Tokens(add_t); 878 | m_iv[0].iov_base = m_write_buf; 879 | m_iv[0].iov_len = m_write_idx; 880 | m_iv[1].iov_base = m_file_address; 881 | m_iv[1].iov_len = m_file_stat.st_size; 882 | m_iv_count = 2; 883 | bytes_to_send = m_write_idx + m_file_stat.st_size; 884 | return true; 885 | } 886 | else 887 | { 888 | const char *ok_string = ""; 889 | add_headers(strlen(ok_string)); 890 | if (!add_content(ok_string)) 891 | return false; 892 | } 893 | } 894 | default: 895 | return false; 896 | } 897 | m_iv[0].iov_base = m_write_buf; 898 | m_iv[0].iov_len = m_write_idx; 899 | m_iv_count = 1; 900 | bytes_to_send = m_write_idx; 901 | return true; 902 | } 903 | void http_conn::process() 904 | { 905 | HTTP_CODE read_ret = process_read(); 906 | if (read_ret == NO_REQUEST) 907 | { 908 | modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); 909 | return; 910 | } 911 | bool write_ret = process_write(read_ret); 912 | if (!write_ret) 913 | { 914 | close_conn(); 915 | } 916 | modfd(m_epollfd, m_sockfd, EPOLLOUT, m_TRIGMode); 917 | } 918 | -------------------------------------------------------------------------------- /http/http_conn.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPCONNECTION_H 2 | #define HTTPCONNECTION_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "../lock/locker.h" 25 | #include "../CGImysql/sql_connection_pool.h" 26 | #include "../CGIRedis/redis_connection_pool.h" 27 | #include "../timer/lst_timer.h" 28 | #include "../log/log.h" 29 | 30 | class http_conn 31 | { 32 | public: 33 | static const int FILENAME_LEN = 200; 34 | static const int READ_BUFFER_SIZE = 2048; 35 | static const int WRITE_BUFFER_SIZE = 1024; 36 | enum METHOD 37 | { 38 | GET = 0, 39 | POST, 40 | HEAD, 41 | PUT, 42 | DELETE, 43 | TRACE, 44 | OPTIONS, 45 | CONNECT, 46 | PATH 47 | }; 48 | enum CHECK_STATE 49 | { 50 | CHECK_STATE_REQUESTLINE = 0, 51 | CHECK_STATE_HEADER, 52 | CHECK_STATE_CONTENT 53 | }; 54 | enum HTTP_CODE 55 | { 56 | NO_REQUEST, 57 | GET_REQUEST, 58 | BAD_REQUEST, 59 | NO_RESOURCE, 60 | FORBIDDEN_REQUEST, 61 | FILE_REQUEST, 62 | INTERNAL_ERROR, 63 | CLOSED_CONNECTION, 64 | NO_TOKENS 65 | }; 66 | enum LINE_STATUS 67 | { 68 | LINE_OK = 0, 69 | LINE_BAD, 70 | LINE_OPEN 71 | }; 72 | 73 | public: 74 | http_conn() {} 75 | ~http_conn() {} 76 | 77 | public: 78 | void init(int sockfd, const sockaddr_in &addr, char *, int, int, string user, string passwd, string sqlname); 79 | void close_conn(bool real_close = true); 80 | void process(); 81 | bool read_once(); 82 | bool write(); 83 | sockaddr_in *get_address() 84 | { 85 | return &m_address; 86 | } 87 | void initmysql_result(connection_pool *connPool); 88 | void initRedis_result(RedisConnectionPool *connPool, int log_ctrl); 89 | int timer_flag; 90 | int improv; 91 | //int m_close_log; 92 | 93 | private: 94 | void init(); 95 | HTTP_CODE process_read(); 96 | bool process_write(HTTP_CODE ret); 97 | HTTP_CODE parse_request_line(char *text); 98 | HTTP_CODE parse_headers(char *text); 99 | HTTP_CODE parse_content(char *text); 100 | HTTP_CODE do_request(); 101 | char *get_line() { return m_read_buf + m_start_line; }; 102 | LINE_STATUS parse_line(); 103 | void unmap(); 104 | bool add_response(const char *format, ...); 105 | bool add_content(const char *content); 106 | bool add_status_line(int status, const char *title); 107 | bool add_headers(int content_length); 108 | bool add_content_type(); 109 | bool add_content_length(int content_length); 110 | bool add_linger(); 111 | bool add_blank_line(); 112 | bool add_Tokens(const char *token); 113 | 114 | public: 115 | static int m_epollfd; 116 | static int m_user_count; 117 | MYSQL *mysql; 118 | CacheConn *redis; 119 | redisContext *localRedisConn; 120 | int m_state; //读为0, 写为1 121 | 122 | private: 123 | string m_Token_picture; 124 | string m_Token_video; 125 | string m_Token_fans; 126 | int m_sockfd; 127 | sockaddr_in m_address; 128 | char m_read_buf[READ_BUFFER_SIZE]; 129 | int m_read_idx; 130 | int m_checked_idx; 131 | int m_start_line; 132 | char m_write_buf[WRITE_BUFFER_SIZE]; 133 | int m_write_idx; 134 | CHECK_STATE m_check_state; 135 | METHOD m_method; 136 | char m_real_file[FILENAME_LEN]; 137 | char *m_url; 138 | char *m_version; 139 | char *m_host; 140 | int m_content_length; 141 | bool m_linger; 142 | char *m_file_address; 143 | struct stat m_file_stat; 144 | struct iovec m_iv[2]; 145 | int m_iv_count; 146 | int cgi; //是否启用的POST 147 | char *m_string; //存储请求头数据 148 | int bytes_to_send; 149 | int bytes_have_send; 150 | char *doc_root; 151 | 152 | map m_users; 153 | int m_TRIGMode; 154 | 155 | char sql_user[100]; 156 | char sql_passwd[100]; 157 | char sql_name[100]; 158 | 159 | char redis_user[100]; 160 | char redis_passwd[100]; 161 | }; 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /lock/README.md: -------------------------------------------------------------------------------- 1 | 2 | 线程同步机制包装类 3 | =============== 4 | 多线程同步,确保任一时刻只能有一个线程能进入关键代码段. 5 | > * 信号量 6 | > * 互斥锁 7 | > * 条件变量 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /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 | class locker 42 | { 43 | public: 44 | locker() 45 | { 46 | if (pthread_mutex_init(&m_mutex, NULL) != 0) 47 | { 48 | throw std::exception(); 49 | } 50 | } 51 | ~locker() 52 | { 53 | pthread_mutex_destroy(&m_mutex); 54 | } 55 | bool lock() 56 | { 57 | return pthread_mutex_lock(&m_mutex) == 0; 58 | } 59 | bool unlock() 60 | { 61 | return pthread_mutex_unlock(&m_mutex) == 0; 62 | } 63 | pthread_mutex_t *get() 64 | { 65 | return &m_mutex; 66 | } 67 | 68 | private: 69 | pthread_mutex_t m_mutex; 70 | }; 71 | class cond 72 | { 73 | public: 74 | cond() 75 | { 76 | if (pthread_cond_init(&m_cond, NULL) != 0) 77 | { 78 | //pthread_mutex_destroy(&m_mutex); 79 | throw std::exception(); 80 | } 81 | } 82 | ~cond() 83 | { 84 | pthread_cond_destroy(&m_cond); 85 | } 86 | bool wait(pthread_mutex_t *m_mutex) 87 | { 88 | int ret = 0; 89 | //pthread_mutex_lock(&m_mutex); 90 | ret = pthread_cond_wait(&m_cond, m_mutex); 91 | //pthread_mutex_unlock(&m_mutex); 92 | return ret == 0; 93 | } 94 | bool timewait(pthread_mutex_t *m_mutex, struct timespec t) 95 | { 96 | int ret = 0; 97 | //pthread_mutex_lock(&m_mutex); 98 | ret = pthread_cond_timedwait(&m_cond, m_mutex, &t); 99 | //pthread_mutex_unlock(&m_mutex); 100 | return ret == 0; 101 | } 102 | bool signal() 103 | { 104 | return pthread_cond_signal(&m_cond) == 0; 105 | } 106 | bool broadcast() 107 | { 108 | return pthread_cond_broadcast(&m_cond) == 0; 109 | } 110 | 111 | private: 112 | //static pthread_mutex_t m_mutex; 113 | pthread_cond_t m_cond; 114 | }; 115 | #endif 116 | -------------------------------------------------------------------------------- /log/README.md: -------------------------------------------------------------------------------- 1 | 2 | 同步/异步日志系统 3 | =============== 4 | 同步/异步日志系统主要涉及了两个模块,一个是日志模块,一个是阻塞队列模块,其中加入阻塞队列模块主要是解决异步写入日志做准备. 5 | > * 自定义阻塞队列 6 | > * 单例模式创建日志 7 | > * 同步日志 8 | > * 异步日志 9 | > * 实现按天、超行分类 10 | -------------------------------------------------------------------------------- /log/block_queue.h: -------------------------------------------------------------------------------- 1 | /************************************************************* 2 | *循环数组实现的阻塞队列,m_back = (m_back + 1) % m_max_size; 3 | *线程安全,每个操作前都要先加互斥锁,操作完后,再解锁 4 | **************************************************************/ 5 | 6 | #ifndef BLOCK_QUEUE_H 7 | #define BLOCK_QUEUE_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "../lock/locker.h" 14 | using namespace std; 15 | 16 | template 17 | class block_queue 18 | { 19 | public: 20 | block_queue(int max_size = 1000) 21 | { 22 | if (max_size <= 0) 23 | { 24 | exit(-1); 25 | } 26 | 27 | m_max_size = max_size; 28 | m_array = new T[max_size]; 29 | m_size = 0; 30 | m_front = -1; 31 | m_back = -1; 32 | } 33 | 34 | void clear() 35 | { 36 | m_mutex.lock(); 37 | m_size = 0; 38 | m_front = -1; 39 | m_back = -1; 40 | m_mutex.unlock(); 41 | } 42 | 43 | ~block_queue() 44 | { 45 | m_mutex.lock(); 46 | if (m_array != NULL) 47 | delete [] m_array; 48 | 49 | m_mutex.unlock(); 50 | } 51 | //判断队列是否满了 52 | bool full() 53 | { 54 | m_mutex.lock(); 55 | if (m_size >= m_max_size) 56 | { 57 | 58 | m_mutex.unlock(); 59 | return true; 60 | } 61 | m_mutex.unlock(); 62 | return false; 63 | } 64 | //判断队列是否为空 65 | bool empty() 66 | { 67 | m_mutex.lock(); 68 | if (0 == m_size) 69 | { 70 | m_mutex.unlock(); 71 | return true; 72 | } 73 | m_mutex.unlock(); 74 | return false; 75 | } 76 | //返回队首元素 77 | bool front(T &value) 78 | { 79 | m_mutex.lock(); 80 | if (0 == m_size) 81 | { 82 | m_mutex.unlock(); 83 | return false; 84 | } 85 | value = m_array[m_front]; 86 | m_mutex.unlock(); 87 | return true; 88 | } 89 | //返回队尾元素 90 | bool back(T &value) 91 | { 92 | m_mutex.lock(); 93 | if (0 == m_size) 94 | { 95 | m_mutex.unlock(); 96 | return false; 97 | } 98 | value = m_array[m_back]; 99 | m_mutex.unlock(); 100 | return true; 101 | } 102 | 103 | int size() 104 | { 105 | int tmp = 0; 106 | 107 | m_mutex.lock(); 108 | tmp = m_size; 109 | 110 | m_mutex.unlock(); 111 | return tmp; 112 | } 113 | 114 | int max_size() 115 | { 116 | int tmp = 0; 117 | 118 | m_mutex.lock(); 119 | tmp = m_max_size; 120 | 121 | m_mutex.unlock(); 122 | return tmp; 123 | } 124 | //往队列添加元素,需要将所有使用队列的线程先唤醒 125 | //当有元素push进队列,相当于生产者生产了一个元素 126 | //若当前没有线程等待条件变量,则唤醒无意义 127 | bool push(const T &item) 128 | { 129 | 130 | m_mutex.lock(); 131 | if (m_size >= m_max_size) 132 | { 133 | 134 | m_cond.broadcast(); 135 | m_mutex.unlock(); 136 | return false; 137 | } 138 | 139 | m_back = (m_back + 1) % m_max_size; 140 | m_array[m_back] = item; 141 | 142 | m_size++; 143 | 144 | m_cond.broadcast(); 145 | m_mutex.unlock(); 146 | return true; 147 | } 148 | //pop时,如果当前队列没有元素,将会等待条件变量 149 | bool pop(T &item) 150 | { 151 | 152 | m_mutex.lock(); 153 | while (m_size <= 0) 154 | { 155 | 156 | if (!m_cond.wait(m_mutex.get())) 157 | { 158 | m_mutex.unlock(); 159 | return false; 160 | } 161 | } 162 | 163 | m_front = (m_front + 1) % m_max_size; 164 | item = m_array[m_front]; 165 | m_size--; 166 | m_mutex.unlock(); 167 | return true; 168 | } 169 | 170 | //增加了超时处理 171 | bool pop(T &item, int ms_timeout) 172 | { 173 | struct timespec t = {0, 0}; 174 | struct timeval now = {0, 0}; 175 | gettimeofday(&now, NULL); 176 | m_mutex.lock(); 177 | if (m_size <= 0) 178 | { 179 | t.tv_sec = now.tv_sec + ms_timeout / 1000; 180 | t.tv_nsec = (ms_timeout % 1000) * 1000; 181 | if (!m_cond.timewait(m_mutex.get(), t)) 182 | { 183 | m_mutex.unlock(); 184 | return false; 185 | } 186 | } 187 | 188 | if (m_size <= 0) 189 | { 190 | m_mutex.unlock(); 191 | return false; 192 | } 193 | 194 | m_front = (m_front + 1) % m_max_size; 195 | item = m_array[m_front]; 196 | m_size--; 197 | m_mutex.unlock(); 198 | return true; 199 | } 200 | 201 | private: 202 | locker m_mutex; 203 | cond m_cond; 204 | 205 | T *m_array; 206 | int m_size; 207 | int m_max_size; 208 | int m_front; 209 | int m_back; 210 | }; 211 | 212 | #endif 213 | -------------------------------------------------------------------------------- /log/log.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "log.h" 6 | #include 7 | using namespace std; 8 | 9 | Log::Log() 10 | { 11 | m_count = 0; 12 | m_is_async = false; 13 | } 14 | 15 | Log::~Log() 16 | { 17 | if (m_fp != NULL) 18 | { 19 | fclose(m_fp); 20 | } 21 | } 22 | //异步需要设置阻塞队列的长度,同步不需要设置 23 | bool Log::init(const char *file_name, int close_log, int log_buf_size, int split_lines, int max_queue_size) 24 | { 25 | //如果设置了max_queue_size,则设置为异步 26 | if (max_queue_size >= 1) 27 | { 28 | m_is_async = true; 29 | m_log_queue = new block_queue(max_queue_size); 30 | pthread_t tid; 31 | //flush_log_thread为回调函数,这里表示创建线程异步写日志 32 | pthread_create(&tid, NULL, flush_log_thread, NULL); 33 | } 34 | 35 | m_close_log = close_log; 36 | m_log_buf_size = log_buf_size; 37 | m_buf = new char[m_log_buf_size]; 38 | memset(m_buf, '\0', m_log_buf_size); 39 | m_split_lines = split_lines; 40 | 41 | time_t t = time(NULL); 42 | struct tm *sys_tm = localtime(&t); 43 | struct tm my_tm = *sys_tm; 44 | 45 | 46 | const char *p = strrchr(file_name, '/'); 47 | char log_full_name[256] = {0}; 48 | 49 | if (p == NULL) 50 | { 51 | snprintf(log_full_name, 255, "%d_%02d_%02d_%s", my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday, file_name); 52 | } 53 | else 54 | { 55 | strcpy(log_name, p + 1); 56 | strncpy(dir_name, file_name, p - file_name + 1); 57 | snprintf(log_full_name, 255, "%s%d_%02d_%02d_%s", dir_name, my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday, log_name); 58 | } 59 | 60 | m_today = my_tm.tm_mday; 61 | 62 | m_fp = fopen(log_full_name, "a"); 63 | if (m_fp == NULL) 64 | { 65 | return false; 66 | } 67 | 68 | return true; 69 | } 70 | 71 | void Log::write_log(int level, const char *format, ...) 72 | { 73 | struct timeval now = {0, 0}; 74 | gettimeofday(&now, NULL); 75 | time_t t = now.tv_sec; 76 | struct tm *sys_tm = localtime(&t); 77 | struct tm my_tm = *sys_tm; 78 | char s[16] = {0}; 79 | switch (level) 80 | { 81 | case 0: 82 | strcpy(s, "[debug]:"); 83 | break; 84 | case 1: 85 | strcpy(s, "[info]:"); 86 | break; 87 | case 2: 88 | strcpy(s, "[warn]:"); 89 | break; 90 | case 3: 91 | strcpy(s, "[erro]:"); 92 | break; 93 | default: 94 | strcpy(s, "[info]:"); 95 | break; 96 | } 97 | //写入一个log,对m_count++, m_split_lines最大行数 98 | m_mutex.lock(); 99 | m_count++; 100 | 101 | if (m_today != my_tm.tm_mday || m_count % m_split_lines == 0) //everyday log 102 | { 103 | 104 | char new_log[256] = {0}; 105 | fflush(m_fp); 106 | fclose(m_fp); 107 | char tail[16] = {0}; 108 | 109 | snprintf(tail, 16, "%d_%02d_%02d_", my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday); 110 | 111 | if (m_today != my_tm.tm_mday) 112 | { 113 | snprintf(new_log, 255, "%s%s%s", dir_name, tail, log_name); 114 | m_today = my_tm.tm_mday; 115 | m_count = 0; 116 | } 117 | else 118 | { 119 | snprintf(new_log, 255, "%s%s%s.%lld", dir_name, tail, log_name, m_count / m_split_lines); 120 | } 121 | m_fp = fopen(new_log, "a"); 122 | } 123 | 124 | m_mutex.unlock(); 125 | 126 | va_list valst; 127 | va_start(valst, format); 128 | 129 | string log_str; 130 | m_mutex.lock(); 131 | 132 | //写入的具体时间内容格式 133 | int n = snprintf(m_buf, 48, "%d-%02d-%02d %02d:%02d:%02d.%06ld %s ", 134 | my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday, 135 | my_tm.tm_hour, my_tm.tm_min, my_tm.tm_sec, now.tv_usec, s); 136 | 137 | int m = vsnprintf(m_buf + n, m_log_buf_size - 1, format, valst); 138 | m_buf[n + m] = '\n'; 139 | m_buf[n + m + 1] = '\0'; 140 | log_str = m_buf; 141 | 142 | m_mutex.unlock(); 143 | 144 | if (m_is_async && !m_log_queue->full()) 145 | { 146 | m_log_queue->push(log_str); 147 | } 148 | else 149 | { 150 | m_mutex.lock(); 151 | fputs(log_str.c_str(), m_fp); 152 | m_mutex.unlock(); 153 | } 154 | 155 | va_end(valst); 156 | } 157 | 158 | void Log::flush(void) 159 | { 160 | m_mutex.lock(); 161 | //强制刷新写入流缓冲区 162 | fflush(m_fp); 163 | m_mutex.unlock(); 164 | } 165 | -------------------------------------------------------------------------------- /log/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "block_queue.h" 10 | 11 | using namespace std; 12 | 13 | class Log 14 | { 15 | public: 16 | //C++11以后,使用局部变量懒汉不用加锁 17 | static Log *get_instance() 18 | { 19 | static Log instance; 20 | return &instance; 21 | } 22 | 23 | static void *flush_log_thread(void *args) 24 | { 25 | Log::get_instance()->async_write_log(); 26 | } 27 | //可选择的参数有日志文件、日志缓冲区大小、最大行数以及最长日志条队列 28 | bool init(const char *file_name, int close_log, int log_buf_size = 8192, int split_lines = 5000000, int max_queue_size = 0); 29 | 30 | void write_log(int level, const char *format, ...); 31 | 32 | void flush(void); 33 | 34 | private: 35 | Log(); 36 | virtual ~Log(); 37 | void *async_write_log() 38 | { 39 | string single_log; 40 | //从阻塞队列中取出一个日志string,写入文件 41 | while (m_log_queue->pop(single_log)) 42 | { 43 | m_mutex.lock(); 44 | fputs(single_log.c_str(), m_fp); 45 | m_mutex.unlock(); 46 | } 47 | } 48 | 49 | private: 50 | char dir_name[128]; //路径名 51 | char log_name[128]; //log文件名 52 | int m_split_lines; //日志最大行数 53 | int m_log_buf_size; //日志缓冲区大小 54 | long long m_count; //日志行数记录 55 | int m_today; //因为按天分类,记录当前时间是那一天 56 | FILE *m_fp; //打开log的文件指针 57 | char *m_buf; 58 | block_queue *m_log_queue; //阻塞队列 59 | bool m_is_async; //是否同步标志位 60 | locker m_mutex; 61 | int m_close_log; //关闭日志 62 | }; 63 | 64 | #define LOG_DEBUG(format, ...) if(0 == m_close_log) {Log::get_instance()->write_log(0, format, ##__VA_ARGS__); Log::get_instance()->flush();} 65 | #define LOG_INFO(format, ...) if(0 == m_close_log) {Log::get_instance()->write_log(1, format, ##__VA_ARGS__); Log::get_instance()->flush();} 66 | #define LOG_WARN(format, ...) if(0 == m_close_log) {Log::get_instance()->write_log(2, format, ##__VA_ARGS__); Log::get_instance()->flush();} 67 | #define LOG_ERROR(format, ...) if(0 == m_close_log) {Log::get_instance()->write_log(3, format, ##__VA_ARGS__); Log::get_instance()->flush();} 68 | //#define LOG_REDIS_ERROR(format, ...) if(0 == RedisLogCtl) {Log::get_instance()->write_log(3, format, ##__VA_ARGS__); Log::get_instance()->flush();} 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | //需要修改的数据库信息,登录名,密码,库名 6 | string user = "root"; 7 | string passwd = "root"; 8 | string databasename = "yourdb"; 9 | 10 | //命令行解析 11 | Config config; 12 | config.parse_arg(argc, argv); 13 | 14 | WebServer server; 15 | 16 | //初始化 17 | server.init(config.PORT, user, passwd, databasename, config.LOGWrite, 18 | config.OPT_LINGER, config.TRIGMode, config.sql_num, config.redis_num, config.thread_num, 19 | config.close_log, config.actor_model); 20 | 21 | 22 | //日志 23 | server.log_write(); 24 | 25 | //数据库 26 | server.sql_pool(); 27 | 28 | server.redis_pool(); 29 | 30 | //线程池 31 | server.thread_pool(); 32 | 33 | //触发模式 34 | server.trig_mode(); 35 | 36 | //监听 37 | server.eventListen(); 38 | 39 | //运行 40 | server.eventLoop(); 41 | 42 | return 0; 43 | } -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | 3 | DEBUG ?= 1 4 | ifeq ($(DEBUG), 1) 5 | CXXFLAGS += -g 6 | else 7 | CXXFLAGS += -O2 8 | 9 | endif 10 | 11 | server: main.cpp ./timer/lst_timer.cpp ./http/http_conn.cpp ./log/log.cpp ./CGImysql/sql_connection_pool.cpp ./CGIRedis/redis_connection_pool.cpp webserver.cpp config.cpp 12 | $(CXX) -o server $^ $(CXXFLAGS) -lpthread -lmysqlclient -lhiredis 13 | 14 | clean: 15 | rm -r server 16 | -------------------------------------------------------------------------------- /root/README.md: -------------------------------------------------------------------------------- 1 | 界面跳转 2 | =============== 3 | 对html中action行为设置标志位,将method设置为POST 4 | > * 0 注册 5 | > * 1 登录 6 | > * 2 登录检测 7 | > * 3 注册检测 8 | > * 5 请求图片 9 | > * 6 请求视频 10 | > * 7 关注我 11 | > * 8 重复操作 -------------------------------------------------------------------------------- /root/fans.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | awsl 6 | 7 |
8 |
9 |
嘿嘿,你来啦.
10 |
11 | 12 |
13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /root/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/favicon.ico -------------------------------------------------------------------------------- /root/frame.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/frame.jpg -------------------------------------------------------------------------------- /root/judge.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebServer 6 | 7 | 8 |
9 |
10 |
欢迎访问
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /root/log.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sign in 6 | 7 | 8 |
9 |
10 |
登录
11 |
12 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /root/logError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sign in 6 | 7 | 8 |
9 |
10 |
登录
11 |
12 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /root/login.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/login.gif -------------------------------------------------------------------------------- /root/loginnew.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/loginnew.gif -------------------------------------------------------------------------------- /root/picture.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/picture.gif -------------------------------------------------------------------------------- /root/picture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | awsl 6 | 7 |
8 |
9 |
你居然想看图,不想关注我
10 |
11 | 12 |
13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /root/register.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/register.gif -------------------------------------------------------------------------------- /root/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sign up 6 | 7 | 8 |
9 |
10 |
注册
11 |
12 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /root/registerError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sign up 6 | 7 | 8 |
9 |
10 |
注册
11 |
12 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /root/registernew.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/registernew.gif -------------------------------------------------------------------------------- /root/repeated.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | awsl 6 | 7 |
8 |
9 |
哦,重复操作.
10 |
11 | 12 |
13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /root/video.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/video.gif -------------------------------------------------------------------------------- /root/video.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | awsl 6 | 7 |
8 |
9 |
你居然想看视频,不想关注我
10 |
11 | 12 |
13 |
14 |
17 | 18 | -------------------------------------------------------------------------------- /root/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebServer 6 | 7 | 8 |
9 |
10 |
是时候做出选择了
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /root/xxx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/xxx.jpg -------------------------------------------------------------------------------- /root/xxx.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/root/xxx.mp4 -------------------------------------------------------------------------------- /test_presure/README.md: -------------------------------------------------------------------------------- 1 | 服务器压力测试 2 | =============== 3 | Webbench是有名的网站压力测试工具,它是由[Lionbridge](http://www.lionbridge.com)公司开发。 4 | 5 | > * 测试处在相同硬件上,不同服务的性能以及不同硬件上同一个服务的运行状况。 6 | > * 展示服务器的两项内容:每秒钟响应请求数和每秒钟传输数据量。 7 | 8 | 9 | 10 | 11 | 测试规则 12 | ------------ 13 | * 测试示例 14 | 15 | ```C++ 16 | ./webbench -c 500 -t 30 http://127.0.0.1/phpionfo.php 17 | ``` 18 | * 参数 19 | 20 | > * `-c` 表示客户端数 21 | > * `-t` 表示时间 22 | 23 | 24 | 测试结果 25 | --------- 26 | > * 并发连接总数:10000 27 | > * 访问服务器时间:5s 28 | > * 每秒钟响应请求数 29 | > * 每秒钟传输数据量 30 | > * 所有访问均成功 31 | 32 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/COPYRIGHT: -------------------------------------------------------------------------------- 1 | debian/copyright -------------------------------------------------------------------------------- /test_presure/webbench-1.5/ChangeLog: -------------------------------------------------------------------------------- 1 | debian/changelog -------------------------------------------------------------------------------- /test_presure/webbench-1.5/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS?= -Wall -ggdb -W -O 2 | CC?= gcc 3 | LIBS?= 4 | LDFLAGS?= 5 | PREFIX?= /usr/local 6 | VERSION=1.5 7 | TMPDIR=/tmp/webbench-$(VERSION) 8 | 9 | all: webbench tags 10 | 11 | tags: *.c 12 | -ctags *.c 13 | 14 | install: webbench 15 | install -s webbench $(DESTDIR)$(PREFIX)/bin 16 | install -m 644 webbench.1 $(DESTDIR)$(PREFIX)/man/man1 17 | install -d $(DESTDIR)$(PREFIX)/share/doc/webbench 18 | install -m 644 debian/copyright $(DESTDIR)$(PREFIX)/share/doc/webbench 19 | install -m 644 debian/changelog $(DESTDIR)$(PREFIX)/share/doc/webbench 20 | 21 | webbench: webbench.o Makefile 22 | $(CC) $(CFLAGS) $(LDFLAGS) -o webbench webbench.o $(LIBS) 23 | 24 | clean: 25 | -rm -f *.o webbench *~ core *.core tags 26 | 27 | tar: clean 28 | -debian/rules clean 29 | rm -rf $(TMPDIR) 30 | install -d $(TMPDIR) 31 | cp -p Makefile webbench.c socket.c webbench.1 $(TMPDIR) 32 | install -d $(TMPDIR)/debian 33 | -cp -p debian/* $(TMPDIR)/debian 34 | ln -sf debian/copyright $(TMPDIR)/COPYRIGHT 35 | ln -sf debian/changelog $(TMPDIR)/ChangeLog 36 | -cd $(TMPDIR) && cd .. && tar cozf webbench-$(VERSION).tar.gz webbench-$(VERSION) 37 | 38 | webbench.o: webbench.c socket.c Makefile 39 | 40 | .PHONY: clean install all tar 41 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/debian/changelog: -------------------------------------------------------------------------------- 1 | webbench (1.5) unstable; urgency=low 2 | 3 | * allow building with both Gnu and BSD make 4 | 5 | -- Radim Kolar Fri, Jun 25 12:00:20 CEST 2004 6 | 7 | webbench (1.4) unstable; urgency=low 8 | 9 | * check if url is not too long 10 | * report correct program version number 11 | * use yield() when waiting for test start 12 | * corrected error codes 13 | * check availability of test server first 14 | * do not abort test if first request failed 15 | * report when some childrens are dead. 16 | * use alarm, not time() for lower syscal use by bench 17 | * use mode 644 for installed doc 18 | * makefile cleaned for better freebsd ports integration 19 | 20 | -- Radim Kolar Thu, 15 Jan 2004 11:15:52 +0100 21 | 22 | webbench (1.3) unstable; urgency=low 23 | 24 | * Build fixes for freeBSD 25 | * Default benchmark time 60 -> 30 26 | * generate tar with subdirectory 27 | * added to freeBSD ports collection 28 | 29 | -- Radim Kolar Mon, 12 Jan 2004 17:00:24 +0100 30 | 31 | webbench (1.2) unstable; urgency=low 32 | 33 | * Only debian-related bugfixes 34 | * Updated Debian/rules 35 | * Adapted to fit new directory system 36 | * moved from debstd to dh_* 37 | 38 | -- Radim Kolar Fri, 18 Jan 2002 12:33:04 +0100 39 | 40 | webbench (1.1) unstable; urgency=medium 41 | 42 | * Program debianized 43 | * added support for multiple methods (GET, HEAD, OPTIONS, TRACE) 44 | * added support for multiple HTTP versions (0.9 -- 1.1) 45 | * added long options 46 | * added multiple clients 47 | * wait for start of second before test 48 | * test time can be specified 49 | * better error checking when reading reply from server 50 | * FIX: tests was one second longer than expected 51 | 52 | -- Radim Kolar Thu, 16 Sep 1999 18:48:00 +0200 53 | 54 | Local variables: 55 | mode: debian-changelog 56 | End: 57 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/debian/control: -------------------------------------------------------------------------------- 1 | Source: webbench 2 | Section: web 3 | Priority: extra 4 | Maintainer: Radim Kolar 5 | Build-Depends: debhelper (>> 3.0.0) 6 | Standards-Version: 3.5.2 7 | 8 | Package: webbench 9 | Architecture: any 10 | Depends: ${shlibs:Depends} 11 | Description: Simple forking Web benchmark 12 | webbench is very simple program for benchmarking WWW or Proxy servers. 13 | Uses fork() for simulating multiple clients load. Can use HTTP 0.9 - 1.1 14 | requests, but Keep-Alive connections are not supported. 15 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/debian/copyright: -------------------------------------------------------------------------------- 1 | Webbench was written by Radim Kolar 1997-2004 (hsn@netmag.cz). 2 | 3 | UNIX sockets code (socket.c) taken from popclient 1.5 4/1/94 4 | public domain code, created by Virginia Tech Computing Center. 5 | 6 | Copyright: GPL (see /usr/share/common-licenses/GPL) 7 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/debian/dirs: -------------------------------------------------------------------------------- 1 | usr/bin -------------------------------------------------------------------------------- /test_presure/webbench-1.5/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Sample debian/rules that uses debhelper. 3 | # GNU copyright 1997 to 1999 by Joey Hess. 4 | 5 | # Uncomment this to turn on verbose mode. 6 | #export DH_VERBOSE=1 7 | 8 | # This is the debhelper compatability version to use. 9 | export DH_COMPAT=3 10 | 11 | configure: configure-stamp 12 | configure-stamp: 13 | dh_testdir 14 | touch configure-stamp 15 | 16 | build: configure-stamp build-stamp 17 | build-stamp: 18 | dh_testdir 19 | $(MAKE) 20 | touch build-stamp 21 | 22 | clean: 23 | dh_testdir 24 | rm -f build-stamp configure-stamp 25 | 26 | # Add here commands to clean up after the build process. 27 | -$(MAKE) clean 28 | 29 | dh_clean 30 | 31 | install: build 32 | dh_testdir 33 | dh_testroot 34 | dh_clean -k 35 | dh_installdirs 36 | 37 | # Add here commands to install the package into debian/webbench. 38 | $(MAKE) install DESTDIR=$(CURDIR)/debian/webbench 39 | 40 | 41 | # Build architecture-independent files here. 42 | binary-indep: build install 43 | # We have nothing to do by default. 44 | 45 | # Build architecture-dependent files here. 46 | binary-arch: build install 47 | dh_testdir 48 | dh_testroot 49 | dh_installdocs 50 | dh_installman webbench.1 51 | dh_installchangelogs 52 | dh_link 53 | dh_strip 54 | dh_compress 55 | dh_fixperms 56 | # dh_makeshlibs 57 | dh_installdeb 58 | dh_shlibdeps 59 | dh_gencontrol 60 | dh_md5sums 61 | dh_builddeb 62 | 63 | binary: binary-indep binary-arch 64 | .PHONY: build clean binary-indep binary-arch binary install configure 65 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/socket.c: -------------------------------------------------------------------------------- 1 | /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $ 2 | * 3 | * This module has been modified by Radim Kolar for OS/2 emx 4 | */ 5 | 6 | /*********************************************************************** 7 | module: socket.c 8 | program: popclient 9 | SCCS ID: @(#)socket.c 1.5 4/1/94 10 | programmer: Virginia Tech Computing Center 11 | compiler: DEC RISC C compiler (Ultrix 4.1) 12 | environment: DEC Ultrix 4.3 13 | description: UNIX sockets code. 14 | ***********************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | int Socket(const char *host, int clientPort) 30 | { 31 | int sock; 32 | unsigned long inaddr; 33 | struct sockaddr_in ad; 34 | struct hostent *hp; 35 | 36 | memset(&ad, 0, sizeof(ad)); 37 | ad.sin_family = AF_INET; 38 | 39 | inaddr = inet_addr(host); 40 | if (inaddr != INADDR_NONE) 41 | memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); 42 | else 43 | { 44 | hp = gethostbyname(host); 45 | if (hp == NULL) 46 | return -1; 47 | memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); 48 | } 49 | ad.sin_port = htons(clientPort); 50 | 51 | sock = socket(AF_INET, SOCK_STREAM, 0); 52 | if (sock < 0) 53 | return sock; 54 | if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0) 55 | return -1; 56 | return sock; 57 | } 58 | 59 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ 4 | !_TAG_PROGRAM_NAME Exuberant Ctags // 5 | !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ 6 | !_TAG_PROGRAM_VERSION 5.9~svn20110310 // 7 | METHOD_GET webbench.c 35;" d file: 8 | METHOD_HEAD webbench.c 36;" d file: 9 | METHOD_OPTIONS webbench.c 37;" d file: 10 | METHOD_TRACE webbench.c 38;" d file: 11 | PROGRAM_VERSION webbench.c 39;" d file: 12 | REQUEST_SIZE webbench.c 50;" d file: 13 | Socket socket.c /^int Socket(const char *host, int clientPort)$/;" f 14 | alarm_handler webbench.c /^static void alarm_handler(int signal)$/;" f file: 15 | bench webbench.c /^static int bench(void)$/;" f file: 16 | benchcore webbench.c /^void benchcore(const char *host,const int port,const char *req)$/;" f 17 | benchtime webbench.c /^int benchtime=30;$/;" v 18 | build_request webbench.c /^void build_request(const char *url)$/;" f 19 | bytes webbench.c /^int bytes=0;$/;" v 20 | clients webbench.c /^int clients=1;$/;" v 21 | failed webbench.c /^int failed=0;$/;" v 22 | force webbench.c /^int force=0;$/;" v 23 | force_reload webbench.c /^int force_reload=0;$/;" v 24 | host webbench.c /^char host[MAXHOSTNAMELEN];$/;" v 25 | http10 webbench.c /^int http10=1; \/* 0 - http\/0.9, 1 - http\/1.0, 2 - http\/1.1 *\/$/;" v 26 | long_options webbench.c /^static const struct option long_options[]=$/;" v typeref:struct:option file: 27 | main webbench.c /^int main(int argc, char *argv[])$/;" f 28 | method webbench.c /^int method=METHOD_GET;$/;" v 29 | mypipe webbench.c /^int mypipe[2];$/;" v 30 | proxyhost webbench.c /^char *proxyhost=NULL;$/;" v 31 | proxyport webbench.c /^int proxyport=80;$/;" v 32 | request webbench.c /^char request[REQUEST_SIZE];$/;" v 33 | speed webbench.c /^int speed=0;$/;" v 34 | timerexpired webbench.c /^volatile int timerexpired=0;$/;" v 35 | usage webbench.c /^static void usage(void)$/;" f file: 36 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/webbench: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/test_presure/webbench-1.5/webbench -------------------------------------------------------------------------------- /test_presure/webbench-1.5/webbench.1: -------------------------------------------------------------------------------- 1 | .TH WEBBENCH 1 "14 Jan 2004" 2 | .\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection 3 | .\" other parms are allowed: see man(7), man(1) 4 | .SH NAME 5 | webbench \- simple forking web benchmark 6 | .SH SYNOPSIS 7 | .B webbench 8 | .I "[options] URL" 9 | .br 10 | .SH "AUTHOR" 11 | This program and manual page was written by Radim Kolar, 12 | for the 13 | .B Supreme Personality of Godhead 14 | (but may be used by others). 15 | .SH "DESCRIPTION" 16 | .B webbench 17 | is simple program for benchmarking HTTP servers or any 18 | other servers, which can be accessed via HTTP proxy. Unlike others 19 | benchmarks, 20 | .B webbench 21 | uses multiple processes for simulating traffic 22 | generated by multiple users. This allows better operating 23 | on SMP systems and on systems with slow or buggy implementation 24 | of select(). 25 | .SH OPTIONS 26 | The programs follow the usual GNU command line syntax, with long 27 | options starting with two dashes (`-'). 28 | A summary of options are included below. 29 | .TP 30 | .B \-?, \-h, \-\-help 31 | Show summary of options. 32 | .TP 33 | .B \-v, \-\-version 34 | Show version of program. 35 | .TP 36 | .B \-f, \-\-force 37 | Do not wait for any response from server. Close connection after 38 | request is send. This option produce quite a good denial of service 39 | attack. 40 | .TP 41 | .B \-9, \-\-http09 42 | Use HTTP/0.9 protocol, if possible. 43 | .TP 44 | .B \-1, \-\-http10 45 | Use HTTP/1.0 protocol, if possible. 46 | .TP 47 | .B \-2, \-\-http11 48 | Use HTTP/1.1 protocol (without 49 | .I Keep-Alive 50 | ), if possible. 51 | .TP 52 | .B \-r, \-\-reload 53 | Forces proxy to reload document. If proxy is not 54 | set, option has no effect. 55 | .TP 56 | .B \-t, \-\-time 57 | Run benchmark for 58 | .I 59 | seconds. Default value is 30. 60 | .TP 61 | .B \-p, \-\-proxy 62 | Send request via proxy server. Needed for supporting others protocols 63 | than HTTP. 64 | .TP 65 | .B \-\-get 66 | Use GET request method. 67 | .TP 68 | .B \-\-head 69 | Use HEAD request method. 70 | .TP 71 | .B \-\-options 72 | Use OPTIONS request method. 73 | .TP 74 | .B \-\-trace 75 | Use TRACE request method. 76 | .TP 77 | .B \-c, \-\-clients 78 | Use 79 | .I 80 | multiple clients for benchmark. Default value 81 | is 1. 82 | .SH "EXIT STATUS" 83 | .TP 84 | 0 - sucess 85 | .TP 86 | 1 - benchmark failed, can not connect to server 87 | .TP 88 | 2 - bad command line argument(s) 89 | .TP 90 | 3 - internal error, i.e. fork failed 91 | .SH "TODO" 92 | Include support for using 93 | .I Keep-Alive 94 | HTTP/1.1 connections. 95 | .SH "COPYING" 96 | Webbench is distributed under GPL. Copyright 1997-2004 97 | Radim Kolar (hsn@netmag.cz). 98 | UNIX sockets code taken from popclient 1.5 4/1/94 99 | public domain code, created by Virginia Tech Computing Center. 100 | .BR 101 | This man page is public domain. 102 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/webbench.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Radim Kolar 1997-2004 3 | * This is free software, see GNU Public License version 2 for 4 | * details. 5 | * 6 | * Simple forking WWW Server benchmark: 7 | * 8 | * Usage: 9 | * webbench --help 10 | * 11 | * Return codes: 12 | * 0 - sucess 13 | * 1 - benchmark failed (server is not on-line) 14 | * 2 - bad param 15 | * 3 - internal error, fork failed 16 | * 17 | */ 18 | #include "socket.c" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /* values */ 28 | volatile int timerexpired=0; 29 | int speed=0; 30 | int failed=0; 31 | int bytes=0; 32 | /* globals */ 33 | int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */ 34 | /* Allow: GET, HEAD, OPTIONS, TRACE */ 35 | #define METHOD_GET 0 36 | #define METHOD_HEAD 1 37 | #define METHOD_OPTIONS 2 38 | #define METHOD_TRACE 3 39 | #define PROGRAM_VERSION "1.5" 40 | int method=METHOD_GET; 41 | int clients=1; 42 | int force=0; 43 | int force_reload=0; 44 | int proxyport=80; 45 | char *proxyhost=NULL; 46 | int benchtime=30; 47 | /* internal */ 48 | int mypipe[2]; 49 | char host[MAXHOSTNAMELEN]; 50 | #define REQUEST_SIZE 2048 51 | char request[REQUEST_SIZE]; 52 | 53 | static const struct option long_options[]= 54 | { 55 | {"force",no_argument,&force,1}, 56 | {"reload",no_argument,&force_reload,1}, 57 | {"time",required_argument,NULL,'t'}, 58 | {"help",no_argument,NULL,'?'}, 59 | {"http09",no_argument,NULL,'9'}, 60 | {"http10",no_argument,NULL,'1'}, 61 | {"http11",no_argument,NULL,'2'}, 62 | {"get",no_argument,&method,METHOD_GET}, 63 | {"head",no_argument,&method,METHOD_HEAD}, 64 | {"options",no_argument,&method,METHOD_OPTIONS}, 65 | {"trace",no_argument,&method,METHOD_TRACE}, 66 | {"version",no_argument,NULL,'V'}, 67 | {"proxy",required_argument,NULL,'p'}, 68 | {"clients",required_argument,NULL,'c'}, 69 | {NULL,0,NULL,0} 70 | }; 71 | 72 | /* prototypes */ 73 | static void benchcore(const char* host,const int port, const char *request); 74 | static int bench(void); 75 | static void build_request(const char *url); 76 | 77 | static void alarm_handler(int signal) 78 | { 79 | timerexpired=1; 80 | } 81 | 82 | static void usage(void) 83 | { 84 | fprintf(stderr, 85 | "webbench [option]... URL\n" 86 | " -f|--force Don't wait for reply from server.\n" 87 | " -r|--reload Send reload request - Pragma: no-cache.\n" 88 | " -t|--time Run benchmark for seconds. Default 30.\n" 89 | " -p|--proxy Use proxy server for request.\n" 90 | " -c|--clients Run HTTP clients at once. Default one.\n" 91 | " -9|--http09 Use HTTP/0.9 style requests.\n" 92 | " -1|--http10 Use HTTP/1.0 protocol.\n" 93 | " -2|--http11 Use HTTP/1.1 protocol.\n" 94 | " --get Use GET request method.\n" 95 | " --head Use HEAD request method.\n" 96 | " --options Use OPTIONS request method.\n" 97 | " --trace Use TRACE request method.\n" 98 | " -?|-h|--help This information.\n" 99 | " -V|--version Display program version.\n" 100 | ); 101 | }; 102 | int main(int argc, char *argv[]) 103 | { 104 | int opt=0; 105 | int options_index=0; 106 | char *tmp=NULL; 107 | 108 | if(argc==1) 109 | { 110 | usage(); 111 | return 2; 112 | } 113 | 114 | while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF ) 115 | { 116 | switch(opt) 117 | { 118 | case 0 : break; 119 | case 'f': force=1;break; 120 | case 'r': force_reload=1;break; 121 | case '9': http10=0;break; 122 | case '1': http10=1;break; 123 | case '2': http10=2;break; 124 | case 'V': printf(PROGRAM_VERSION"\n");exit(0); 125 | case 't': benchtime=atoi(optarg);break; 126 | case 'p': 127 | /* proxy server parsing server:port */ 128 | tmp=strrchr(optarg,':'); 129 | proxyhost=optarg; 130 | if(tmp==NULL) 131 | { 132 | break; 133 | } 134 | if(tmp==optarg) 135 | { 136 | fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg); 137 | return 2; 138 | } 139 | if(tmp==optarg+strlen(optarg)-1) 140 | { 141 | fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg); 142 | return 2; 143 | } 144 | *tmp='\0'; 145 | proxyport=atoi(tmp+1);break; 146 | case ':': 147 | case 'h': 148 | case '?': usage();return 2;break; 149 | case 'c': clients=atoi(optarg);break; 150 | } 151 | } 152 | 153 | if(optind==argc) { 154 | fprintf(stderr,"webbench: Missing URL!\n"); 155 | usage(); 156 | return 2; 157 | } 158 | 159 | if(clients==0) clients=1; 160 | if(benchtime==0) benchtime=60; 161 | /* Copyright */ 162 | fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n" 163 | "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n" 164 | ); 165 | build_request(argv[optind]); 166 | /* print bench info */ 167 | printf("\nBenchmarking: "); 168 | switch(method) 169 | { 170 | case METHOD_GET: 171 | default: 172 | printf("GET");break; 173 | case METHOD_OPTIONS: 174 | printf("OPTIONS");break; 175 | case METHOD_HEAD: 176 | printf("HEAD");break; 177 | case METHOD_TRACE: 178 | printf("TRACE");break; 179 | } 180 | printf(" %s",argv[optind]); 181 | switch(http10) 182 | { 183 | case 0: printf(" (using HTTP/0.9)");break; 184 | case 2: printf(" (using HTTP/1.1)");break; 185 | } 186 | printf("\n"); 187 | if(clients==1) printf("1 client"); 188 | else 189 | printf("%d clients",clients); 190 | 191 | printf(", running %d sec", benchtime); 192 | if(force) printf(", early socket close"); 193 | if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport); 194 | if(force_reload) printf(", forcing reload"); 195 | printf(".\n"); 196 | return bench(); 197 | } 198 | 199 | void build_request(const char *url) 200 | { 201 | char tmp[10]; 202 | int i; 203 | 204 | bzero(host,MAXHOSTNAMELEN); 205 | bzero(request,REQUEST_SIZE); 206 | 207 | if(force_reload && proxyhost!=NULL && http10<1) http10=1; 208 | if(method==METHOD_HEAD && http10<1) http10=1; 209 | if(method==METHOD_OPTIONS && http10<2) http10=2; 210 | if(method==METHOD_TRACE && http10<2) http10=2; 211 | 212 | switch(method) 213 | { 214 | default: 215 | case METHOD_GET: strcpy(request,"GET");break; 216 | case METHOD_HEAD: strcpy(request,"HEAD");break; 217 | case METHOD_OPTIONS: strcpy(request,"OPTIONS");break; 218 | case METHOD_TRACE: strcpy(request,"TRACE");break; 219 | } 220 | 221 | strcat(request," "); 222 | 223 | if(NULL==strstr(url,"://")) 224 | { 225 | fprintf(stderr, "\n%s: is not a valid URL.\n",url); 226 | exit(2); 227 | } 228 | if(strlen(url)>1500) 229 | { 230 | fprintf(stderr,"URL is too long.\n"); 231 | exit(2); 232 | } 233 | if(proxyhost==NULL) 234 | if (0!=strncasecmp("http://",url,7)) 235 | { fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n"); 236 | exit(2); 237 | } 238 | /* protocol/host delimiter */ 239 | i=strstr(url,"://")-url+3; 240 | /* printf("%d\n",i); */ 241 | 242 | if(strchr(url+i,'/')==NULL) { 243 | fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n"); 244 | exit(2); 245 | } 246 | if(proxyhost==NULL) 247 | { 248 | /* get port from hostname */ 249 | if(index(url+i,':')!=NULL && 250 | index(url+i,':')0) 275 | strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n"); 276 | if(proxyhost==NULL && http10>0) 277 | { 278 | strcat(request,"Host: "); 279 | strcat(request,host); 280 | strcat(request,"\r\n"); 281 | } 282 | if(force_reload && proxyhost!=NULL) 283 | { 284 | strcat(request,"Pragma: no-cache\r\n"); 285 | } 286 | if(http10>1) 287 | strcat(request,"Connection: close\r\n"); 288 | /* add empty line at end */ 289 | if(http10>0) strcat(request,"\r\n"); 290 | // printf("Req=%s\n",request); 291 | } 292 | 293 | /* vraci system rc error kod */ 294 | static int bench(void) 295 | { 296 | int i,j,k; 297 | pid_t pid=0; 298 | FILE *f; 299 | 300 | /* check avaibility of target server */ 301 | i=Socket(proxyhost==NULL?host:proxyhost,proxyport); 302 | if(i<0) { 303 | fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n"); 304 | return 1; 305 | } 306 | close(i); 307 | /* create pipe */ 308 | if(pipe(mypipe)) 309 | { 310 | perror("pipe failed."); 311 | return 3; 312 | } 313 | 314 | /* not needed, since we have alarm() in childrens */ 315 | /* wait 4 next system clock tick */ 316 | /* 317 | cas=time(NULL); 318 | while(time(NULL)==cas) 319 | sched_yield(); 320 | */ 321 | 322 | /* fork childs */ 323 | for(i=0;i0) 418 | { 419 | /* fprintf(stderr,"Correcting failed by signal\n"); */ 420 | failed--; 421 | } 422 | return; 423 | } 424 | s=Socket(host,port); 425 | if(s<0) { failed++;continue;} 426 | if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;} 427 | if(http10==0) 428 | if(shutdown(s,1)) { failed++;close(s);continue;} 429 | if(force==0) 430 | { 431 | /* read all available data from socket */ 432 | while(1) 433 | { 434 | if(timerexpired) break; 435 | i=read(s,buf,1500); 436 | /* fprintf(stderr,"%d\n",i); */ 437 | if(i<0) 438 | { 439 | failed++; 440 | close(s); 441 | goto nexttry; 442 | } 443 | else 444 | if(i==0) break; 445 | else 446 | bytes+=i; 447 | } 448 | } 449 | if(close(s)) {failed++;continue;} 450 | speed++; 451 | } 452 | } 453 | -------------------------------------------------------------------------------- /test_presure/webbench-1.5/webbench.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeshZhao/TinyWebServerRedis/9a679dc6fb9b5e2a13d675c38ebdc96e9f881487/test_presure/webbench-1.5/webbench.o -------------------------------------------------------------------------------- /threadpool/README.md: -------------------------------------------------------------------------------- 1 | 2 | 半同步/半反应堆线程池 3 | =============== 4 | 使用一个工作队列完全解除了主线程和工作线程的耦合关系:主线程往工作队列中插入任务,工作线程通过竞争来取得任务并执行它。 5 | > * 同步I/O模拟proactor模式 6 | > * 半同步/半反应堆 7 | > * 线程池 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /threadpool/threadpool.h: -------------------------------------------------------------------------------- 1 | #ifndef THREADPOOL_H 2 | #define THREADPOOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "../lock/locker.h" 9 | #include "../webserver.h" 10 | #include "../CGImysql/sql_connection_pool.h" 11 | #include "../CGIRedis/redis_connection_pool.h" 12 | 13 | template 14 | class threadpool 15 | { 16 | public: 17 | /*thread_number是线程池中线程的数量,max_requests是请求队列中最多允许的、等待处理的请求的数量*/ 18 | threadpool(int actor_model, connection_pool *connPool, RedisConnectionPool *redisPool, int thread_number = 8, int max_request = 10000); 19 | ~threadpool(); 20 | bool append(T *request, int state); 21 | bool append_p(T *request); 22 | 23 | private: 24 | /*工作线程运行的函数,它不断从工作队列中取出任务并执行之*/ 25 | static void *worker(void *arg); 26 | void run(); 27 | 28 | private: 29 | int m_thread_number; //线程池中的线程数 30 | int m_max_requests; //请求队列中允许的最大请求数 31 | pthread_t *m_threads; //描述线程池的数组,其大小为m_thread_number 32 | std::list m_workqueue; //请求队列 33 | locker m_queuelocker; //保护请求队列的互斥锁 34 | sem m_queuestat; //是否有任务需要处理 35 | connection_pool *m_connPool; //数据库 36 | RedisConnectionPool *m_redisPool; //redis连接池 37 | int m_actor_model; //模型切换 38 | }; 39 | template 40 | threadpool::threadpool( int actor_model, connection_pool *connPool, RedisConnectionPool *redisPool, int thread_number, int max_requests) : m_actor_model(actor_model),m_thread_number(thread_number), m_max_requests(max_requests), m_threads(NULL), m_connPool(connPool), m_redisPool(redisPool) 41 | { 42 | if (thread_number <= 0 || max_requests <= 0) 43 | throw std::exception(); 44 | m_threads = new pthread_t[m_thread_number]; 45 | if (!m_threads) 46 | throw std::exception(); 47 | for (int i = 0; i < thread_number; ++i) 48 | { 49 | if (pthread_create(m_threads + i, NULL, worker, this) != 0) 50 | { 51 | delete[] m_threads; 52 | throw std::exception(); 53 | } 54 | if (pthread_detach(m_threads[i])) 55 | { 56 | delete[] m_threads; 57 | throw std::exception(); 58 | } 59 | } 60 | } 61 | template 62 | threadpool::~threadpool() 63 | { 64 | delete[] m_threads; 65 | } 66 | template 67 | bool threadpool::append(T *request, int state) 68 | { 69 | m_queuelocker.lock(); 70 | if (m_workqueue.size() >= m_max_requests) 71 | { 72 | m_queuelocker.unlock(); 73 | return false; 74 | } 75 | request->m_state = state; 76 | m_workqueue.push_back(request); 77 | m_queuelocker.unlock(); 78 | m_queuestat.post(); 79 | return true; 80 | } 81 | template 82 | bool threadpool::append_p(T *request) 83 | { 84 | m_queuelocker.lock(); 85 | if (m_workqueue.size() >= m_max_requests) 86 | { 87 | m_queuelocker.unlock(); 88 | return false; 89 | } 90 | m_workqueue.push_back(request); 91 | m_queuelocker.unlock(); 92 | m_queuestat.post(); 93 | return true; 94 | } 95 | template 96 | void *threadpool::worker(void *arg) 97 | { 98 | threadpool *pool = (threadpool *)arg; 99 | pool->run(); 100 | return pool; 101 | } 102 | template 103 | void threadpool::run() 104 | { 105 | while (true) 106 | { 107 | m_queuestat.wait(); 108 | m_queuelocker.lock(); 109 | if (m_workqueue.empty()) 110 | { 111 | m_queuelocker.unlock(); 112 | continue; 113 | } 114 | T *request = m_workqueue.front(); 115 | m_workqueue.pop_front(); 116 | m_queuelocker.unlock(); 117 | if (!request) 118 | continue; 119 | if (1 == m_actor_model) 120 | { 121 | if (0 == request->m_state) 122 | { 123 | if (request->read_once()) 124 | { 125 | request->improv = 1; 126 | if(m_redisPool!=NULL && m_connPool==NULL) 127 | { 128 | RedisConnectionRAII rediscon(&request->redis, m_redisPool); 129 | } 130 | else 131 | { 132 | connectionRAII mysqlcon(&request->mysql, m_connPool); 133 | } 134 | request->process(); 135 | } 136 | else 137 | { 138 | request->improv = 1; 139 | request->timer_flag = 1; 140 | } 141 | } 142 | else 143 | { 144 | if (request->write()) 145 | { 146 | request->improv = 1; 147 | } 148 | else 149 | { 150 | request->improv = 1; 151 | request->timer_flag = 1; 152 | } 153 | } 154 | } 155 | else 156 | { 157 | if(m_redisPool!=NULL && m_connPool==NULL) 158 | { 159 | RedisConnectionRAII rediscon(&request->redis, m_redisPool); 160 | request->localRedisConn = request->redis->m_pContext; 161 | } 162 | else 163 | { 164 | connectionRAII mysqlcon(&request->mysql, m_connPool); 165 | } 166 | request->process(); 167 | } 168 | } 169 | } 170 | #endif 171 | -------------------------------------------------------------------------------- /timer/README.md: -------------------------------------------------------------------------------- 1 | 2 | 定时器处理非活动连接 3 | =============== 4 | 由于非活跃连接占用了连接资源,严重影响服务器的性能,通过实现一个服务器定时器,处理这种非活跃连接,释放连接资源。利用alarm函数周期性地触发SIGALRM信号,该信号的信号处理函数利用管道通知主循环执行定时器链表上的定时任务. 5 | > * 统一事件源 6 | > * 基于升序链表的定时器 7 | > * 处理非活动连接 8 | -------------------------------------------------------------------------------- /timer/lst_timer.cpp: -------------------------------------------------------------------------------- 1 | #include "lst_timer.h" 2 | #include "../http/http_conn.h" 3 | 4 | sort_timer_lst::sort_timer_lst() 5 | { 6 | head = NULL; 7 | tail = NULL; 8 | } 9 | sort_timer_lst::~sort_timer_lst() 10 | { 11 | util_timer *tmp = head; 12 | while (tmp) 13 | { 14 | head = tmp->next; 15 | delete tmp; 16 | tmp = head; 17 | } 18 | } 19 | 20 | void sort_timer_lst::add_timer(util_timer *timer) 21 | { 22 | if (!timer) 23 | { 24 | return; 25 | } 26 | if (!head) 27 | { 28 | head = tail = timer; 29 | return; 30 | } 31 | if (timer->expire < head->expire) 32 | { 33 | timer->next = head; 34 | head->prev = timer; 35 | head = timer; 36 | return; 37 | } 38 | add_timer(timer, head); 39 | } 40 | void sort_timer_lst::adjust_timer(util_timer *timer) 41 | { 42 | if (!timer) 43 | { 44 | return; 45 | } 46 | util_timer *tmp = timer->next; 47 | if (!tmp || (timer->expire < tmp->expire)) 48 | { 49 | return; 50 | } 51 | if (timer == head) 52 | { 53 | head = head->next; 54 | head->prev = NULL; 55 | timer->next = NULL; 56 | add_timer(timer, head); 57 | } 58 | else 59 | { 60 | timer->prev->next = timer->next; 61 | timer->next->prev = timer->prev; 62 | add_timer(timer, timer->next); 63 | } 64 | } 65 | void sort_timer_lst::del_timer(util_timer *timer) 66 | { 67 | if (!timer) 68 | { 69 | return; 70 | } 71 | if ((timer == head) && (timer == tail)) 72 | { 73 | delete timer; 74 | head = NULL; 75 | tail = NULL; 76 | return; 77 | } 78 | if (timer == head) 79 | { 80 | head = head->next; 81 | head->prev = NULL; 82 | delete timer; 83 | return; 84 | } 85 | if (timer == tail) 86 | { 87 | tail = tail->prev; 88 | tail->next = NULL; 89 | delete timer; 90 | return; 91 | } 92 | timer->prev->next = timer->next; 93 | timer->next->prev = timer->prev; 94 | delete timer; 95 | } 96 | void sort_timer_lst::tick() 97 | { 98 | if (!head) 99 | { 100 | return; 101 | } 102 | 103 | time_t cur = time(NULL); 104 | util_timer *tmp = head; 105 | while (tmp) 106 | { 107 | if (cur < tmp->expire) 108 | { 109 | break; 110 | } 111 | tmp->cb_func(tmp->user_data); 112 | head = tmp->next; 113 | if (head) 114 | { 115 | head->prev = NULL; 116 | } 117 | delete tmp; 118 | tmp = head; 119 | } 120 | } 121 | 122 | void sort_timer_lst::add_timer(util_timer *timer, util_timer *lst_head) 123 | { 124 | util_timer *prev = lst_head; 125 | util_timer *tmp = prev->next; 126 | while (tmp) 127 | { 128 | if (timer->expire < tmp->expire) 129 | { 130 | prev->next = timer; 131 | timer->next = tmp; 132 | tmp->prev = timer; 133 | timer->prev = prev; 134 | break; 135 | } 136 | prev = tmp; 137 | tmp = tmp->next; 138 | } 139 | if (!tmp) 140 | { 141 | prev->next = timer; 142 | timer->prev = prev; 143 | timer->next = NULL; 144 | tail = timer; 145 | } 146 | } 147 | 148 | void Utils::init(int timeslot) 149 | { 150 | m_TIMESLOT = timeslot; 151 | } 152 | 153 | //对文件描述符设置非阻塞 154 | int Utils::setnonblocking(int fd) 155 | { 156 | int old_option = fcntl(fd, F_GETFL); 157 | int new_option = old_option | O_NONBLOCK; 158 | fcntl(fd, F_SETFL, new_option); 159 | return old_option; 160 | } 161 | 162 | //将内核事件表注册读事件,ET模式,选择开启EPOLLONESHOT 163 | void Utils::addfd(int epollfd, int fd, bool one_shot, int TRIGMode) 164 | { 165 | epoll_event event; 166 | event.data.fd = fd; 167 | 168 | if (1 == TRIGMode) 169 | event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; 170 | else 171 | event.events = EPOLLIN | EPOLLRDHUP; 172 | 173 | if (one_shot) 174 | event.events |= EPOLLONESHOT; 175 | epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event); 176 | setnonblocking(fd); 177 | } 178 | 179 | //信号处理函数 180 | void Utils::sig_handler(int sig) 181 | { 182 | //为保证函数的可重入性,保留原来的errno 183 | int save_errno = errno; 184 | int msg = sig; 185 | send(u_pipefd[1], (char *)&msg, 1, 0); 186 | errno = save_errno; 187 | } 188 | 189 | //设置信号函数 190 | void Utils::addsig(int sig, void(handler)(int), bool restart) 191 | { 192 | struct sigaction sa; 193 | memset(&sa, '\0', sizeof(sa)); 194 | sa.sa_handler = handler; 195 | if (restart) 196 | sa.sa_flags |= SA_RESTART; 197 | sigfillset(&sa.sa_mask); 198 | assert(sigaction(sig, &sa, NULL) != -1); 199 | } 200 | 201 | //定时处理任务,重新定时以不断触发SIGALRM信号 202 | void Utils::timer_handler() 203 | { 204 | m_timer_lst.tick(); 205 | alarm(m_TIMESLOT); 206 | } 207 | 208 | void Utils::show_error(int connfd, const char *info) 209 | { 210 | send(connfd, info, strlen(info), 0); 211 | close(connfd); 212 | } 213 | 214 | int *Utils::u_pipefd = 0; 215 | int Utils::u_epollfd = 0; 216 | 217 | class Utils; 218 | void cb_func(client_data *user_data) 219 | { 220 | epoll_ctl(Utils::u_epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0); 221 | assert(user_data); 222 | close(user_data->sockfd); 223 | http_conn::m_user_count--; 224 | } 225 | -------------------------------------------------------------------------------- /timer/lst_timer.h: -------------------------------------------------------------------------------- 1 | #ifndef LST_TIMER 2 | #define LST_TIMER 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include "../log/log.h" 26 | 27 | class util_timer; 28 | 29 | struct client_data 30 | { 31 | sockaddr_in address; 32 | int sockfd; 33 | util_timer *timer; 34 | }; 35 | 36 | class util_timer 37 | { 38 | public: 39 | util_timer() : prev(NULL), next(NULL) {} 40 | 41 | public: 42 | time_t expire; 43 | 44 | void (* cb_func)(client_data *); 45 | client_data *user_data; 46 | util_timer *prev; 47 | util_timer *next; 48 | }; 49 | 50 | class sort_timer_lst 51 | { 52 | public: 53 | sort_timer_lst(); 54 | ~sort_timer_lst(); 55 | 56 | void add_timer(util_timer *timer); 57 | void adjust_timer(util_timer *timer); 58 | void del_timer(util_timer *timer); 59 | void tick(); 60 | 61 | private: 62 | void add_timer(util_timer *timer, util_timer *lst_head); 63 | 64 | util_timer *head; 65 | util_timer *tail; 66 | }; 67 | 68 | class Utils 69 | { 70 | public: 71 | Utils() {} 72 | ~Utils() {} 73 | 74 | void init(int timeslot); 75 | 76 | //对文件描述符设置非阻塞 77 | int setnonblocking(int fd); 78 | 79 | //将内核事件表注册读事件,ET模式,选择开启EPOLLONESHOT 80 | void addfd(int epollfd, int fd, bool one_shot, int TRIGMode); 81 | 82 | //信号处理函数 83 | static void sig_handler(int sig); 84 | 85 | //设置信号函数 86 | void addsig(int sig, void(handler)(int), bool restart = true); 87 | 88 | //定时处理任务,重新定时以不断触发SIGALRM信号 89 | void timer_handler(); 90 | 91 | void show_error(int connfd, const char *info); 92 | 93 | public: 94 | static int *u_pipefd; 95 | sort_timer_lst m_timer_lst; 96 | static int u_epollfd; 97 | int m_TIMESLOT; 98 | }; 99 | 100 | void cb_func(client_data *user_data); 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /webserver.cpp: -------------------------------------------------------------------------------- 1 | #include "webserver.h" 2 | 3 | WebServer::WebServer() 4 | { 5 | //http_conn类对象 6 | users = new http_conn[MAX_FD]; 7 | 8 | //root文件夹路径 9 | char server_path[200]; 10 | getcwd(server_path, 200); 11 | char root[6] = "/root"; 12 | m_root = (char *)malloc(strlen(server_path) + strlen(root) + 1); 13 | strcpy(m_root, server_path); 14 | strcat(m_root, root); 15 | 16 | //定时器 17 | users_timer = new client_data[MAX_FD]; 18 | } 19 | 20 | WebServer::~WebServer() 21 | { 22 | close(m_epollfd); 23 | close(m_listenfd); 24 | close(m_pipefd[1]); 25 | close(m_pipefd[0]); 26 | delete[] users; 27 | delete[] users_timer; 28 | delete m_pool; 29 | } 30 | 31 | void WebServer::init(int port, string user, string passWord, string databaseName, int log_write, 32 | int opt_linger, int trigmode, int sql_num, int redis_num, int thread_num, int close_log, int actor_model) 33 | { 34 | m_port = port; 35 | m_user = user; 36 | m_passWord = passWord; 37 | m_databaseName = databaseName; 38 | m_sql_num = sql_num; 39 | m_redis_num=redis_num; 40 | m_thread_num = thread_num; 41 | m_log_write = log_write; 42 | m_OPT_LINGER = opt_linger; 43 | m_TRIGMode = trigmode; 44 | m_close_log = close_log; 45 | m_actormodel = actor_model; 46 | } 47 | 48 | void WebServer::trig_mode() 49 | { 50 | //LT + LT 51 | if (0 == m_TRIGMode) 52 | { 53 | m_LISTENTrigmode = 0; 54 | m_CONNTrigmode = 0; 55 | } 56 | //LT + ET 57 | else if (1 == m_TRIGMode) 58 | { 59 | m_LISTENTrigmode = 0; 60 | m_CONNTrigmode = 1; 61 | } 62 | //ET + LT 63 | else if (2 == m_TRIGMode) 64 | { 65 | m_LISTENTrigmode = 1; 66 | m_CONNTrigmode = 0; 67 | } 68 | //ET + ET 69 | else if (3 == m_TRIGMode) 70 | { 71 | m_LISTENTrigmode = 1; 72 | m_CONNTrigmode = 1; 73 | } 74 | } 75 | 76 | void WebServer::log_write() 77 | { 78 | if (0 == m_close_log) 79 | { 80 | //初始化日志 81 | if (1 == m_log_write) 82 | Log::get_instance()->init("./ServerLog", m_close_log, 2000, 800000, 800); 83 | else 84 | Log::get_instance()->init("./ServerLog", m_close_log, 2000, 800000, 0); 85 | } 86 | } 87 | 88 | void WebServer::sql_pool() 89 | { 90 | //初始化数据库连接池 91 | connection_pool *m_connPool = connection_pool::GetInstance(); 92 | m_connPool->init("localhost", m_user, m_passWord, m_databaseName, 3306, m_sql_num, m_close_log); 93 | 94 | //初始化数据库读取表 95 | users->initmysql_result(m_connPool); 96 | } 97 | 98 | void WebServer::redis_pool() 99 | { 100 | //初始化数据库连接池 101 | m_redisPool = RedisConnectionPool::RedisPoolInstance();//fatal error when local ptr 102 | m_redisPool->init("localhost", m_user, m_passWord, m_databaseName, 6379, m_redis_num, m_close_log); 103 | 104 | //初始化数据库读取表 105 | users->initRedis_result(m_redisPool, m_close_log); 106 | } 107 | 108 | void WebServer::thread_pool() 109 | { 110 | //线程池 111 | if(m_redis_num>0) 112 | { 113 | m_pool = new threadpool(m_actormodel, NULL, m_redisPool, m_thread_num); 114 | } 115 | else 116 | { 117 | m_pool = new threadpool(m_actormodel, m_connPool, NULL, m_thread_num); 118 | } 119 | } 120 | 121 | void WebServer::eventListen() 122 | { 123 | //网络编程基础步骤 124 | m_listenfd = socket(PF_INET, SOCK_STREAM, 0); 125 | assert(m_listenfd >= 0); 126 | 127 | //优雅关闭连接 128 | if (0 == m_OPT_LINGER) 129 | { 130 | struct linger tmp = {0, 1}; 131 | setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp)); 132 | } 133 | else if (1 == m_OPT_LINGER) 134 | { 135 | struct linger tmp = {1, 1}; 136 | setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp)); 137 | } 138 | 139 | int ret = 0; 140 | struct sockaddr_in address; 141 | bzero(&address, sizeof(address)); 142 | address.sin_family = AF_INET; 143 | address.sin_addr.s_addr = htonl(INADDR_ANY); 144 | address.sin_port = htons(m_port); 145 | 146 | int flag = 1; 147 | setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); 148 | ret = bind(m_listenfd, (struct sockaddr *)&address, sizeof(address)); 149 | assert(ret >= 0); 150 | ret = listen(m_listenfd, 5); 151 | assert(ret >= 0); 152 | 153 | utils.init(TIMESLOT); 154 | 155 | //epoll创建内核事件表 156 | epoll_event events[MAX_EVENT_NUMBER]; 157 | m_epollfd = epoll_create(5); 158 | assert(m_epollfd != -1); 159 | 160 | utils.addfd(m_epollfd, m_listenfd, false, m_LISTENTrigmode); 161 | http_conn::m_epollfd = m_epollfd; 162 | 163 | ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_pipefd); 164 | assert(ret != -1); 165 | utils.setnonblocking(m_pipefd[1]); 166 | utils.addfd(m_epollfd, m_pipefd[0], false, 0); 167 | 168 | utils.addsig(SIGPIPE, SIG_IGN); 169 | utils.addsig(SIGALRM, utils.sig_handler, false); 170 | utils.addsig(SIGTERM, utils.sig_handler, false); 171 | 172 | alarm(TIMESLOT); 173 | 174 | //工具类,信号和描述符基础操作 175 | Utils::u_pipefd = m_pipefd; 176 | Utils::u_epollfd = m_epollfd; 177 | } 178 | 179 | void WebServer::timer(int connfd, struct sockaddr_in client_address) 180 | { 181 | users[connfd].init(connfd, client_address, m_root, m_CONNTrigmode, m_close_log, m_user, m_passWord, m_databaseName); 182 | 183 | //初始化client_data数据 184 | //创建定时器,设置回调函数和超时时间,绑定用户数据,将定时器添加到链表中 185 | users_timer[connfd].address = client_address; 186 | users_timer[connfd].sockfd = connfd; 187 | util_timer *timer = new util_timer; 188 | timer->user_data = &users_timer[connfd]; 189 | timer->cb_func = cb_func; 190 | time_t cur = time(NULL); 191 | timer->expire = cur + 3 * TIMESLOT; 192 | users_timer[connfd].timer = timer; 193 | utils.m_timer_lst.add_timer(timer); 194 | } 195 | 196 | //若有数据传输,则将定时器往后延迟3个单位 197 | //并对新的定时器在链表上的位置进行调整 198 | void WebServer::adjust_timer(util_timer *timer) 199 | { 200 | time_t cur = time(NULL); 201 | timer->expire = cur + 3 * TIMESLOT; 202 | utils.m_timer_lst.adjust_timer(timer); 203 | 204 | LOG_INFO("%s", "adjust timer once"); 205 | } 206 | 207 | void WebServer::deal_timer(util_timer *timer, int sockfd) 208 | { 209 | timer->cb_func(&users_timer[sockfd]); 210 | if (timer) 211 | { 212 | utils.m_timer_lst.del_timer(timer); 213 | } 214 | 215 | LOG_INFO("close fd %d", users_timer[sockfd].sockfd); 216 | } 217 | 218 | bool WebServer::dealclinetdata() 219 | { 220 | struct sockaddr_in client_address; 221 | socklen_t client_addrlength = sizeof(client_address); 222 | if (0 == m_LISTENTrigmode)//LT 223 | { 224 | int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength); 225 | if (connfd < 0) 226 | { 227 | LOG_ERROR("%s:errno is:%d", "accept error", errno); 228 | return false; 229 | } 230 | if (http_conn::m_user_count >= MAX_FD) 231 | { 232 | utils.show_error(connfd, "Internal server busy"); 233 | LOG_ERROR("%s", "Internal server busy"); 234 | return false; 235 | } 236 | timer(connfd, client_address); 237 | } 238 | 239 | else 240 | { 241 | while (1) 242 | { 243 | int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength); 244 | if (connfd < 0) 245 | { 246 | LOG_ERROR("%s:errno is:%d", "accept error", errno); 247 | break; 248 | } 249 | if (http_conn::m_user_count >= MAX_FD) 250 | { 251 | utils.show_error(connfd, "Internal server busy"); 252 | LOG_ERROR("%s", "Internal server busy"); 253 | break; 254 | } 255 | timer(connfd, client_address); 256 | } 257 | return false; 258 | } 259 | return true; 260 | } 261 | 262 | bool WebServer::dealwithsignal(bool &timeout, bool &stop_server) 263 | { 264 | int ret = 0; 265 | int sig; 266 | char signals[1024]; 267 | ret = recv(m_pipefd[0], signals, sizeof(signals), 0); 268 | if (ret == -1) 269 | { 270 | return false; 271 | } 272 | else if (ret == 0) 273 | { 274 | return false; 275 | } 276 | else 277 | { 278 | for (int i = 0; i < ret; ++i) 279 | { 280 | switch (signals[i]) 281 | { 282 | case SIGALRM: 283 | { 284 | timeout = true; 285 | break; 286 | } 287 | case SIGTERM: 288 | { 289 | stop_server = true; 290 | break; 291 | } 292 | } 293 | } 294 | } 295 | return true; 296 | } 297 | 298 | void WebServer::dealwithread(int sockfd) 299 | { 300 | util_timer *timer = users_timer[sockfd].timer; 301 | 302 | //reactor 303 | if (1 == m_actormodel) 304 | { 305 | if (timer) 306 | { 307 | adjust_timer(timer); 308 | } 309 | 310 | //若监测到读事件,将该事件放入请求队列 311 | m_pool->append(users + sockfd, 0); 312 | 313 | while (true) 314 | { 315 | if (1 == users[sockfd].improv) 316 | { 317 | if (1 == users[sockfd].timer_flag) 318 | { 319 | deal_timer(timer, sockfd);//cb_func=& read_cb* 320 | users[sockfd].timer_flag = 0; 321 | } 322 | users[sockfd].improv = 0; 323 | break; 324 | } 325 | } 326 | } 327 | else 328 | { 329 | //proactor 330 | if (users[sockfd].read_once()) 331 | { 332 | LOG_INFO("deal with the client(%s)", inet_ntoa(users[sockfd].get_address()->sin_addr)); 333 | 334 | //若监测到读事件,将该事件放入请求队列 335 | m_pool->append_p(users + sockfd); 336 | 337 | if (timer) 338 | { 339 | adjust_timer(timer); 340 | } 341 | } 342 | else 343 | { 344 | deal_timer(timer, sockfd); 345 | } 346 | } 347 | } 348 | 349 | void WebServer::dealwithwrite(int sockfd) 350 | { 351 | util_timer *timer = users_timer[sockfd].timer; 352 | //reactor 353 | if (1 == m_actormodel) 354 | { 355 | if (timer) 356 | { 357 | adjust_timer(timer); 358 | } 359 | 360 | m_pool->append(users + sockfd, 1); 361 | 362 | while (true) 363 | { 364 | if (1 == users[sockfd].improv) 365 | { 366 | if (1 == users[sockfd].timer_flag) 367 | { 368 | deal_timer(timer, sockfd); 369 | users[sockfd].timer_flag = 0; 370 | } 371 | users[sockfd].improv = 0; 372 | break; 373 | } 374 | } 375 | } 376 | else 377 | { 378 | //proactor 379 | if (users[sockfd].write()) 380 | { 381 | LOG_INFO("send data to the client(%s)", inet_ntoa(users[sockfd].get_address()->sin_addr)); 382 | 383 | if (timer) 384 | { 385 | adjust_timer(timer); 386 | } 387 | } 388 | else 389 | { 390 | deal_timer(timer, sockfd); 391 | } 392 | } 393 | } 394 | 395 | void WebServer::eventLoop() 396 | { 397 | bool timeout = false; 398 | bool stop_server = false; 399 | 400 | while (!stop_server) 401 | { 402 | int number = epoll_wait(m_epollfd, events, MAX_EVENT_NUMBER, -1); 403 | if (number < 0 && errno != EINTR) //忽略gdb系统调用 404 | { 405 | LOG_ERROR("%s", "epoll failure"); 406 | break; 407 | } 408 | 409 | for (int i = 0; i < number; i++) 410 | { 411 | int sockfd = events[i].data.fd; 412 | 413 | //处理新到的客户连接 414 | if (sockfd == m_listenfd) 415 | { 416 | bool flag = dealclinetdata(); 417 | if (false == flag) 418 | continue; 419 | } 420 | else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) 421 | { 422 | //服务器端关闭连接,移除对应的定时器 423 | util_timer *timer = users_timer[sockfd].timer; 424 | deal_timer(timer, sockfd); 425 | } 426 | //处理信号 427 | else if ((sockfd == m_pipefd[0]) && (events[i].events & EPOLLIN)) 428 | { 429 | bool flag = dealwithsignal(timeout, stop_server); 430 | if (false == flag) 431 | LOG_ERROR("%s", "dealclientdata failure"); 432 | } 433 | //处理客户连接上接收到的数据 434 | else if (events[i].events & EPOLLIN) 435 | { 436 | dealwithread(sockfd);//int idx=i/len; int j=i%len; 437 | } 438 | else if (events[i].events & EPOLLOUT) 439 | { 440 | dealwithwrite(sockfd); 441 | } 442 | } 443 | if (timeout) 444 | { 445 | utils.timer_handler(); 446 | 447 | LOG_INFO("%s", "timer tick"); 448 | 449 | timeout = false; 450 | } 451 | } 452 | } -------------------------------------------------------------------------------- /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 | #include 12 | #include 13 | #include 14 | 15 | #include "./threadpool/threadpool.h" 16 | #include "./http/http_conn.h" 17 | 18 | const int MAX_FD = 65536; //最大文件描述符 19 | const int MAX_EVENT_NUMBER = 10000; //最大事件数 20 | const int TIMESLOT = 5; //最小超时单位 21 | 22 | class WebServer 23 | { 24 | public: 25 | WebServer(); 26 | ~WebServer(); 27 | 28 | void init(int port , string user, string passWord, string databaseName, 29 | int log_write , int opt_linger, int trigmode, int sql_num, int redis_num, 30 | int thread_num, int close_log, int actor_model); 31 | 32 | void thread_pool(); 33 | void sql_pool(); 34 | void redis_pool(); 35 | void log_write(); 36 | void trig_mode(); 37 | void eventListen(); 38 | void eventLoop(); 39 | void timer(int connfd, struct sockaddr_in client_address); 40 | void adjust_timer(util_timer *timer); 41 | void deal_timer(util_timer *timer, int sockfd); 42 | bool dealclinetdata(); 43 | bool dealwithsignal(bool& timeout, bool& stop_server); 44 | void dealwithread(int sockfd); 45 | void dealwithwrite(int sockfd); 46 | 47 | public: 48 | //基础 49 | int m_port; 50 | char *m_root; 51 | int m_log_write; 52 | int m_close_log; 53 | int m_actormodel; 54 | 55 | int m_pipefd[2]; 56 | int m_epollfd; 57 | http_conn *users; 58 | 59 | //数据库相关 60 | connection_pool *m_connPool; 61 | RedisConnectionPool *m_redisPool; 62 | string m_user; //登陆数据库用户名 63 | string m_passWord; //登陆数据库密码 64 | string m_databaseName; //使用数据库名 65 | int m_sql_num; 66 | int m_redis_num; 67 | 68 | //线程池相关 69 | threadpool *m_pool; 70 | int m_thread_num; 71 | 72 | //epoll_event相关 73 | epoll_event events[MAX_EVENT_NUMBER]; 74 | 75 | int m_listenfd; 76 | int m_OPT_LINGER; 77 | int m_TRIGMode; 78 | int m_LISTENTrigmode; 79 | int m_CONNTrigmode; 80 | 81 | //定时器相关 82 | client_data *users_timer; 83 | Utils utils; 84 | }; 85 | #endif 86 | --------------------------------------------------------------------------------