diff --git a/CGImysql/id_passwd.txt b/CGImysql/id_passwd.txt new file mode 100644 index 0000000..fe1f413 --- /dev/null +++ b/CGImysql/id_passwd.txt @@ -0,0 +1,49 @@ +qgy qgy +123 123 +wts 123 +wrx wrx +xxx xxx +yyy yyy +456 567 +134 134 +xq xq +xxxx xxxx +xqq xqq +%E5%95%8A a +gsa gsa +bhu bhu +596 596 +256 256 +098 098 +qgy1 qgy1 +qgy0 qgy0 +qgy99 qgy99 +qgy9 qgy9 +ly9 ly9 +qgy qgy +qgy qgy +qgy78 qgy78 +qgy76 qgy76 +qgy789 qgy789 +yx0 yx1 +qgyqgyqgy qgyqgyqgy +xme xme +3698 3695 +0693 0693 +3698 3698 +3697 3697 +twomonkeysclub 123 +0328 0328 +6129 6129 +1996 1996 +abc 123 +0328 0328 +0328 0328 +0911 0911 +0912 0912 +123456 123456 +jsdd 123456 +1234 123 +wxg wxg +rrr rrr +234 234 diff --git a/CGImysql/sign.cpp b/CGImysql/sign.cpp index 9f4d856..b5fb617 100644 --- a/CGImysql/sign.cpp +++ b/CGImysql/sign.cpp @@ -1,90 +1,137 @@ -#include -#include -#include -#include -#include -#include"sql_connection_pool.h" -#include +#include +#include +#include +#include +#include +#include "sql_connection_pool.h" +#include +#include +#include using namespace std; -int main(int argc,char *argv[]) +int main(int argc, char *argv[]) { - map users; + map users; - pthread_mutex_t lock; - pthread_mutex_init(&lock, NULL); - - //初始化数据库连接池,连接池为静态大小 - //通过主机地址和登录账号,密码进入服务器数据库,选择qgydb库 - connection_pool *connPool=connection_pool::GetInstance("localhost","root","root","qgydb",3306,5); + locker lock; + string SQLVerify(argv[3]); - //在连接池中取一个连接 - MYSQL *mysql=connPool->GetConnection(); - //在user表中检索username,passwd数据,浏览器端输入 - if(mysql_query(mysql,"SELECT username,passwd FROM user")) + if ("2" == SQLVerify) { - printf("INSERT error:%s\n",mysql_error(mysql)); - return -1; - } - //从表中检索完整的结果集 - MYSQL_RES *result=mysql_store_result(mysql); - //返回结果集中的列数 - int num_fields=mysql_num_fields(result); - //返回所有字段结构的数组 - MYSQL_FIELD *fields=mysql_fetch_fields(result); + string sql_user(argv[4]); + string sql_passwd(argv[5]); + string sql_name(argv[6]); - //从结果集中获取下一行,将对应的用户名和密码,存入map中 - while(MYSQL_ROW row=mysql_fetch_row(result)) - { - string temp1(row[0]); - string temp2(row[1]); - users[temp1]=temp2; - } + MYSQL *con = NULL; + con = mysql_init(con); - string name(argv[1]); - const char *namep = name.c_str(); - string passwd(argv[2]); - const char *passwdp = passwd.c_str(); - char flag = *argv[0]; - - //如果是注册,先检测数据库中是否有重名的 - //没有重名的,进行增加数据 - char *sql_insert = (char*)malloc(sizeof(char)*200); - strcpy(sql_insert, "INSERT INTO user(username, passwd) VALUES("); - strcat(sql_insert, "'"); - strcat(sql_insert, namep); - strcat(sql_insert, "', '"); - strcat(sql_insert, passwdp); - strcat(sql_insert, "')"); - - if(flag == '3'){ - if(users.find(name)==users.end()){ + if (con == NULL) + { + cout << "Error:" << mysql_error(con); + exit(1); + } + con = mysql_real_connect(con, "localhost", sql_user.c_str(), sql_passwd.c_str(), sql_name.c_str(), 3306, NULL, 0); - pthread_mutex_lock(&lock); - int res = mysql_query(mysql,sql_insert); - pthread_mutex_unlock(&lock); + if (con == NULL) + { + cout << "Error: " << mysql_error(con); + exit(1); + } - if(!res) - printf("1\n"); - else - printf("0\n"); - } - else - printf("0\n"); - } - //如果是登录,直接判断 - //若浏览器端输入的用户名和密码在表中可以查找到,返回1,否则返回0 - else if(flag == '2'){ - if(users.find(name)!=users.end()&&users[name]==passwd) - printf("1\n"); - else - printf("0\n"); + //在user表中检索username,passwd数据,浏览器端输入 + if (mysql_query(con, "SELECT username,passwd FROM user")) + { + printf("INSERT error:%s\n", mysql_error(con)); + return -1; + } + //从表中检索完整的结果集 + MYSQL_RES *result = mysql_store_result(con); + //返回结果集中的列数 + int num_fields = mysql_num_fields(result); + //返回所有字段结构的数组 + MYSQL_FIELD *fields = mysql_fetch_fields(result); + + //从结果集中获取下一行,将对应的用户名和密码,存入map中 + while (MYSQL_ROW row = mysql_fetch_row(result)) + { + string temp1(row[0]); + string temp2(row[1]); + users[temp1] = temp2; + } + + string name(argv[1]); + const char *namep = name.c_str(); + string passwd(argv[2]); + const char *passwdp = passwd.c_str(); + char flag = *argv[0]; + + //如果是注册,先检测数据库中是否有重名的 + //没有重名的,进行增加数据 + char *sql_insert = (char *)malloc(sizeof(char) * 200); + strcpy(sql_insert, "INSERT INTO user(username, passwd) VALUES("); + strcat(sql_insert, "'"); + strcat(sql_insert, namep); + strcat(sql_insert, "', '"); + strcat(sql_insert, passwdp); + strcat(sql_insert, "')"); + + if (flag == '3') + { + if (users.find(name) == users.end()) + { + lock.lock(); + int res = mysql_query(con, sql_insert); + lock.unlock(); + + if (!res) + printf("1\n"); + else + printf("0\n"); + } + else + printf("0\n"); + } + //如果是登录,直接判断 + //若浏览器端输入的用户名和密码在表中可以查找到,返回1,否则返回0 + else if (flag == '2') + { + if (users.find(name) != users.end() && users[name] == passwd) + printf("1\n"); + else + printf("0\n"); + } + else + printf("0\n"); + //释放结果集使用的内存 + mysql_free_result(result); } else - printf("0\n"); - //释放结果集使用的内存 - mysql_free_result(result); + { + ifstream out(argv[2]); + string linestr; + while (getline(out, linestr)) + { + string str; + stringstream id_passwd(linestr); - connPool->DestroyPool(); -} + getline(id_passwd, str, ' '); + string temp1(str); + getline(id_passwd, str, ' '); + string temp2(str); + users[temp1] = temp2; + } + out.close(); + + //只完成登录 + string name(argv[0]); + const char *namep = name.c_str(); + string passwd(argv[1]); + const char *passwdp = passwd.c_str(); + + if (users.find(name) != users.end() && users[name] == passwd) + printf("1\n"); + else + printf("0\n"); + } +} diff --git a/CGImysql/sql_connection_pool.cpp b/CGImysql/sql_connection_pool.cpp index 2d841d5..558db0e 100644 --- a/CGImysql/sql_connection_pool.cpp +++ b/CGImysql/sql_connection_pool.cpp @@ -1,133 +1,127 @@ -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include "sql_connection_pool.h" using namespace std; -connection_pool* connection_pool::connPool = NULL; -static pthread_mutex_t mutex; +connection_pool::connection_pool() +{ + this->CurConn = 0; + this->FreeConn = 0; +} -//初始化mutex -connection_pool::connection_pool() +connection_pool *connection_pool::GetInstance() { - pthread_mutex_init(&mutex, NULL); + static connection_pool connPool; + return &connPool; } //构造初始化 -connection_pool::connection_pool(string url,string User,string PassWord,string DBName,int Port,unsigned int MaxConn) +void connection_pool::init(string url, string User, string PassWord, string DBName, int Port, unsigned int MaxConn) { this->url = url; this->Port = Port; this->User = User; this->PassWord = PassWord; - this->DatabaseName=DBName; + this->DatabaseName = DBName; - pthread_mutex_lock(&lock); - for(int i = 0; i < MaxConn; i++) + lock.lock(); + for (int i = 0; i < MaxConn; i++) { - MYSQL *con=NULL; - con=mysql_init(con); - - if(con == NULL) + MYSQL *con = NULL; + con = mysql_init(con); + + if (con == NULL) { - cout<<"Error:"<MaxConn = MaxConn; - this->CurConn = 0; - pthread_mutex_unlock(&lock); -} + reserve = sem(FreeConn); -//获得实例,只会有一个 -connection_pool* connection_pool::GetInstance(string url,string User,string PassWord,string DBName,int Port,unsigned int MaxConn) -{ - //先判断是否为空,若为空则创建,否则直接返回现有 - if(connPool == NULL) - { - pthread_mutex_lock(&mutex); - if(connPool == NULL) - { - connPool = new connection_pool(url,User,PassWord,DBName,Port,MaxConn); - } - pthread_mutex_unlock(&mutex); - } - return connPool; + this->MaxConn = FreeConn; + + lock.unlock(); } + //当有请求时,从数据库连接池中返回一个可用连接,更新使用和空闲连接数 -MYSQL* connection_pool::GetConnection() +MYSQL *connection_pool::GetConnection() { - MYSQL * con = NULL; - pthread_mutex_lock(&lock); - //reserve.wait(); - if(connList.size() > 0) - { - con = connList.front(); - connList.pop_front(); - - --FreeConn; - ++CurConn; - - pthread_mutex_unlock(&lock); - return con; - } - pthread_mutex_unlock(&lock); - return NULL; + MYSQL *con = NULL; + + if (0 == connList.size()) + return NULL; + + reserve.wait(); + + lock.lock(); + + con = connList.front(); + connList.pop_front(); + + --FreeConn; + ++CurConn; + + lock.unlock(); + return con; } //释放当前使用的连接 -bool connection_pool::ReleaseConnection(MYSQL * con) +bool connection_pool::ReleaseConnection(MYSQL *con) { - pthread_mutex_lock(&lock); - if(con != NULL) - { - connList.push_back(con); - ++FreeConn; - --CurConn; + if (NULL == con) + return false; - pthread_mutex_unlock(&lock); - //reserve.post(); - return true; - } - pthread_mutex_unlock(&lock); - return false; + lock.lock(); + + connList.push_back(con); + ++FreeConn; + --CurConn; + + lock.unlock(); + + reserve.post(); + return true; } //销毁数据库连接池 void connection_pool::DestroyPool() { - pthread_mutex_lock(&lock); - if(connList.size() > 0) + + lock.lock(); + if (connList.size() > 0) { list::iterator it; - for(it = connList.begin(); it != connList.end(); ++it) + for (it = connList.begin(); it != connList.end(); ++it) { - MYSQL * con = *it; + MYSQL *con = *it; mysql_close(con); } CurConn = 0; FreeConn = 0; connList.clear(); - pthread_mutex_unlock(&lock); + + lock.unlock(); } - pthread_mutex_unlock(&lock); + + lock.unlock(); } //当前空闲的连接数 @@ -136,8 +130,18 @@ int connection_pool::GetFreeConn() return this->FreeConn; } - connection_pool::~connection_pool() { DestroyPool(); } + +connectionRAII::connectionRAII(MYSQL **SQL, connection_pool *connPool){ + *SQL = connPool->GetConnection(); + + conRAII = *SQL; + poolRAII = connPool; +} + +connectionRAII::~connectionRAII(){ + poolRAII->ReleaseConnection(conRAII); +} \ No newline at end of file diff --git a/CGImysql/sql_connection_pool.h b/CGImysql/sql_connection_pool.h index 9e79c88..8dc2e10 100644 --- a/CGImysql/sql_connection_pool.h +++ b/CGImysql/sql_connection_pool.h @@ -1,50 +1,60 @@ #ifndef _CONNECTION_POOL_ #define _CONNECTION_POOL_ -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include "../lock/locker.h" using namespace std; class connection_pool { - public: - MYSQL * GetConnection(); //获取数据库连接 - bool ReleaseConnection(MYSQL* conn); //释放连接 - void DestroyPool(); //销毁所有连接 - - //单例模式获取一个连接 - static connection_pool* GetInstance(string url,string User,string PassWord,string DataName,int Port,unsigned int MaxConn); - int GetFreeConn(); - - connection_pool(); - ~connection_pool(); - - private: - unsigned int MaxConn; //最大连接数 - unsigned int CurConn; //当前已使用的连接数 - unsigned int FreeConn; //当前空闲的连接数 - - private: - pthread_mutex_t lock; //互斥锁 - list connList; //连接池 - connection_pool * conn; - MYSQL *Con; - connection_pool(string url,string User,string PassWord,string DataBaseName,int Port,unsigned int MaxConn);//构造方法 - static connection_pool *connPool;//静态实例 - //sem reserve; - private: - string url; //主机地址 - string Port; //数据库端口号 - string User; //登陆数据库用户名 - string PassWord; //登陆数据库密码 - string DatabaseName; //使用数据库名 +public: + MYSQL *GetConnection(); //获取数据库连接 + bool ReleaseConnection(MYSQL *conn); //释放连接 + int GetFreeConn(); //获取连接 + void DestroyPool(); //销毁所有连接 + + //单例模式 + static connection_pool *GetInstance(); + + void init(string url, string User, string PassWord, string DataBaseName, int Port, unsigned int MaxConn); + + + +private: + connection_pool(); + ~connection_pool(); + + unsigned int MaxConn; //最大连接数 + unsigned int CurConn; //当前已使用的连接数 + unsigned int FreeConn; //当前空闲的连接数 + locker lock; + list connList; //连接池 + sem reserve; + +private: + string url; //主机地址 + string Port; //数据库端口号 + string User; //登陆数据库用户名 + string PassWord; //登陆数据库密码 + string DatabaseName; //使用数据库名 +}; + +class connectionRAII{ + +public: + connectionRAII(MYSQL **con, connection_pool *connPool); + ~connectionRAII(); + +private: + MYSQL *conRAII; + connection_pool *poolRAII; }; #endif diff --git a/README.md b/README.md index c4a1c0a..acc00f6 100644 --- a/README.md +++ b/README.md @@ -2,163 +2,217 @@ TinyWebServer =============== -Linux下的简易web服务器,实现web端用户注册,登录功能,经压力测试可以实现上万的并发连接数据交换.详细参考《Linux服务器高性能编程》 -> * C/C++ -> * B/S模型 -> * [线程同步机制包装类] -> * [http连接请求处理类] -> * [半同步/半反应堆线程池] -> * [定时器处理非活动连接] -> * [同步/异步日志系统 ] -> * [数据库连接池] -> * [CGI及同步线程注册和登录校验] -> * [简易服务器压力测试] - -Demo ----------- -> * 注册演示 - -
- -> * 登录演示 - -
- - -测试结果 -------------- -Webbench对服务器进行压力测试,可以实现上万的并发连接. -> * 并发连接总数:10500 -> * 访问服务器时间:5s -> * 每秒钟响应请求数:552852 pages/min -> * 每秒钟传输数据量:1031990 bytes/sec -> * 所有访问均成功 - -**注意:** 使用本项目的webbench进行压测时,若报错显示webbench命令找不到,将可执行文件webbench删除后,重新编译即可。 - -
+Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器. +* 使用**线程池 + epoll(ET和LT均实现) + 模拟Proactor模式**的并发模型 +* 使用**状态机**解析HTTP请求报文,支持解析**GET和POST**请求 +* 访问服务器数据库实现web端用户**注册、登录**功能,可以请求服务器**图片和视频文件** +* 实现**同步/异步日志系统**,记录服务器运行状态 +* 经Webbench压力测试可以实现**上万的并发连接**数据交换 +模块概述 +---------- -框架 -------------- -
- - -web端界面 -------------- - -> * 判断是否注册 -> * 注册 -> * 注册失败提示 -> * 登录 -> * 登录失败提示 - - -
- -
- -web端测试 +> * C/C++ +> * B/S模型 +> * 线程同步机制包装类 +> * http连接请求处理类 +> * 半同步/半反应堆线程池 +> * 定时器处理非活动连接 +> * 同步/异步日志系统 +> * 数据库连接池 +> * CGI及同步线程注册和登录校验 +> * 简易服务器压力测试 + +Update +---------- +- [x] 解决请求服务器上大文件的Bug +- [x] 增加请求视频文件的页面 +- [x] 解决数据库同步校验内存泄漏 +- [x] 实现两种CGI数据库访问逻辑 +- [x] 实现ET非阻塞和LT阻塞,并完成压力测试 +- [x] 完善`lock.h`中的封装类,统一使用该同步机制 +- [x] 改进代码结构,更新局部变量懒汉单例模式 +- [x] 优化数据库连接池信号量与代码结构 +- [x] 使用RAII机制优化数据库连接的获取与释放 +- [x] 优化代码结构,封装工具类以减少全局变量 +- [x] 编译一次即可,命令行进行个性化测试更加友好 +- [x] main函数封装重构 +- [x] 新增定时器时间轮,时间堆的设计方案 + + +基础测试 ------------ * 服务器测试环境 * Ubuntu版本16.04 * MySQL版本5.7.29 +* 浏览器测试环境 + * Windows、Linux均可 + * Chrome + * FireFox + * 其他浏览器暂无测试 * 测试前确认已安装MySQL数据库 ```C++ - //建立yourdb库 + // 建立yourdb库 create database yourdb set utf8; - //创建user表 + // 创建user表 USE yourdb; CREATE TABLE user( username char(50) NULL, passwd char(50) NULL )ENGINE=InnoDB; - //添加数据 + // 添加数据 INSERT INTO user(username, passwd) VALUES('name', 'passwd'); ``` + * 修改main.c中的数据库初始化信息 ```C++ - //root root为服务器数据库的登录名和密码 - connection_pool *connPool=connection_pool::GetInstance("localhost","root","root","yourdb",3306,5); + //数据库登录名,密码,库名 + string user = "root"; + string passwd = "root"; + string databasename = "yourdb"; ``` -* 修改http_conn.cpp中的数据库初始化信息 +* 生成server ```C++ - //root root为服务器数据库的登录名和密码 - connection_pool *connPool=connection_pool::GetInstance("localhost","root","root","yourdb",3306,5); + make server ``` -* 修改http_conn.cpp中的root路径 +* 启动server ```C++ - const char* doc_root="/home/qgy/github/ini_tinywebserver/root"; + ./server ``` -* 选择任一校验方式,代码中使用同步校验。当使用CGI时才进行如下修改,否则可跳过本步骤,直接生成server - -- [ ] CGI多进程注册/登录校验 - * 打开http_conn.cpp中CGI,关闭同步线程 - ```C++ - 380 //同步线程登录校验 - 381 //#if 0 - 423 //#endif - - 425 //CGI多进程登录校验 - 426 #if 0 - 495 #endif - ``` - - * 修改sign.cpp中的数据库初始化信息 - - ```C++ - //root root为服务器数据库的登录名和密码 - connection_pool *connPool=connection_pool::GetInstance("localhost","root","root","yourdb",3306,5); - ``` - * 生成check.cgi - - ```C++ - make check.cgi - ``` - * 将生成的check.cgi放到root文件夹 - - ```C++ - cp ./check.cgi ./root - ``` - -- [x] 同步线程注册/登录校验 - * 关闭http_conn.cpp中CGI,打开同步线程 - - ```C++ - 380 //同步线程登录校验 - 381 #if 0 - 423 #endif - - 425 //CGI多进程登录校验 - 426 //#if 0 - 495 //#endif - ``` -* 生成server +* 浏览器端 ```C++ - make server + ip:9006 ``` -* 启动server +个性化测试 +------ + +```C++ +./server [-p port] [-v SQLVerify] [-l LOGWrite] [-m TRIGMode] [-o OPT_LINGER] [-s sql_num] [-t thread_num] +``` + +温馨提示:以上参数不是非必须,不用全部使用,根据个人情况搭配选用即可. + +* -p,自定义端口号 + * 默认9006 +* -v,选择数据库校验方式,默认同步校验 + * 0,同步校验,使用连接池 + * 1,CGI校验,使用连接池 + * 2,CGI校验,不使用连接池 +* -l,选择日志写入方式,默认同步写入 + * 0,同步写入 + * 1,异步写入 +* -m,epoll的触发模式,默认使用LT + * 0,表示使用LT + * 1,表示使用ET +* -o,优雅关闭连接,默认不使用 + * 0,不使用 + * 1,使用 +* -s,数据库连接数量 + * 默认为8 +* -t,线程数量 + * 默认为8 + +若使用CGI数据库校验方式,按如下编译代码,若使用同步校验,则跳过下面修改与生成CGI步骤. + +* 修改sign.cpp中的数据库初始化信息 ```C++ - ./server port + // root root修改为服务器数据库的登录名和密码 + // yourdb修改为上述创建的yourdb库名 + con = mysql_real_connect(con, "localhost", "root", "root", "yourdb", 3306, NULL, 0); ``` -* 浏览器端 +* 生成CGISQL.cgi ```C++ - ip:port + make CGISQL.cgi ``` +测试示例命令与含义 + +```C++ +./server -p 9007 -v 1 -l 1 -m 0 -o 1 -s 10 -t 10 +``` + +- [x] 端口9007 +- [x] 同步数据库校验,使用连接池 +- [x] 异步写入日志 +- [x] 使用LT水平触发 +- [x] 使用优雅关闭连接 +- [x] 数据库连接池内有10条连接 +- [x] 线程池内有10条线程 + + +Demo +---------- +> * 注册演示 + +
+ +> * 登录演示 + +
+ +> * 请求图片文件演示(6M) + +
+ +> * 请求视频文件演示(39M) + +
+ + +压力测试 +------------- +Webbench对服务器进行压力测试,在ET非阻塞和LT阻塞模式下均可实现上万的并发连接. 这里以同步日志,不使用优雅关闭连接为例. + +> * ET非阻塞,18657 QPS + +
+ +> * LT阻塞,22868 QPS + +
+ +> * 并发连接总数:10500 +> * 访问服务器时间:10s +> * 所有访问均成功 + +**注意:** 使用本项目的webbench进行压测时,若报错显示webbench命令找不到,将可执行文件webbench删除后,重新编译即可。 + + +框架 +------------- +
+ + +web端界面 +------------- + +> * 判断是否注册 +> * 注册 +> * 注册失败提示 +> * 登录 +> * 登录失败提示 + + +
+ +
+ +致谢 +------------ +Linux高性能服务器编程,游双著. diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..5e1bd97 --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +make server +make CGISQL.cgi \ No newline at end of file diff --git a/config.cpp b/config.cpp new file mode 100644 index 0000000..1f5241f --- /dev/null +++ b/config.cpp @@ -0,0 +1,80 @@ +#include "config.h" + +Config::Config(){ + //端口号,默认9006 + PORT = 9006; + + //数据库校验方式,默认同步 + SQLVerify = 0; + + //日志写入方式,默认同步 + LOGWrite = 0; + + //触发模式,默认LT + TRIGMode = 0; + + //优雅关闭链接,默认不使用 + OPT_LINGER = 0; + + //数据库连接池数量,默认8 + sql_num = 8; + + //线程池内的线程数量,默认8 + thread_num = 8; + + //关闭日志,默认不关闭 + close_log = 0; +} + +void Config::parse_arg(int argc, char*argv[]){ + int opt; + const char *str = "p:v:l:m:o:s:t:c:"; + while ((opt = getopt(argc, argv, str)) != -1) + { + switch (opt) + { + case 'p': + { + PORT = atoi(optarg); + break; + } + case 'v': + { + SQLVerify = atoi(optarg); + break; + } + case 'l': + { + LOGWrite = atoi(optarg); + break; + } + case 'm': + { + TRIGMode = atoi(optarg); + break; + } + case 'o': + { + OPT_LINGER = atoi(optarg); + break; + } + case 's': + { + sql_num = atoi(optarg); + break; + } + case 't': + { + thread_num = atoi(optarg); + break; + } + case 'c': + { + close_log = atoi(optarg); + break; + } + default: + break; + } + } +} \ No newline at end of file diff --git a/config.h b/config.h new file mode 100644 index 0000000..9285672 --- /dev/null +++ b/config.h @@ -0,0 +1,41 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include "webserver.h" + +using namespace std; + +class Config +{ +public: + Config(); + ~Config(){}; + + void parse_arg(int argc, char*argv[]); + + //端口号 + int PORT; + + //数据库校验方式 + int SQLVerify; + + //日志写入方式 + int LOGWrite; + + //触发模式 + int TRIGMode; + + //优雅关闭链接 + int OPT_LINGER; + + //数据库连接池数量 + int sql_num; + + //线程池内的线程数量 + int thread_num; + + //是否关闭日志 + int close_log; +}; + +#endif \ No newline at end of file diff --git a/http/http_conn.cpp b/http/http_conn.cpp index cfd9983..8556a9f 100644 --- a/http/http_conn.cpp +++ b/http/http_conn.cpp @@ -1,119 +1,167 @@ #include "http_conn.h" #include "../log/log.h" -#include -#include +#include +#include //定义http响应的一些状态信息 -const char* ok_200_title="OK"; -const char* error_400_title="Bad Request"; -const char* error_400_form="Your request has bad syntax or is inherently impossible to staisfy.\n"; -const char* error_403_title="Forbidden"; -const char* error_403_form="You do not have permission to get file form this server.\n"; -const char* error_404_title="Not Found"; -const char* error_404_form="The requested file was not found on this server.\n"; -const char* error_500_title="Internal Error"; -const char* error_500_form="There was an unusual problem serving the request file.\n"; - -//当浏览器出现连接重置时,可能是网站根目录出错或http响应格式出错或者访问的文件中内容完全为空 -const char* doc_root="/home/qgy/github/ini_tinywebserver/root"; - -//创建数据库连接池 -connection_pool *connPool=connection_pool::GetInstance("localhost","root","root","qgydb",3306,5); - -//将表中的用户名和密码放入map -map users; - -void http_conn::initmysql_result(){ -//先从连接池中取一个连接 -MYSQL *mysql=connPool->GetConnection(); - -//在user表中检索username,passwd数据,浏览器端输入 -if(mysql_query(mysql,"SELECT username,passwd FROM user")) +const char *ok_200_title = "OK"; +const char *error_400_title = "Bad Request"; +const char *error_400_form = "Your request has bad syntax or is inherently impossible to staisfy.\n"; +const char *error_403_title = "Forbidden"; +const char *error_403_form = "You do not have permission to get file form this server.\n"; +const char *error_404_title = "Not Found"; +const char *error_404_form = "The requested file was not found on this server.\n"; +const char *error_500_title = "Internal Error"; +const char *error_500_form = "There was an unusual problem serving the request file.\n"; + +locker m_lock; + +void http_conn::initmysql_result(connection_pool *connPool, map &users) { - //printf("INSERT error:%s\n",mysql_error(mysql)); - LOG_ERROR("SELECT error:%s\n",mysql_error(mysql)); - //return BAD_REQUEST; -} + //先从连接池中取一个连接 + MYSQL *mysql = NULL; + connectionRAII mysqlcon(&mysql, connPool); + + //在user表中检索username,passwd数据,浏览器端输入 + if (mysql_query(mysql, "SELECT username,passwd FROM user")) + { + LOG_ERROR("SELECT error:%s\n", mysql_error(mysql)); + } -//从表中检索完整的结果集 -MYSQL_RES *result=mysql_store_result(mysql); + //从表中检索完整的结果集 + MYSQL_RES *result = mysql_store_result(mysql); -//返回结果集中的列数 -int num_fields=mysql_num_fields(result); + //返回结果集中的列数 + int num_fields = mysql_num_fields(result); -//返回所有字段结构的数组 -MYSQL_FIELD *fields=mysql_fetch_fields(result); + //返回所有字段结构的数组 + MYSQL_FIELD *fields = mysql_fetch_fields(result); -//从结果集中获取下一行,将对应的用户名和密码,存入map中 -while(MYSQL_ROW row=mysql_fetch_row(result)) -{ - string temp1(row[0]); - string temp2(row[1]); - users[temp1]=temp2; + //从结果集中获取下一行,将对应的用户名和密码,存入map中 + while (MYSQL_ROW row = mysql_fetch_row(result)) + { + string temp1(row[0]); + string temp2(row[1]); + users[temp1] = temp2; + } } -//将连接归还连接池 -connPool->ReleaseConnection(mysql); +void http_conn::initresultFile(connection_pool *connPool, map &users) +{ + ofstream out("./CGImysql/id_passwd.txt"); + //先从连接池中取一个连接 + MYSQL *mysql = NULL; + connectionRAII mysqlcon(&mysql, connPool); + + //在user表中检索username,passwd数据,浏览器端输入 + if (mysql_query(mysql, "SELECT username,passwd FROM user")) + { + LOG_ERROR("SELECT error:%s\n", mysql_error(mysql)); + } + + //从表中检索完整的结果集 + MYSQL_RES *result = mysql_store_result(mysql); + + //返回结果集中的列数 + int num_fields = mysql_num_fields(result); + + //返回所有字段结构的数组 + MYSQL_FIELD *fields = mysql_fetch_fields(result); + + //从结果集中获取下一行,将对应的用户名和密码,存入map中 + while (MYSQL_ROW row = mysql_fetch_row(result)) + { + string temp1(row[0]); + string temp2(row[1]); + out << temp1 << " " << temp2 << endl; + users[temp1] = temp2; + } + + out.close(); } + //对文件描述符设置非阻塞 -int setnonblocking(int fd){ - int old_option=fcntl(fd,F_GETFL); - int new_option=old_option | O_NONBLOCK; - fcntl(fd,F_SETFL,new_option); +int setnonblocking(int fd) +{ + int old_option = fcntl(fd, F_GETFL); + int new_option = old_option | O_NONBLOCK; + fcntl(fd, F_SETFL, new_option); return old_option; } //将内核事件表注册读事件,ET模式,选择开启EPOLLONESHOT -void addfd(int epollfd,int fd,bool one_shot) +void addfd(int epollfd, int fd, bool one_shot, int TRIGMode) { epoll_event event; - event.data.fd=fd; - event.events=EPOLLIN|EPOLLET|EPOLLRDHUP; - if(one_shot) - event.events|=EPOLLONESHOT; - epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event); + event.data.fd = fd; + + if (1 == TRIGMode) + event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; + else + event.events = EPOLLIN | EPOLLRDHUP; + + if (one_shot) + event.events |= EPOLLONESHOT; + epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event); setnonblocking(fd); } //从内核时间表删除描述符 -void removefd(int epollfd,int fd) +void removefd(int epollfd, int fd) { - epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,0); - close(fd); + epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, 0); + close(fd); } //将事件重置为EPOLLONESHOT -void modfd(int epollfd,int fd,int ev) +void modfd(int epollfd, int fd, int ev, int TRIGMode) { epoll_event event; - event.data.fd=fd; - event.events=ev|EPOLLET|EPOLLONESHOT|EPOLLRDHUP; - epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event); + event.data.fd = fd; + + if (1 == TRIGMode) + event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP; + else + event.events = ev | EPOLLONESHOT | EPOLLRDHUP; + + epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event); } -int http_conn::m_user_count=0; -int http_conn::m_epollfd=-1; +int http_conn::m_user_count = 0; +int http_conn::m_epollfd = -1; //关闭连接,关闭一个连接,客户总量减一 void http_conn::close_conn(bool real_close) { - if(real_close&&(m_sockfd!=-1)) + if (real_close && (m_sockfd != -1)) { - removefd(m_epollfd,m_sockfd); - m_sockfd=-1; + removefd(m_epollfd, m_sockfd); + m_sockfd = -1; m_user_count--; } } //初始化连接,外部调用初始化套接字地址 -void http_conn::init(int sockfd,const sockaddr_in& addr) +void http_conn::init(int sockfd, const sockaddr_in &addr, char *root, map &users, int SQLVerify, int TRIGMode, + int close_log, string user, string passwd, string sqlname) { - m_sockfd=sockfd; - m_address=addr; - //int reuse=1; - //setsockopt(m_sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)); - addfd(m_epollfd,sockfd,true); + m_sockfd = sockfd; + m_address = addr; + + addfd(m_epollfd, sockfd, true, m_TRIGMode); m_user_count++; + + //当浏览器出现连接重置时,可能是网站根目录出错或http响应格式出错或者访问的文件中内容完全为空 + doc_root = root; + m_users = users; + m_SQLVerify = SQLVerify; + m_TRIGMode = TRIGMode; + m_close_log = close_log; + + strcpy(sql_user, user.c_str()); + strcpy(sql_passwd, passwd.c_str()); + strcpy(sql_name, sqlname.c_str()); + init(); } @@ -121,23 +169,25 @@ void http_conn::init(int sockfd,const sockaddr_in& addr) //check_state默认为分析请求行状态 void http_conn::init() { - m_check_state=CHECK_STATE_REQUESTLINE; - m_linger=false; - m_method=GET; - m_url=0; - m_version=0; - m_content_length=0; - m_host=0; - m_start_line=0; - m_checked_idx=0; - m_read_idx=0; - m_write_idx=0; - int bytes_have_send=0; - int bytes_to_send=0; + mysql = NULL; + bytes_to_send = 0; + bytes_have_send = 0; + m_check_state = CHECK_STATE_REQUESTLINE; + m_linger = false; + m_method = GET; + m_url = 0; + m_version = 0; + m_content_length = 0; + m_host = 0; + m_start_line = 0; + m_checked_idx = 0; + m_read_idx = 0; + m_write_idx = 0; cgi = 0; - memset(m_read_buf,'\0',READ_BUFFER_SIZE); - memset(m_write_buf,'\0',WRITE_BUFFER_SIZE); - memset(m_real_file,'\0',FILENAME_LEN); + + memset(m_read_buf, '\0', READ_BUFFER_SIZE); + memset(m_write_buf, '\0', WRITE_BUFFER_SIZE); + memset(m_real_file, '\0', FILENAME_LEN); } //从状态机,用于分析出一行内容 @@ -145,144 +195,190 @@ void http_conn::init() http_conn::LINE_STATUS http_conn::parse_line() { char temp; - for(;m_checked_idx1&&m_read_buf[m_checked_idx-1]=='\r') + if (m_checked_idx > 1 && m_read_buf[m_checked_idx - 1] == '\r') { - m_read_buf[m_checked_idx-1]='\0'; - m_read_buf[m_checked_idx++]='\0'; + m_read_buf[m_checked_idx - 1] = '\0'; + m_read_buf[m_checked_idx++] = '\0'; return LINE_OK; } return LINE_BAD; } } - return LINE_OPEN; + return LINE_OPEN; } //循环读取客户数据,直到无数据可读或对方关闭连接 //非阻塞ET工作模式下,需要一次性将数据读完 +// listenfd使用LT,connfd使用ET +// bool http_conn::read_once() +// { +// if (m_read_idx >= READ_BUFFER_SIZE) +// { +// return false; +// } +// int bytes_read = 0; +// while (true) +// { +// bytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0); +// if (bytes_read == -1) +// { +// if (errno == EAGAIN || errno == EWOULDBLOCK) +// break; +// return false; +// } +// else if (bytes_read == 0) +// { +// return false; +// } +// m_read_idx += bytes_read; +// } +// return true; +// } + bool http_conn::read_once() { - if(m_read_idx>=READ_BUFFER_SIZE) + if (m_read_idx >= READ_BUFFER_SIZE) { return false; } - int bytes_read=0; - while(true) + int bytes_read = 0; + + //LT读取数据 + if (0 == m_TRIGMode) { - bytes_read=recv(m_sockfd,m_read_buf+m_read_idx,READ_BUFFER_SIZE-m_read_idx,0); - if(bytes_read==-1) + bytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0); + m_read_idx += bytes_read; + + if (bytes_read <= 0) { - if(errno==EAGAIN||errno==EWOULDBLOCK) - break; return false; } - else if(bytes_read==0) + + return true; + } + //ET读数据 + else + { + while (true) { - return false; + bytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0); + if (bytes_read == -1) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + return false; + } + else if (bytes_read == 0) + { + return false; + } + m_read_idx += bytes_read; } - m_read_idx+=bytes_read; + return true; } - return true; } //解析http请求行,获得请求方法,目标url及http版本号 http_conn::HTTP_CODE http_conn::parse_request_line(char *text) { - m_url=strpbrk(text," \t"); - if(!m_url) + m_url = strpbrk(text, " \t"); + if (!m_url) { return BAD_REQUEST; } - *m_url++='\0'; - char *method=text; - if(strcasecmp(method,"GET")==0) - m_method=GET; - else if(strcasecmp(method,"POST")==0) + *m_url++ = '\0'; + char *method = text; + if (strcasecmp(method, "GET") == 0) + m_method = GET; + else if (strcasecmp(method, "POST") == 0) { - m_method=POST; - cgi=1; + m_method = POST; + cgi = 1; } else return BAD_REQUEST; - m_url+=strspn(m_url," \t"); - m_version=strpbrk(m_url," \t"); - if(!m_version) + m_url += strspn(m_url, " \t"); + m_version = strpbrk(m_url, " \t"); + if (!m_version) return BAD_REQUEST; - *m_version++='\0'; - m_version+=strspn(m_version," \t"); - if(strcasecmp(m_version,"HTTP/1.1")!=0) + *m_version++ = '\0'; + m_version += strspn(m_version, " \t"); + if (strcasecmp(m_version, "HTTP/1.1") != 0) return BAD_REQUEST; - if(strncasecmp(m_url,"http://",7)==0) + if (strncasecmp(m_url, "http://", 7) == 0) { - m_url+=7; - m_url=strchr(m_url,'/'); + m_url += 7; + m_url = strchr(m_url, '/'); } - - if(strncasecmp(m_url,"https://",8)==0) + + if (strncasecmp(m_url, "https://", 8) == 0) { - m_url+=8; - m_url=strchr(m_url,'/'); + m_url += 8; + m_url = strchr(m_url, '/'); } - if(!m_url||m_url[0]!='/') + if (!m_url || m_url[0] != '/') return BAD_REQUEST; //当url为/时,显示判断界面 - if(strlen(m_url)==1) - strcat(m_url,"judge.html"); - m_check_state=CHECK_STATE_HEADER; + if (strlen(m_url) == 1) + strcat(m_url, "judge.html"); + m_check_state = CHECK_STATE_HEADER; return NO_REQUEST; } //解析http请求的一个头部信息 http_conn::HTTP_CODE http_conn::parse_headers(char *text) { - if(text[0]=='\0') + if (text[0] == '\0') { - if(m_content_length!=0) + if (m_content_length != 0) { - m_check_state=CHECK_STATE_CONTENT; + m_check_state = CHECK_STATE_CONTENT; return NO_REQUEST; } return GET_REQUEST; } - else if(strncasecmp(text,"Connection:",11)==0) + else if (strncasecmp(text, "Connection:", 11) == 0) { - text+=11; - text+=strspn(text," \t"); - if(strcasecmp(text,"keep-alive")==0) + text += 11; + text += strspn(text, " \t"); + if (strcasecmp(text, "keep-alive") == 0) { - m_linger=true; + m_linger = true; } } - else if(strncasecmp(text,"Content-length:",15)==0) + else if (strncasecmp(text, "Content-length:", 15) == 0) { - text+=15; - text+=strspn(text," \t"); - m_content_length=atol(text); + text += 15; + text += strspn(text, " \t"); + m_content_length = atol(text); } - else if(strncasecmp(text,"Host:",5)==0) + else if (strncasecmp(text, "Host:", 5) == 0) { - text+=5; - text+=strspn(text," \t"); - m_host=text; + text += 5; + text += strspn(text, " \t"); + m_host = text; } - else{ - //printf("oop!unknow header: %s\n",text); - LOG_INFO("oop!unknow header: %s",text); + else + { + LOG_INFO("oop!unknow header: %s", text); Log::get_instance()->flush(); } return NO_REQUEST; @@ -291,432 +387,539 @@ http_conn::HTTP_CODE http_conn::parse_headers(char *text) //判断http请求是否被完整读入 http_conn::HTTP_CODE http_conn::parse_content(char *text) { - if(m_read_idx>=(m_content_length+m_checked_idx)){ - text[m_content_length]='\0'; - //POST请求中最后为输入的用户名和密码 - m_string = text; + if (m_read_idx >= (m_content_length + m_checked_idx)) + { + text[m_content_length] = '\0'; + //POST请求中最后为输入的用户名和密码 + m_string = text; return GET_REQUEST; } return NO_REQUEST; } -// http_conn::HTTP_CODE http_conn::process_read() { - LINE_STATUS line_status=LINE_OK; - HTTP_CODE ret=NO_REQUEST; - char* text=0; - //printf("=========请求行头=========\n"); - //LOG_INFO("=========请求行头=========\n"); - //Log::get_instance()->flush(); - while((m_check_state==CHECK_STATE_CONTENT&&line_status==LINE_OK)||((line_status=parse_line())==LINE_OK)) - { - text=get_line(); - m_start_line=m_checked_idx; - //printf("======:%s\n",text); - LOG_INFO("%s",text); - Log::get_instance()->flush(); - switch(m_check_state) + LINE_STATUS line_status = LINE_OK; + HTTP_CODE ret = NO_REQUEST; + char *text = 0; + + while ((m_check_state == CHECK_STATE_CONTENT && line_status == LINE_OK) || ((line_status = parse_line()) == LINE_OK)) + { + text = get_line(); + m_start_line = m_checked_idx; + LOG_INFO("%s", text); + Log::get_instance()->flush(); + switch (m_check_state) { - case CHECK_STATE_REQUESTLINE: - { - ret=parse_request_line(text); - if(ret==BAD_REQUEST) - return BAD_REQUEST; - break; - } - case CHECK_STATE_HEADER: - { - ret=parse_headers(text); - if(ret==BAD_REQUEST) - return BAD_REQUEST; - else if(ret==GET_REQUEST) - { - return do_request(); - } - break; - } - case CHECK_STATE_CONTENT: + case CHECK_STATE_REQUESTLINE: + { + ret = parse_request_line(text); + if (ret == BAD_REQUEST) + return BAD_REQUEST; + break; + } + case CHECK_STATE_HEADER: + { + ret = parse_headers(text); + if (ret == BAD_REQUEST) + return BAD_REQUEST; + else if (ret == GET_REQUEST) { - ret=parse_content(text); - if(ret==GET_REQUEST) - return do_request(); - line_status=LINE_OPEN; - break; + return do_request(); } - default: + break; + } + case CHECK_STATE_CONTENT: + { + ret = parse_content(text); + if (ret == GET_REQUEST) + return do_request(); + line_status = LINE_OPEN; + break; + } + default: return INTERNAL_ERROR; } } - //printf("=========请求行end=========\n"); return NO_REQUEST; } http_conn::HTTP_CODE http_conn::do_request() { - strcpy(m_real_file,doc_root); - int len=strlen(doc_root); + strcpy(m_real_file, doc_root); + int len = strlen(doc_root); //printf("m_url:%s\n", m_url); - const char *p = strrchr(m_url, '/'); - -//#if 0 + const char *p = strrchr(m_url, '/'); + //处理cgi - if(cgi==1 && (*(p+1) == '2' || *(p+1) == '3')) - { - - //根据标志判断是登录检测还是注册检测 - char flag = m_url[1]; - //printf("====+++====+++%c\n", flag); - - char *m_url_real = (char *)malloc(sizeof(char) * 200); - strcpy(m_url_real, "/"); - strcat(m_url_real, m_url+2); - strncpy(m_real_file+len,m_url_real,FILENAME_LEN-len-1); - free(m_url_real); - - //将用户名和密码提取出来 - //user=123&passwd=123 - char name[100],password[100]; + if (cgi == 1 && (*(p + 1) == '2' || *(p + 1) == '3')) + { + + //根据标志判断是登录检测还是注册检测 + char flag = m_url[1]; + + char *m_url_real = (char *)malloc(sizeof(char) * 200); + strcpy(m_url_real, "/"); + strcat(m_url_real, m_url + 2); + strncpy(m_real_file + len, m_url_real, FILENAME_LEN - len - 1); + free(m_url_real); + + //将用户名和密码提取出来 + //user=123&passwd=123 + char name[100], password[100]; int i; - for(i=5;m_string[i]!='&';++i) - name[i-5]=m_string[i]; - name[i-5]='\0'; - int j=0; - for(i=i+10;m_string[i]!='\0';++i,++j) - password[j]=m_string[i]; - password[j]='\0'; - -//同步线程登录校验 -//#if 0 - pthread_mutex_t lock; - pthread_mutex_init(&lock, NULL); - - //从连接池中取一个连接 - MYSQL *mysql=connPool->GetConnection(); - - //如果是注册,先检测数据库中是否有重名的 - //没有重名的,进行增加数据 - char *sql_insert = (char*)malloc(sizeof(char)*200); - strcpy(sql_insert, "INSERT INTO user(username, passwd) VALUES("); - strcat(sql_insert, "'"); - strcat(sql_insert, name); - strcat(sql_insert, "', '"); - strcat(sql_insert, password); - strcat(sql_insert, "')"); - - if(*(p+1) == '3'){ - if(users.find(name)==users.end()){ - - pthread_mutex_lock(&lock); - int res = mysql_query(mysql,sql_insert); - users.insert(pair(name, password)); - pthread_mutex_unlock(&lock); - - if(!res) - strcpy(m_url, "/log.html"); - else - strcpy(m_url, "/registerError.html"); - } - else - strcpy(m_url, "/registerError.html"); - } - //如果是登录,直接判断 - //若浏览器端输入的用户名和密码在表中可以查找到,返回1,否则返回0 - else if(*(p+1) == '2'){ - if(users.find(name)!=users.end()&&users[name]==password) - strcpy(m_url, "/welcome.html"); - else - strcpy(m_url, "/logError.html"); - } - connPool->ReleaseConnection(mysql); -//#endif - -//CGI多进程登录校验 -#if 0 - - //fd[0]:读管道,fd[1]:写管道 - pid_t pid; - int pipefd[2]; - if(pipe(pipefd)<0) + for (i = 5; m_string[i] != '&'; ++i) + name[i - 5] = m_string[i]; + name[i - 5] = '\0'; + + int j = 0; + for (i = i + 10; m_string[i] != '\0'; ++i, ++j) + password[j] = m_string[i]; + password[j] = '\0'; + + if (0 == m_SQLVerify) { - LOG_ERROR("pipe() error:%d",4); - return BAD_REQUEST; + if (*(p + 1) == '3') + { + //如果是注册,先检测数据库中是否有重名的 + //没有重名的,进行增加数据 + char *sql_insert = (char *)malloc(sizeof(char) * 200); + strcpy(sql_insert, "INSERT INTO user(username, passwd) VALUES("); + strcat(sql_insert, "'"); + strcat(sql_insert, name); + strcat(sql_insert, "', '"); + strcat(sql_insert, password); + strcat(sql_insert, "')"); + + if (m_users.find(name) == m_users.end()) + { + m_lock.lock(); + int res = mysql_query(mysql, sql_insert); + m_users.insert(pair(name, password)); + m_lock.unlock(); + + if (!res) + strcpy(m_url, "/log.html"); + else + strcpy(m_url, "/registerError.html"); + } + else + strcpy(m_url, "/registerError.html"); + } + //如果是登录,直接判断 + //若浏览器端输入的用户名和密码在表中可以查找到,返回1,否则返回0 + else if (*(p + 1) == '2') + { + if (m_users.find(name) != m_users.end() && m_users[name] == password) + strcpy(m_url, "/welcome.html"); + else + strcpy(m_url, "/logError.html"); + } } - if((pid=fork())<0) + else if (1 == m_SQLVerify) { - LOG_ERROR("fork() error:%d",3); - return BAD_REQUEST; + //注册 + if (*(p + 1) == '3') + { + //如果是注册,先检测数据库中是否有重名的 + //没有重名的,进行增加数据 + char *sql_insert = (char *)malloc(sizeof(char) * 200); + strcpy(sql_insert, "INSERT INTO user(username, passwd) VALUES("); + strcat(sql_insert, "'"); + strcat(sql_insert, name); + strcat(sql_insert, "', '"); + strcat(sql_insert, password); + strcat(sql_insert, "')"); + + if (m_users.find(name) == m_users.end()) + { + m_lock.lock(); + int res = mysql_query(mysql, sql_insert); + m_users.insert(pair(name, password)); + m_lock.unlock(); + + if (!res) + { + strcpy(m_url, "/log.html"); + m_lock.lock(); + //每次都需要重新更新id_passwd.txt + ofstream out("./CGImysql/id_passwd.txt", ios::app); + out << name << " " << password << endl; + out.close(); + m_lock.unlock(); + } + else + strcpy(m_url, "/registerError.html"); + } + else + strcpy(m_url, "/registerError.html"); + } + //登录 + else if (*(p + 1) == '2') + { + pid_t pid; + int pipefd[2]; + if (pipe(pipefd) < 0) + { + LOG_ERROR("pipe() error:%d", 4); + return BAD_REQUEST; + } + if ((pid = fork()) < 0) + { + LOG_ERROR("fork() error:%d", 3); + return BAD_REQUEST; + } + + if (pid == 0) + { + //标准输出,文件描述符是1,然后将输出重定向到管道写端 + dup2(pipefd[1], 1); + //关闭管道的读端 + close(pipefd[0]); + //父进程去执行cgi程序,m_real_file,name,password为输入 + execl(m_real_file, name, password, "./CGImysql/id_passwd.txt", "1", NULL); + } + else + { + //子进程关闭写端,打开读端,读取父进程的输出 + close(pipefd[1]); + char result; + int ret = read(pipefd[0], &result, 1); + + if (ret != 1) + { + LOG_ERROR("管道read error:ret=%d", ret); + return BAD_REQUEST; + } + + LOG_INFO("%s", "登录检测"); + Log::get_instance()->flush(); + //当用户名和密码正确,则显示welcome界面,否则显示错误界面 + if (result == '1') + strcpy(m_url, "/welcome.html"); + else + strcpy(m_url, "/logError.html"); + + //回收进程资源 + waitpid(pid, NULL, 0); + } + } } - - if(pid==0) + + //CGI多进程登录校验,不用数据库连接池 + //子进程完成注册和登录 + else { - //标准输出,文件描述符是1,然后将输出重定向到管道写端 - dup2(pipefd[1],1); - //关闭管道的读端 - close(pipefd[0]); - //父进程去执行cgi程序,m_real_file,name,password为输入 - //./check.cgi name password - - execl(m_real_file,&flag,name,password, NULL); - } - else{ - //printf("子进程\n"); - //子进程关闭写端,打开读端,读取父进程的输出 - close(pipefd[1]); - char result; - int ret=read(pipefd[0],&result,1); - - if(ret!=1) + //fd[0]:读管道,fd[1]:写管道 + pid_t pid; + int pipefd[2]; + if (pipe(pipefd) < 0) { - LOG_ERROR("管道read error:ret=%d",ret); + LOG_ERROR("pipe() error:%d", 4); return BAD_REQUEST; } - if(flag == '2'){ - //printf("登录检测\n"); - LOG_INFO("%s","登录检测"); - Log::get_instance()->flush(); - //当用户名和密码正确,则显示welcome界面,否则显示错误界面 - if(result=='1') - strcpy(m_url, "/welcome.html"); - //m_url="/welcome.html"; - else - strcpy(m_url, "/logError.html"); - //m_url="/logError.html"; - } - else if(flag == '3'){ - //printf("注册检测\n"); - LOG_INFO("%s","注册检测"); - Log::get_instance()->flush(); - //当成功注册后,则显示登陆界面,否则显示错误界面 - if(result=='1') - strcpy(m_url, "/log.html"); - //m_url="/log.html"; - else - strcpy(m_url, "/registerError.html"); - //m_url="/registerError.html"; - } - //printf("m_url:%s\n", m_url); - //回收进程资源 - waitpid(pid,NULL,0); - //waitpid(pid,0,NULL); - //printf("回收完成\n"); + if ((pid = fork()) < 0) + { + LOG_ERROR("fork() error:%d", 3); + return BAD_REQUEST; + } + + if (pid == 0) + { + //标准输出,文件描述符是1,然后将输出重定向到管道写端 + dup2(pipefd[1], 1); + + //关闭管道的读端 + close(pipefd[0]); + + //父进程去执行cgi程序 + execl(m_real_file, &flag, name, password, "2", sql_user, sql_passwd, sql_name, NULL); + } + else + { + //子进程关闭写端,打开读端,读取父进程的输出 + close(pipefd[1]); + char result; + int ret = read(pipefd[0], &result, 1); + + if (ret != 1) + { + LOG_ERROR("管道read error:ret=%d", ret); + return BAD_REQUEST; + } + if (flag == '2') + { + LOG_INFO("%s", "登录检测"); + Log::get_instance()->flush(); + + //当用户名和密码正确,则显示welcome界面,否则显示错误界面 + if (result == '1') + strcpy(m_url, "/welcome.html"); + else + strcpy(m_url, "/logError.html"); + } + else if (flag == '3') + { + LOG_INFO("%s", "注册检测"); + Log::get_instance()->flush(); + + //当成功注册后,则显示登陆界面,否则显示错误界面 + if (result == '1') + strcpy(m_url, "/log.html"); + else + strcpy(m_url, "/registerError.html"); + } + //回收进程资源 + waitpid(pid, NULL, 0); + } } -#endif } + if (*(p + 1) == '0') + { + char *m_url_real = (char *)malloc(sizeof(char) * 200); + strcpy(m_url_real, "/register.html"); + strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); - if(*(p+1) == '0'){ - char *m_url_real = (char *)malloc(sizeof(char) * 200); - strcpy(m_url_real,"/register.html"); - strncpy(m_real_file+len,m_url_real,strlen(m_url_real)); - - free(m_url_real); + free(m_url_real); } - else if( *(p+1) == '1'){ - char *m_url_real = (char *)malloc(sizeof(char) * 200); - strcpy(m_url_real,"/log.html"); - strncpy(m_real_file+len,m_url_real,strlen(m_url_real)); - - free(m_url_real); + else if (*(p + 1) == '1') + { + char *m_url_real = (char *)malloc(sizeof(char) * 200); + strcpy(m_url_real, "/log.html"); + strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); + + free(m_url_real); } - else - strncpy(m_real_file+len,m_url,FILENAME_LEN-len-1); + else if (*(p + 1) == '5') + { + char *m_url_real = (char *)malloc(sizeof(char) * 200); + strcpy(m_url_real, "/picture.html"); + strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); + + free(m_url_real); + } + else if (*(p + 1) == '6') + { + char *m_url_real = (char *)malloc(sizeof(char) * 200); + strcpy(m_url_real, "/video.html"); + strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); + + free(m_url_real); + } + else if (*(p + 1) == '7') + { + char *m_url_real = (char *)malloc(sizeof(char) * 200); + strcpy(m_url_real, "/fans.html"); + strncpy(m_real_file + len, m_url_real, strlen(m_url_real)); + free(m_url_real); + } + else + strncpy(m_real_file + len, m_url, FILENAME_LEN - len - 1); - if(stat(m_real_file,&m_file_stat)<0) + if (stat(m_real_file, &m_file_stat) < 0) return NO_RESOURCE; - if(!(m_file_stat.st_mode&S_IROTH)) + + if (!(m_file_stat.st_mode & S_IROTH)) return FORBIDDEN_REQUEST; - if(S_ISDIR(m_file_stat.st_mode)) + + if (S_ISDIR(m_file_stat.st_mode)) return BAD_REQUEST; - int fd=open(m_real_file,O_RDONLY); - m_file_address=(char*)mmap(0,m_file_stat.st_size,PROT_READ,MAP_PRIVATE,fd,0); + + int fd = open(m_real_file, O_RDONLY); + m_file_address = (char *)mmap(0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); return FILE_REQUEST; } void http_conn::unmap() { - if(m_file_address) + if (m_file_address) { - munmap(m_file_address,m_file_stat.st_size); - m_file_address=0; + munmap(m_file_address, m_file_stat.st_size); + m_file_address = 0; } } bool http_conn::write() { - int temp=0; + int temp = 0; + int newadd = 0; - if(bytes_to_send==0) + + if (bytes_to_send == 0) { - modfd(m_epollfd,m_sockfd,EPOLLIN); + modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); init(); return true; } - while(1) + + while (1) { - temp=writev(m_sockfd,m_iv,m_iv_count); - printf("temp:%d\n",temp); + temp = writev(m_sockfd, m_iv, m_iv_count); - if(temp > 0) + if (temp >= 0) { bytes_have_send += temp; newadd = bytes_have_send - m_write_idx; } - - if(temp<=-1) + else { - if(errno==EAGAIN) + if (errno == EAGAIN) { - m_iv[0].iov_len = 0; - m_iv[1].iov_base = m_file_address + newadd; - m_iv[1].iov_len = bytes_to_send - temp; - printf("out\n"); - modfd(m_epollfd,m_sockfd,EPOLLOUT); + if (bytes_have_send >= m_iv[0].iov_len) + { + m_iv[0].iov_len = 0; + m_iv[1].iov_base = m_file_address + newadd; + m_iv[1].iov_len = bytes_to_send; + } + else + { + m_iv[0].iov_base = m_write_buf + bytes_to_send; + m_iv[0].iov_len = m_iv[0].iov_len - bytes_have_send; + } + modfd(m_epollfd, m_sockfd, EPOLLOUT, m_TRIGMode); return true; } unmap(); return false; } - bytes_to_send-=temp; - //bytes_have_send+=temp; - if(bytes_to_send<=0) + bytes_to_send -= temp; + if (bytes_to_send <= 0) + { unmap(); - if(m_linger) + modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); + + if (m_linger) { - printf("========================\n"); - printf("%s\n", "发送响应成功"); init(); - modfd(m_epollfd,m_sockfd,EPOLLIN); return true; } else { - modfd(m_epollfd,m_sockfd,EPOLLIN); return false; } } } } -bool http_conn::add_response(const char* format,...) +bool http_conn::add_response(const char *format, ...) { - if(m_write_idx>=WRITE_BUFFER_SIZE) + if (m_write_idx >= WRITE_BUFFER_SIZE) return false; va_list arg_list; - va_start(arg_list,format); - int len=vsnprintf(m_write_buf+m_write_idx,WRITE_BUFFER_SIZE-1-m_write_idx,format,arg_list); - if(len>=(WRITE_BUFFER_SIZE-1-m_write_idx)){ + va_start(arg_list, format); + int len = vsnprintf(m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list); + if (len >= (WRITE_BUFFER_SIZE - 1 - m_write_idx)) + { va_end(arg_list); return false; } - m_write_idx+=len; + m_write_idx += len; va_end(arg_list); - //printf("%s\n",m_write_buf); LOG_INFO("request:%s", m_write_buf); Log::get_instance()->flush(); return true; } -bool http_conn::add_status_line(int status,const char* title) +bool http_conn::add_status_line(int status, const char *title) { - return add_response("%s %d %s\r\n","HTTP/1.1",status,title); + return add_response("%s %d %s\r\n", "HTTP/1.1", status, title); } bool http_conn::add_headers(int content_len) { - //add_content_type(); - add_content_length(content_len); - add_linger(); - add_blank_line(); + return add_content_length(content_len) && add_linger() && + add_blank_line(); } bool http_conn::add_content_length(int content_len) { - return add_response("Content-Length:%d\r\n",content_len); + return add_response("Content-Length:%d\r\n", content_len); } bool http_conn::add_content_type() { - return add_response("Content-Type:%s\r\n","text/html"); + return add_response("Content-Type:%s\r\n", "text/html"); } bool http_conn::add_linger() { - return add_response("Connection:%s\r\n",(m_linger==true)?"keep-alive":"close"); + return add_response("Connection:%s\r\n", (m_linger == true) ? "keep-alive" : "close"); } bool http_conn::add_blank_line() { - return add_response("%s","\r\n"); + return add_response("%s", "\r\n"); } -bool http_conn::add_content(const char* content) +bool http_conn::add_content(const char *content) { - return add_response("%s",content); + return add_response("%s", content); } bool http_conn::process_write(HTTP_CODE ret) { - switch(ret) + switch (ret) { - case INTERNAL_ERROR: - { - add_status_line(500,error_500_title); - add_headers(strlen(error_500_form)); - if(!add_content(error_500_form)) - return false; - break; - } - case BAD_REQUEST: - { - add_status_line(404,error_404_title); - add_headers(strlen(error_404_form)); - if(!add_content(error_404_form)) - return false; - break; - } - case FORBIDDEN_REQUEST: - { - add_status_line(403,error_403_title); - add_headers(strlen(error_403_form)); - if(!add_content(error_403_form)) - return false; - break; - } - case FILE_REQUEST: - { - add_status_line(200,ok_200_title); - if(m_file_stat.st_size!=0) - { - add_headers(m_file_stat.st_size); - m_iv[0].iov_base=m_write_buf; - m_iv[0].iov_len=m_write_idx; - m_iv[1].iov_base=m_file_address; - m_iv[1].iov_len=m_file_stat.st_size; - m_iv_count=2; - bytes_to_send = m_write_idx + m_file_stat.st_size; - return true; - } - else - { - const char* ok_string=""; - add_headers(strlen(ok_string)); - if(!add_content(ok_string)) - return false; - } - } - default: + case INTERNAL_ERROR: + { + add_status_line(500, error_500_title); + add_headers(strlen(error_500_form)); + if (!add_content(error_500_form)) + return false; + break; + } + case BAD_REQUEST: + { + add_status_line(404, error_404_title); + add_headers(strlen(error_404_form)); + if (!add_content(error_404_form)) return false; + break; + } + case FORBIDDEN_REQUEST: + { + add_status_line(403, error_403_title); + add_headers(strlen(error_403_form)); + if (!add_content(error_403_form)) + return false; + break; + } + case FILE_REQUEST: + { + add_status_line(200, ok_200_title); + if (m_file_stat.st_size != 0) + { + add_headers(m_file_stat.st_size); + m_iv[0].iov_base = m_write_buf; + m_iv[0].iov_len = m_write_idx; + m_iv[1].iov_base = m_file_address; + m_iv[1].iov_len = m_file_stat.st_size; + m_iv_count = 2; + bytes_to_send = m_write_idx + m_file_stat.st_size; + return true; + } + else + { + const char *ok_string = ""; + add_headers(strlen(ok_string)); + if (!add_content(ok_string)) + return false; + } + } + default: + return false; } - m_iv[0].iov_base=m_write_buf; - m_iv[0].iov_len=m_write_idx; - m_iv_count=1; + m_iv[0].iov_base = m_write_buf; + m_iv[0].iov_len = m_write_idx; + m_iv_count = 1; + bytes_to_send = m_write_idx; return true; } void http_conn::process() { - HTTP_CODE read_ret=process_read(); - if(read_ret==NO_REQUEST) + HTTP_CODE read_ret = process_read(); + if (read_ret == NO_REQUEST) { - modfd(m_epollfd,m_sockfd,EPOLLIN); + modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode); return; } - bool write_ret=process_write(read_ret); - if(!write_ret) + bool write_ret = process_write(read_ret); + if (!write_ret) { close_conn(); } - modfd(m_epollfd,m_sockfd,EPOLLOUT); + modfd(m_epollfd, m_sockfd, EPOLLOUT, m_TRIGMode); } diff --git a/http/http_conn.h b/http/http_conn.h index e57a871..95bbe68 100644 --- a/http/http_conn.h +++ b/http/http_conn.h @@ -1,94 +1,147 @@ #ifndef HTTPCONNECTION_H #define HTTPCONNECTION_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include"../lock/locker.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../lock/locker.h" #include "../CGImysql/sql_connection_pool.h" -class http_conn{ - public: - static const int FILENAME_LEN=200; - static const int READ_BUFFER_SIZE=2048; - static const int WRITE_BUFFER_SIZE=1024; - enum METHOD{GET=0,POST,HEAD,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATH}; - enum CHECK_STATE{CHECK_STATE_REQUESTLINE=0,CHECK_STATE_HEADER,CHECK_STATE_CONTENT}; - enum HTTP_CODE{NO_REQUEST,GET_REQUEST,BAD_REQUEST,NO_RESOURCE,FORBIDDEN_REQUEST,FILE_REQUEST,INTERNAL_ERROR,CLOSED_CONNECTION}; - enum LINE_STATUS{LINE_OK=0,LINE_BAD,LINE_OPEN}; - public: - http_conn(){} - ~http_conn(){} - public: - void init(int sockfd,const sockaddr_in &addr); - void close_conn(bool real_close=true); - void process(); - bool read_once(); - bool write(); - sockaddr_in *get_address(){ - return &m_address; - } - void initmysql_result(); - private: - void init(); - HTTP_CODE process_read(); - bool process_write(HTTP_CODE ret); - HTTP_CODE parse_request_line(char *text); - HTTP_CODE parse_headers(char *text); - HTTP_CODE parse_content(char *text); - HTTP_CODE do_request(); - char* get_line(){return m_read_buf+m_start_line;}; - LINE_STATUS parse_line(); - void unmap(); - bool add_response(const char* format,...); - bool add_content(const char* content); - bool add_status_line(int status,const char* title); - bool add_headers(int content_length); - bool add_content_type(); - bool add_content_length(int content_length); - bool add_linger(); - bool add_blank_line(); - public: - static int m_epollfd; - static int m_user_count; - private: - int m_sockfd; - sockaddr_in m_address; - int bytes_have_send; - int bytes_to_send; - char m_read_buf[READ_BUFFER_SIZE]; - int m_read_idx; - int m_checked_idx; - int m_start_line; - char m_write_buf[WRITE_BUFFER_SIZE]; - int m_write_idx; - CHECK_STATE m_check_state; - METHOD m_method; - char m_real_file[FILENAME_LEN]; - char *m_url; - char *m_version; - char *m_host; - int m_content_length; - bool m_linger; - char *m_file_address; - struct stat m_file_stat; - struct iovec m_iv[2]; - int m_iv_count; - int cgi;//是否启用的POST - char *m_string;//存储请求头数据 + +class http_conn +{ +public: + static const int FILENAME_LEN = 200; + static const int READ_BUFFER_SIZE = 2048; + static const int WRITE_BUFFER_SIZE = 1024; + enum METHOD + { + GET = 0, + POST, + HEAD, + PUT, + DELETE, + TRACE, + OPTIONS, + CONNECT, + PATH + }; + enum CHECK_STATE + { + CHECK_STATE_REQUESTLINE = 0, + CHECK_STATE_HEADER, + CHECK_STATE_CONTENT + }; + enum HTTP_CODE + { + NO_REQUEST, + GET_REQUEST, + BAD_REQUEST, + NO_RESOURCE, + FORBIDDEN_REQUEST, + FILE_REQUEST, + INTERNAL_ERROR, + CLOSED_CONNECTION + }; + enum LINE_STATUS + { + LINE_OK = 0, + LINE_BAD, + LINE_OPEN + }; + +public: + http_conn() {} + ~http_conn() {} + +public: + void init(int sockfd, const sockaddr_in &addr, char *, map &, int, int, int, string user, string passwd, string sqlname); + void close_conn(bool real_close = true); + void process(); + bool read_once(); + bool write(); + sockaddr_in *get_address() + { + return &m_address; + } + void initmysql_result(connection_pool *connPool, map &); + void initresultFile(connection_pool *connPool, map &); + +private: + void init(); + HTTP_CODE process_read(); + bool process_write(HTTP_CODE ret); + HTTP_CODE parse_request_line(char *text); + HTTP_CODE parse_headers(char *text); + HTTP_CODE parse_content(char *text); + HTTP_CODE do_request(); + char *get_line() { return m_read_buf + m_start_line; }; + LINE_STATUS parse_line(); + void unmap(); + bool add_response(const char *format, ...); + bool add_content(const char *content); + bool add_status_line(int status, const char *title); + bool add_headers(int content_length); + bool add_content_type(); + bool add_content_length(int content_length); + bool add_linger(); + bool add_blank_line(); + +public: + static int m_epollfd; + static int m_user_count; + MYSQL *mysql; + +private: + int m_sockfd; + sockaddr_in m_address; + char m_read_buf[READ_BUFFER_SIZE]; + int m_read_idx; + int m_checked_idx; + int m_start_line; + char m_write_buf[WRITE_BUFFER_SIZE]; + int m_write_idx; + CHECK_STATE m_check_state; + METHOD m_method; + char m_real_file[FILENAME_LEN]; + char *m_url; + char *m_version; + char *m_host; + int m_content_length; + bool m_linger; + char *m_file_address; + struct stat m_file_stat; + struct iovec m_iv[2]; + int m_iv_count; + int cgi; //是否启用的POST + char *m_string; //存储请求头数据 + int bytes_to_send; + int bytes_have_send; + char *doc_root; + + map m_users; + int m_SQLVerify; + int m_TRIGMode; + int m_close_log; + + char sql_user[100]; + char sql_passwd[100]; + char sql_name[100]; }; + #endif diff --git a/lock/locker.h b/lock/locker.h index 3779ec3..de38a06 100644 --- a/lock/locker.h +++ b/lock/locker.h @@ -1,88 +1,115 @@ #ifndef LOCKER_H #define LOCKER_H -#include -#include -#include +#include +#include +#include -class sem{ - public: - sem() - { - if(sem_init(&m_sem,0,0)!=0){ - throw std::exception(); - } - } - ~sem() - { - sem_destroy(&m_sem); - } - bool wait() +class sem +{ +public: + sem() + { + if (sem_init(&m_sem, 0, 0) != 0) { - return sem_wait(&m_sem)==0; + throw std::exception(); } - bool post() + } + sem(int num) + { + if (sem_init(&m_sem, 0, num) != 0) { - return sem_post(&m_sem)==0; + throw std::exception(); } - private: - sem_t m_sem; + } + ~sem() + { + sem_destroy(&m_sem); + } + bool wait() + { + return sem_wait(&m_sem) == 0; + } + bool post() + { + return sem_post(&m_sem) == 0; + } + +private: + sem_t m_sem; }; class locker { - public: - locker() - { - if(pthread_mutex_init(&m_mutex,NULL)!=0) - { - throw std::exception(); - } - } - ~locker() - { - pthread_mutex_destroy(&m_mutex); - } - bool lock() - { - return pthread_mutex_lock(&m_mutex)==0; - } - bool unlock() +public: + locker() + { + if (pthread_mutex_init(&m_mutex, NULL) != 0) { - return pthread_mutex_unlock(&m_mutex)==0; + throw std::exception(); } - private: - pthread_mutex_t m_mutex; + } + ~locker() + { + pthread_mutex_destroy(&m_mutex); + } + bool lock() + { + return pthread_mutex_lock(&m_mutex) == 0; + } + bool unlock() + { + return pthread_mutex_unlock(&m_mutex) == 0; + } + pthread_mutex_t *get() + { + return &m_mutex; + } + +private: + pthread_mutex_t m_mutex; }; class cond { public: - cond(){ - if(pthread_mutex_init(&m_mutex,NULL)!=0){ - throw std::exception(); - } - if(pthread_cond_init(&m_cond,NULL)!=0){ - pthread_mutex_destroy(&m_mutex); + cond() + { + if (pthread_cond_init(&m_cond, NULL) != 0) + { + //pthread_mutex_destroy(&m_mutex); throw std::exception(); } } - ~cond(){ - pthread_mutex_destroy(&m_mutex); + ~cond() + { pthread_cond_destroy(&m_cond); } - bool wait() + bool wait(pthread_mutex_t *m_mutex) + { + int ret = 0; + //pthread_mutex_lock(&m_mutex); + ret = pthread_cond_wait(&m_cond, m_mutex); + //pthread_mutex_unlock(&m_mutex); + return ret == 0; + } + bool timewait(pthread_mutex_t *m_mutex, struct timespec t) { - int ret=0; - pthread_mutex_lock(&m_mutex); - ret=pthread_cond_wait(&m_cond,&m_mutex); - pthread_mutex_unlock(&m_mutex); - return ret==0; + int ret = 0; + //pthread_mutex_lock(&m_mutex); + ret = pthread_cond_timedwait(&m_cond, m_mutex, &t); + //pthread_mutex_unlock(&m_mutex); + return ret == 0; } bool signal() { - return pthread_cond_signal(&m_cond)==0; + return pthread_cond_signal(&m_cond) == 0; } + bool broadcast() + { + return pthread_cond_broadcast(&m_cond) == 0; + } + private: - pthread_mutex_t m_mutex; + //static pthread_mutex_t m_mutex; pthread_cond_t m_cond; }; #endif diff --git a/log/block_queue.h b/log/block_queue.h index 0076b34..34c77bd 100644 --- a/log/block_queue.h +++ b/log/block_queue.h @@ -3,209 +3,210 @@ *线程安全,每个操作前都要先加互斥锁,操作完后,再解锁 **************************************************************/ -#ifndef BLOCK_QUEUE_H -#define BLOCK_QUEUE_H - -#include -#include -#include -#include -using namespace std; - -template -class block_queue -{ - public: - block_queue(int max_size = 1000) - { - if(max_size <= 0) - { - exit(-1); - } - - m_max_size = max_size; - m_array = new T[max_size]; - m_size = 0; - m_front = -1; - m_back = -1; - //创建互斥锁和条件变量 - m_mutex = new pthread_mutex_t; - m_cond = new pthread_cond_t; - pthread_mutex_init(m_mutex, NULL); - pthread_cond_init(m_cond, NULL); - } - - void clear() - { - pthread_mutex_lock(m_mutex); - m_size = 0; - m_front = -1; - m_back = -1; - pthread_mutex_unlock(m_mutex); - } - - ~block_queue() - { - pthread_mutex_lock(m_mutex); - if(m_array != NULL) - delete m_array; - pthread_mutex_unlock(m_mutex); - - pthread_mutex_destroy(m_mutex); - pthread_cond_destroy(m_cond); - - delete m_mutex; - delete m_cond; - } - //判断队列是否满了 - bool full()const - { - pthread_mutex_lock(m_mutex); - if(m_size >= m_max_size) - { - pthread_mutex_unlock(m_mutex); - return true; - } - pthread_mutex_unlock(m_mutex); - return false; - } - //判断队列是否为空 - bool empty()const - { - pthread_mutex_lock(m_mutex); - if(0 == m_size) - { - pthread_mutex_unlock(m_mutex); - return true; - } - pthread_mutex_unlock(m_mutex); - return false; - } - //返回队首元素 - bool front(T& value)const - { - pthread_mutex_lock(m_mutex); - if(0 == m_size) - { - pthread_mutex_unlock(m_mutex); - return false; - } - value = m_array[m_front]; - pthread_mutex_unlock(m_mutex); - return true; - } - //返回队尾元素 - bool back(T& value)const - { - pthread_mutex_lock(m_mutex); - if(0 == m_size) - { - pthread_mutex_unlock(m_mutex); - return false; - } - value = m_array[m_back]; - pthread_mutex_unlock(m_mutex); - return true; - } - - int size()const - { - int tmp = 0; - pthread_mutex_lock(m_mutex); - tmp = m_size; - pthread_mutex_unlock(m_mutex); - return tmp; - } - - int max_size()const - { - int tmp = 0; - pthread_mutex_lock(m_mutex); - tmp = m_max_size; - pthread_mutex_unlock(m_mutex); - return tmp; - } - //往队列添加元素,需要将所有使用队列的线程先唤醒 - //当有元素push进队列,相当于生产者生产了一个元素 - //若当前没有线程等待条件变量,则唤醒无意义 - bool push(const T& item) - { - pthread_mutex_lock(m_mutex); - if(m_size >= m_max_size) - { - pthread_cond_broadcast(m_cond); - pthread_mutex_unlock(m_mutex); - return false; - } - - m_back = (m_back + 1) % m_max_size; - m_array[m_back] = item; - - m_size++; - pthread_cond_broadcast(m_cond); - pthread_mutex_unlock(m_mutex); - - return true; - } - //pop时,如果当前队列没有元素,将会等待条件变量 - bool pop(T& item) - { - pthread_mutex_lock(m_mutex); - while(m_size <= 0) - { - // - if(0 != pthread_cond_wait(m_cond, m_mutex)) - { - pthread_mutex_unlock(m_mutex); - return false; - } - } - - m_front = (m_front + 1) % m_max_size; - item = m_array[m_front]; - m_size--; - pthread_mutex_unlock(m_mutex); - return true; - } - - //增加了超时处理 - bool pop(T& item, int ms_timeout) - { - struct timespec t = {0,0}; - struct timeval now = {0,0}; - gettimeofday(&now, NULL); - pthread_mutex_lock(m_mutex); - if(m_size <= 0) - { - t.tv_sec = now.tv_sec + ms_timeout/1000; - t.tv_nsec = (ms_timeout % 1000)*1000; - if(0 != pthread_cond_timedwait(m_cond, m_mutex, &t)) - { - pthread_mutex_unlock(m_mutex); - return false; - } - } - - if(m_size <= 0) - { - pthread_mutex_unlock(m_mutex); - return false; - } - - m_front = (m_front + 1) % m_max_size; - item = m_array[m_front];m_size--; - pthread_mutex_unlock(m_mutex); - return true; - } - -private: - pthread_mutex_t *m_mutex; - pthread_cond_t *m_cond; - T *m_array; - int m_size; - int m_max_size; - int m_front; - int m_back; -}; - -#endif +#ifndef BLOCK_QUEUE_H +#define BLOCK_QUEUE_H + +#include +#include +#include +#include +#include "../lock/locker.h" +using namespace std; + +template +class block_queue +{ +public: + block_queue(int max_size = 1000) + { + if (max_size <= 0) + { + exit(-1); + } + + m_max_size = max_size; + m_array = new T[max_size]; + m_size = 0; + m_front = -1; + m_back = -1; + } + + void clear() + { + m_mutex.lock(); + m_size = 0; + m_front = -1; + m_back = -1; + m_mutex.unlock(); + } + + ~block_queue() + { + m_mutex.lock(); + if (m_array != NULL) + delete [] m_array; + + m_mutex.unlock(); + } + //判断队列是否满了 + bool full() + { + m_mutex.lock(); + if (m_size >= m_max_size) + { + + m_mutex.unlock(); + return true; + } + m_mutex.unlock(); + return false; + } + //判断队列是否为空 + bool empty() + { + m_mutex.lock(); + if (0 == m_size) + { + m_mutex.unlock(); + return true; + } + m_mutex.unlock(); + return false; + } + //返回队首元素 + bool front(T &value) + { + m_mutex.lock(); + if (0 == m_size) + { + m_mutex.unlock(); + return false; + } + value = m_array[m_front]; + m_mutex.unlock(); + return true; + } + //返回队尾元素 + bool back(T &value) + { + m_mutex.lock(); + if (0 == m_size) + { + m_mutex.unlock(); + return false; + } + value = m_array[m_back]; + m_mutex.unlock(); + return true; + } + + int size() + { + int tmp = 0; + + m_mutex.lock(); + tmp = m_size; + + m_mutex.unlock(); + return tmp; + } + + int max_size() + { + int tmp = 0; + + m_mutex.lock(); + tmp = m_max_size; + + m_mutex.unlock(); + return tmp; + } + //往队列添加元素,需要将所有使用队列的线程先唤醒 + //当有元素push进队列,相当于生产者生产了一个元素 + //若当前没有线程等待条件变量,则唤醒无意义 + bool push(const T &item) + { + + m_mutex.lock(); + if (m_size >= m_max_size) + { + + m_cond.broadcast(); + m_mutex.unlock(); + return false; + } + + m_back = (m_back + 1) % m_max_size; + m_array[m_back] = item; + + m_size++; + + m_cond.broadcast(); + m_mutex.unlock(); + return true; + } + //pop时,如果当前队列没有元素,将会等待条件变量 + bool pop(T &item) + { + + m_mutex.lock(); + while (m_size <= 0) + { + + if (!m_cond.wait(m_mutex.get())) + { + m_mutex.unlock(); + return false; + } + } + + m_front = (m_front + 1) % m_max_size; + item = m_array[m_front]; + m_size--; + m_mutex.unlock(); + return true; + } + + //增加了超时处理 + bool pop(T &item, int ms_timeout) + { + struct timespec t = {0, 0}; + struct timeval now = {0, 0}; + gettimeofday(&now, NULL); + m_mutex.lock(); + if (m_size <= 0) + { + t.tv_sec = now.tv_sec + ms_timeout / 1000; + t.tv_nsec = (ms_timeout % 1000) * 1000; + if (!m_cond.timewait(m_mutex.get(), t)) + { + m_mutex.unlock(); + return false; + } + } + + if (m_size <= 0) + { + m_mutex.unlock(); + return false; + } + + m_front = (m_front + 1) % m_max_size; + item = m_array[m_front]; + m_size--; + m_mutex.unlock(); + return true; + } + +private: + locker m_mutex; + cond m_cond; + + T *m_array; + int m_size; + int m_max_size; + int m_front; + int m_back; +}; + +#endif diff --git a/log/log.cpp b/log/log.cpp index 4f7f754..c309813 100644 --- a/log/log.cpp +++ b/log/log.cpp @@ -1,169 +1,164 @@ -#include -#include -#include -#include -#include "log.h" -#include -using namespace std; - -//默认构造函数,创建互斥锁,初始化是否同步标志位 -Log::Log() -{ - m_count = 0; - m_is_async = false; - pthread_mutex_init(m_mutex, NULL); -} - -Log::~Log() -{ - if(m_fp != NULL) - { - fclose(m_fp); - } - pthread_mutex_destroy(m_mutex); - - if(m_mutex != NULL) - { - delete m_mutex; - } -} +#include +#include +#include +#include +#include "log.h" +#include +using namespace std; + +Log::Log() +{ + m_count = 0; + m_is_async = false; +} + +Log::~Log() +{ + if (m_fp != NULL) + { + fclose(m_fp); + } +} //异步需要设置阻塞队列的长度,同步不需要设置 -bool Log::init(const char* file_name, int log_buf_size, int split_lines, int max_queue_size) -{ +bool Log::init(const char *file_name, int close_log, int log_buf_size, int split_lines, int max_queue_size) +{ //如果设置了max_queue_size,则设置为异步 - if(max_queue_size >= 1) - { - m_is_async = true; - m_log_queue = new block_queue(max_queue_size); - pthread_t tid; - //flush_log_thread为回调函数,这里表示创建线程异步写日志 - pthread_create(&tid, NULL, flush_log_thread, NULL); - } - - m_log_buf_size = log_buf_size; - m_buf = new char[m_log_buf_size]; - memset(m_buf, '\0', sizeof(m_buf)); - m_split_lines = split_lines; - - time_t t = time(NULL); - struct tm* sys_tm = localtime(&t); - struct tm my_tm = *sys_tm; - - //从后往前找到第一个/的位置 - const char *p = strrchr(file_name, '/'); - char log_full_name[256] = {0}; - //相当于自定义日志名 - //若输入的文件名没有/,则直接将时间+文件名作为日志名 - if(p == NULL) - { - 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); - } - else - { - //将/的位置向后移动一个位置,然后复制到logname中 - //p - file_name + 1是文件所在路径文件夹的长度 - //dirname相当于./ - strcpy(log_name, p+1); + if (max_queue_size >= 1) + { + m_is_async = true; + m_log_queue = new block_queue(max_queue_size); + pthread_t tid; + //flush_log_thread为回调函数,这里表示创建线程异步写日志 + pthread_create(&tid, NULL, flush_log_thread, NULL); + } + + m_close_log = close_log; + m_log_buf_size = log_buf_size; + m_buf = new char[m_log_buf_size]; + memset(m_buf, '\0', sizeof(m_buf)); + m_split_lines = split_lines; + + time_t t = time(NULL); + struct tm *sys_tm = localtime(&t); + struct tm my_tm = *sys_tm; + + + const char *p = strrchr(file_name, '/'); + char log_full_name[256] = {0}; + + if (p == NULL) + { + 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); + } + else + { + strcpy(log_name, p + 1); strncpy(dir_name, file_name, p - file_name + 1); - //后面的参数跟format有关 - 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 ); - } - - m_today = my_tm.tm_mday; - - m_fp = fopen(log_full_name, "a"); - if(m_fp == NULL) - { - return false; - } - - return true; -} - -void Log::write_log(int level, const char* format, ...) -{ - struct timeval now = {0,0}; - gettimeofday(&now, NULL); - time_t t = now.tv_sec; - struct tm* sys_tm = localtime(&t); - struct tm my_tm = *sys_tm; - char s[16] = {0}; - switch(level) - { - case 0 : strcpy(s, "[debug]:"); break; - case 1 : strcpy(s, "[info]:"); break; - case 2 : strcpy(s, "[warn]:"); break; - case 3 : strcpy(s, "[erro]:"); break; - default: - strcpy(s, "[info]:"); break; - } + 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); + } + + m_today = my_tm.tm_mday; + + m_fp = fopen(log_full_name, "a"); + if (m_fp == NULL) + { + return false; + } + + return true; +} + +void Log::write_log(int level, const char *format, ...) +{ + struct timeval now = {0, 0}; + gettimeofday(&now, NULL); + time_t t = now.tv_sec; + struct tm *sys_tm = localtime(&t); + struct tm my_tm = *sys_tm; + char s[16] = {0}; + switch (level) + { + case 0: + strcpy(s, "[debug]:"); + break; + case 1: + strcpy(s, "[info]:"); + break; + case 2: + strcpy(s, "[warn]:"); + break; + case 3: + strcpy(s, "[erro]:"); + break; + default: + strcpy(s, "[info]:"); + break; + } //写入一个log,对m_count++, m_split_lines最大行数 - pthread_mutex_lock(m_mutex); - m_count++; - //my_tm.tm_mday为每次写日志的时候判断当前时间,m_today是创建文件的时候记录的时间 - if(m_today != my_tm.tm_mday || m_count % m_split_lines == 0) //everyday log - { - //判断写入的日志行数超出了最大行数,若超过了最大行数,则新建文件 - //每次写日志时,需要判断当前日志,是不是今天的日志,如果不是今天的日志,需要将当前打开的关闭,再重新打开 - char new_log[256] = {0}; - fflush(m_fp); - fclose(m_fp); - char tail[16] = {0}; - //先把时间头写好 - snprintf(tail, 16, "%d_%02d_%02d_", my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday); - //如果是时间不是今天,则创建今天的日志,否则是超过了最大行,还是之前的日志名,只不过加了后缀, m_count/m_split_lines - if(m_today != my_tm.tm_mday) - { - snprintf(new_log, 255, "%s%s%s", dir_name, tail, log_name); - m_today = my_tm.tm_mday; - m_count = 0; - } - else - { - snprintf(new_log, 255, "%s%s%s.%lld", dir_name, tail, log_name, m_count/m_split_lines); - } - m_fp = fopen(new_log, "a"); - } - pthread_mutex_unlock(m_mutex); - - va_list valst; - va_start(valst, format); - - string log_str; - pthread_mutex_lock(m_mutex); + m_mutex.lock(); + m_count++; + + if (m_today != my_tm.tm_mday || m_count % m_split_lines == 0) //everyday log + { + + char new_log[256] = {0}; + fflush(m_fp); + fclose(m_fp); + char tail[16] = {0}; + + snprintf(tail, 16, "%d_%02d_%02d_", my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday); + + if (m_today != my_tm.tm_mday) + { + snprintf(new_log, 255, "%s%s%s", dir_name, tail, log_name); + m_today = my_tm.tm_mday; + m_count = 0; + } + else + { + snprintf(new_log, 255, "%s%s%s.%lld", dir_name, tail, log_name, m_count / m_split_lines); + } + m_fp = fopen(new_log, "a"); + } + m_mutex.unlock(); + + va_list valst; + va_start(valst, format); + + string log_str; + m_mutex.lock(); + //写入的具体时间内容格式 - //snprintf成功返回写字符的总数,其中不包括结尾的null字符 - int n = snprintf(m_buf, 48, "%d-%02d-%02d %02d:%02d:%02d.%06ld %s ", - my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday, - my_tm.tm_hour, my_tm.tm_min, my_tm.tm_sec, now.tv_usec, s); - //属于可变参数。用于向字符串中打印数据、数据格式用户自定义,返回写入到字符数组str中的字符个数(不包含终止符),最大不超过size - int m = vsnprintf(m_buf + n, m_log_buf_size-1, format, valst); + int n = snprintf(m_buf, 48, "%d-%02d-%02d %02d:%02d:%02d.%06ld %s ", + my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday, + my_tm.tm_hour, my_tm.tm_min, my_tm.tm_sec, now.tv_usec, s); + + int m = vsnprintf(m_buf + n, m_log_buf_size - 1, format, valst); m_buf[n + m] = '\n'; - m_buf[n+m+1]='\0'; - log_str = m_buf; - pthread_mutex_unlock(m_mutex); - - //若m_is_async为true表示不同步,默认为同步 - //若异步,则将日志信息加入阻塞队列,同步则加锁向文件中写 - if(m_is_async && !m_log_queue->full()) - { - m_log_queue->push(log_str); - } - else - { - pthread_mutex_lock(m_mutex); + m_buf[n + m + 1] = '\0'; + log_str = m_buf; + + m_mutex.unlock(); + + if (m_is_async && !m_log_queue->full()) + { + m_log_queue->push(log_str); + } + else + { + m_mutex.lock(); fputs(log_str.c_str(), m_fp); - pthread_mutex_unlock(m_mutex); - } - - va_end(valst); -} - -void Log::flush(void) -{ - pthread_mutex_lock(m_mutex); - //强制刷新写入流缓冲区 - fflush(m_fp); - pthread_mutex_unlock(m_mutex); -} + m_mutex.unlock(); + } + + va_end(valst); +} + +void Log::flush(void) +{ + m_mutex.lock(); + //强制刷新写入流缓冲区 + fflush(m_fp); + m_mutex.unlock(); +} diff --git a/log/log.h b/log/log.h index 951020b..8751759 100644 --- a/log/log.h +++ b/log/log.h @@ -1,70 +1,70 @@ -#ifndef LOG_H -#define LOG_H - -#include -#include -#include -#include -#include -#include "block_queue.h" -using namespace std; - -static pthread_mutex_t *m_mutex = new pthread_mutex_t; //互斥锁 -class Log -{ - public: - static Log* get_instance() - { - pthread_mutex_lock(m_mutex); - static Log instance; - pthread_mutex_unlock(m_mutex); - return &instance; - } - - static void *flush_log_thread(void* args) - { - Log::get_instance()->async_write_log(); - } - //可选择的参数有日志文件、日志缓冲区大小、最大行数以及最长日志条队列 - bool init(const char* file_name, int log_buf_size = 8192, int split_lines = 5000000, int max_queue_size = 0); - - void write_log(int level, const char* format, ...); - - void flush(void); - - private: - Log(); - virtual ~Log(); - void *async_write_log() - { - string single_log; - //从阻塞队列中取出一个日志string,写入文件 - while(m_log_queue->pop(single_log)) - { - pthread_mutex_lock(m_mutex); - fputs(single_log.c_str(), m_fp); - pthread_mutex_unlock(m_mutex); - } - } - - private: - char dir_name[128]; //路径名 - char log_name[128]; //log文件名 - int m_split_lines; //日志最大行数 - int m_log_buf_size; //日志缓冲区大小 - long long m_count; //日志行数记录 - int m_today; //因为按天分类,记录当前时间是那一天 - FILE *m_fp; //打开log的文件指针 - char *m_buf; - block_queue *m_log_queue; //阻塞队列 - bool m_is_async; //是否同步标志位 -}; +#ifndef LOG_H +#define LOG_H -//__VA_ARGS__ 是一个可变参数的宏,实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点) - -#define LOG_DEBUG(format, ...) Log::get_instance()->write_log(0, format, __VA_ARGS__) -#define LOG_INFO(format, ...) Log::get_instance()->write_log(1, format, __VA_ARGS__) -#define LOG_WARN(format, ...) Log::get_instance()->write_log(2, format, __VA_ARGS__) -#define LOG_ERROR(format, ...) Log::get_instance()->write_log(3, format, __VA_ARGS__) - -#endif +#include +#include +#include +#include +#include +#include "block_queue.h" + +using namespace std; + +class Log +{ +public: + //C++11以后,使用局部变量懒汉不用加锁 + static Log *get_instance() + { + static Log instance; + return &instance; + } + + static void *flush_log_thread(void *args) + { + Log::get_instance()->async_write_log(); + } + //可选择的参数有日志文件、日志缓冲区大小、最大行数以及最长日志条队列 + bool init(const char *file_name, int close_log, int log_buf_size = 8192, int split_lines = 5000000, int max_queue_size = 0); + + void write_log(int level, const char *format, ...); + + void flush(void); + +private: + Log(); + virtual ~Log(); + void *async_write_log() + { + string single_log; + //从阻塞队列中取出一个日志string,写入文件 + while (m_log_queue->pop(single_log)) + { + m_mutex.lock(); + fputs(single_log.c_str(), m_fp); + m_mutex.unlock(); + } + } + +private: + char dir_name[128]; //路径名 + char log_name[128]; //log文件名 + int m_split_lines; //日志最大行数 + int m_log_buf_size; //日志缓冲区大小 + long long m_count; //日志行数记录 + int m_today; //因为按天分类,记录当前时间是那一天 + FILE *m_fp; //打开log的文件指针 + char *m_buf; + block_queue *m_log_queue; //阻塞队列 + bool m_is_async; //是否同步标志位 + locker m_mutex; + int m_close_log; //关闭日志 +}; + + +#define LOG_DEBUG(format, ...) if(0 == m_close_log) Log::get_instance()->write_log(0, format, ##__VA_ARGS__) +#define LOG_INFO(format, ...) if(0 == m_close_log) Log::get_instance()->write_log(1, format, ##__VA_ARGS__) +#define LOG_WARN(format, ...) if(0 == m_close_log) Log::get_instance()->write_log(2, format, ##__VA_ARGS__) +#define LOG_ERROR(format, ...) if(0 == m_close_log) Log::get_instance()->write_log(3, format, ##__VA_ARGS__) + +#endif diff --git a/main.c b/main.c index 832b3f9..2e8a3e1 100644 --- a/main.c +++ b/main.c @@ -1,329 +1,36 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "config.h" -#include "./lock/locker.h" -#include "./threadpool/threadpool.h" -#include "./timer/lst_timer.h" -#include "./http/http_conn.h" -#include "./log/log.h" -#include "./CGImysql/sql_connection_pool.h" - -#define MAX_FD 65536 //最大文件描述符 -#define MAX_EVENT_NUMBER 10000 //最大事件数 -#define TIMESLOT 5 //最小超时单位 - -//这三个函数在http_conn.cpp中定义,改变链接属性 -extern int addfd(int epollfd,int fd,bool one_shot); -extern int remove(int epollfd,int fd); -extern int setnonblocking( int fd ); - -//设置定时器相关参数 -static int pipefd[2]; -static sort_timer_lst timer_lst; -static int epollfd = 0; - -//信号处理函数 -void sig_handler( int sig ) +int main(int argc, char *argv[]) { - //为保证函数的可重入性,保留原来的errno - int save_errno = errno; - int msg = sig; - send( pipefd[1], ( char* )&msg, 1, 0 ); - errno = save_errno; -} - -//设置信号函数 -void addsig(int sig,void(handler)(int),bool restart=true) -{ - struct sigaction sa; - memset(&sa,'\0',sizeof(sa)); - sa.sa_handler=handler; - if(restart) - sa.sa_flags |= SA_RESTART; - sigfillset(&sa.sa_mask); - assert(sigaction(sig,&sa,NULL)!=-1); -} - -//定时处理任务,重新定时以不断触发SIGALRM信号 -void timer_handler() -{ - timer_lst.tick(); - alarm( TIMESLOT ); -} - -//定时器回调函数,删除非活动连接在socket上的注册事件,并关闭 -void cb_func( client_data* user_data ) -{ - epoll_ctl( epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0 ); - assert( user_data ); - close( user_data->sockfd ); - LOG_INFO("close fd %d", user_data->sockfd); - Log::get_instance()->flush(); - //printf( "close fd %d\n", user_data->sockfd ); -} - -void show_error(int connfd,const char* info) -{ - printf("%s",info); - send(connfd,info,strlen(info),0); - close(connfd); -} + //需要修改的数据库信息,登录名,密码,库名 + string user = "root"; + string passwd = "root"; + string databasename = "qgydb"; -//设置信号为LT阻塞模式 -void addfd_(int epollfd,int fd,bool one_shot) -{ - epoll_event event; - event.data.fd=fd; - event.events=EPOLLIN|EPOLLRDHUP; - if(one_shot) - event.events|=EPOLLONESHOT; - epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event); -} + //命令行解析 + Config config; + config.parse_arg(argc, argv); -int main(int argc,char *argv[]) -{ - - //Log::get_instance()->init("./mylog.log",8192,2000000,10);//异步日志模型 - Log::get_instance()->init("./mylog.log",8192,2000000,0);//同步日志模型 + WebServer server; - if(argc<=1) - { - printf("usage: %s ip_address port_number\n",basename(argv[0])); - return 1; - } - //const char* ip=argv[1]; - int port=atoi(argv[1]); + //初始化 + server.init(config.PORT, user, passwd, databasename, config.LOGWrite, config.SQLVerify, + config.OPT_LINGER, config.TRIGMode, config.sql_num, config.thread_num, config.close_log); - //忽略SIGPIPE信号 - addsig(SIGPIPE,SIG_IGN); + //日志 + server.log_write(); - //创建线程池 - threadpool* pool=NULL; - try - { - pool=new threadpool; - } - catch(...){ - return 1; - } - //单例模式创建数据库连接池 - connection_pool *connPool=connection_pool::GetInstance("localhost","root","root","qgydb",3306,5); - - http_conn* users=new http_conn[MAX_FD]; - assert(users); - int user_count=0; - - //初始化数据库读取表 - users->initmysql_result(); - - //创建套接字,返回listenfd - int listenfd=socket(PF_INET,SOCK_STREAM,0); - assert(listenfd>=0); - //struct linger tmp={1,0}; - - //SO_LINGER若有数据待发送,延迟关闭 - //setsockopt(listenfd,SOL_SOCKET,SO_LINGER,&tmp,sizeof(tmp)); - int ret=0; - struct sockaddr_in address; - bzero(&address,sizeof(address)); - address.sin_family=AF_INET; - //inet_pton(AF_INET,ip,&address.sin_addr); - address.sin_addr.s_addr=htonl(INADDR_ANY); - address.sin_port=htons(port); - - // 设置端口复用,绑定端口 - int flag = 1; - setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); - ret=bind(listenfd,(struct sockaddr*)&address,sizeof(address)); - //printf("bind ret = %d\n", ret); - assert(ret>=0); - ret=listen(listenfd,5); - assert(ret>=0); + //数据库 + server.sql_pool(); - //创建内核事件表 - epoll_event events[MAX_EVENT_NUMBER]; - int epollfd=epoll_create(5); - assert(epollfd!=-1); - addfd_(epollfd,listenfd,false); - http_conn::m_epollfd=epollfd; - - //创建管道 - ret = socketpair( PF_UNIX, SOCK_STREAM, 0, pipefd ); - assert( ret != -1 ); - setnonblocking( pipefd[1] ); - addfd( epollfd, pipefd[0], false); - - // add all the interesting signals here - addsig(SIGALRM, sig_handler, false); - addsig(SIGTERM, sig_handler, false); - bool stop_server = false; - - client_data* users_timer = new client_data[MAX_FD]; - bool timeout = false; - alarm( TIMESLOT ); - - //printf("监听......\n"); - - while(!stop_server) - { - int number=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1); - if(number<0&&errno!=EINTR) - { - //printf("epoll failure\n"); - LOG_ERROR("%s","epoll failure"); - break; - } - - for(int i=0;i=MAX_FD) - { - show_error(connfd,"Internal server busy"); - LOG_ERROR("%s","Internal server busy"); - continue; - } - users[connfd].init(connfd,client_address); - - //初始化client_data数据 - //创建定时器,设置回调函数和超时时间,绑定用户数据,将定时器添加到链表中 - users_timer[connfd].address = client_address; - users_timer[connfd].sockfd = connfd; - util_timer* timer = new util_timer; - timer->user_data = &users_timer[connfd]; - timer->cb_func = cb_func; - time_t cur = time( NULL ); - timer->expire = cur + 3 * TIMESLOT; - users_timer[connfd].timer = timer; - timer_lst.add_timer( timer ); - } - - else if(events[i].events & (EPOLLRDHUP | EPOLLHUP|EPOLLERR)) - { - users[sockfd].close_conn(); - - //服务器端关闭连接,移除对应的定时器 - cb_func( &users_timer[sockfd] ); - util_timer *timer=users_timer[sockfd].timer; - if( timer ) - { - timer_lst.del_timer( timer ); - } - } + //线程池 + server.thread_pool(); - //处理信号 - else if( ( sockfd == pipefd[0] ) && ( events[i].events & EPOLLIN ) ) - { - int sig; - char signals[1024]; - ret = recv( pipefd[0], signals, sizeof( signals ), 0 ); - if( ret == -1 ) - { - // handle the error - continue; - } - else if( ret == 0 ) - { - continue; - } - else - { - for( int i = 0; i < ret; ++i ) - { - switch( signals[i] ) - { - case SIGALRM: - { - timeout = true; - break; - } - case SIGTERM: - { - stop_server = true; - } - } - } - } - } + //监听 + server.eventListen(); - //处理客户连接上接收到的数据 - else if(events[i].events&EPOLLIN) - { - util_timer* timer = users_timer[sockfd].timer; - if(users[sockfd].read_once()){ - LOG_INFO("deal with the client(%s)",inet_ntoa(users[sockfd].get_address()->sin_addr)); - Log::get_instance()->flush(); - //若监测到读事件,将该事件放入请求队列 - pool->append(users+sockfd); + //运行 + server.eventLoop(); - //若有数据传输,则将定时器往后延迟3个单位 - //并对新的定时器在链表上的位置进行调整 - if( timer ) - { - time_t cur = time( NULL ); - timer->expire = cur + 3 * TIMESLOT; - //printf( "adjust timer once\n" ); - LOG_INFO("%s","adjust timer once"); - Log::get_instance()->flush(); - timer_lst.adjust_timer( timer ); - } - } - else - { - users[sockfd].close_conn(); - cb_func( &users_timer[sockfd] ); - if( timer ) - { - timer_lst.del_timer( timer ); - } - } - } - else if(events[i].events&EPOLLOUT) - { - - if(!users[sockfd].write()) - users[sockfd].close_conn(); - } - //else - //{ - //} - - } - if(timeout){ - timer_handler(); - timeout=false; - } - } - close(epollfd); - close(listenfd); - close(pipefd[1]); - close(pipefd[0]); - delete [] users; - delete [] users_timer; - delete pool; - //销毁数据库连接池 - connPool->DestroyPool(); return 0; } - diff --git a/makefile b/makefile index 86b4bdf..91924f9 100644 --- a/makefile +++ b/makefile @@ -1,9 +1,9 @@ -server: main.c ./threadpool/threadpool.h ./http/http_conn.cpp ./http/http_conn.h ./lock/locker.h ./log/log.cpp ./log/log.h ./log/block_queue.h ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h - g++ -o server main.c ./threadpool/threadpool.h ./http/http_conn.cpp ./http/http_conn.h ./lock/locker.h ./log/log.cpp ./log/log.h ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h -lpthread -lmysqlclient +server: main.c ./timer/lst_timer.h ./timer/lst_timer.cpp ./threadpool/threadpool.h ./http/http_conn.cpp ./http/http_conn.h ./lock/locker.h ./log/log.cpp ./log/log.h ./log/block_queue.h ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h webserver.h webserver.cpp config.h config.cpp + g++ -o server main.c ./timer/lst_timer.h ./timer/lst_timer.cpp ./threadpool/threadpool.h ./http/http_conn.cpp ./http/http_conn.h ./lock/locker.h ./log/log.cpp ./log/log.h ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h webserver.h webserver.cpp config.h config.cpp -lpthread -lmysqlclient -check.cgi:./CGImysql/sign.cpp ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h - g++ -o check.cgi ./CGImysql/sign.cpp ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h -lmysqlclient +CGISQL.cgi:./CGImysql/sign.cpp ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h + g++ -o ./root/CGISQL.cgi ./CGImysql/sign.cpp ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h -lmysqlclient -lpthread clean: rm -r server - rm -r check.cgi + rm -r ./root/CGISQL.cgi diff --git a/root/CGISQL.cgi b/root/CGISQL.cgi new file mode 100644 index 0000000..b626080 Binary files /dev/null and b/root/CGISQL.cgi differ diff --git a/root/ET.png b/root/ET.png new file mode 100644 index 0000000..d6dbe97 Binary files /dev/null and b/root/ET.png differ diff --git a/root/LT.png b/root/LT.png new file mode 100644 index 0000000..c50fc39 Binary files /dev/null and b/root/LT.png differ diff --git a/root/README.md b/root/README.md new file mode 100644 index 0000000..d6fdb70 --- /dev/null +++ b/root/README.md @@ -0,0 +1,10 @@ +界面跳转 +=============== +对html中action行为设置标志位,将method设置为POST +> * 0 注册 +> * 1 登录 +> * 2 登录检测 +> * 3 注册检测 +> * 5 请求图片 +> * 6 请求视频 +> * 7 关注我 diff --git a/root/fans.html b/root/fans.html new file mode 100644 index 0000000..b7c6179 --- /dev/null +++ b/root/fans.html @@ -0,0 +1,15 @@ + + + + + awsl + +
+
+
嘿嘿,你来啦,更多资料,请关注 “两猿社” 喔.
+
+ +
+
+
+ diff --git a/root/judge.html b/root/judge.html index 97e8a08..82fc2d7 100644 --- a/root/judge.html +++ b/root/judge.html @@ -9,7 +9,6 @@
欢迎访问

-
diff --git a/root/log.html b/root/log.html index a16d26d..b0d95e0 100644 --- a/root/log.html +++ b/root/log.html @@ -10,8 +10,7 @@
登录