├── CGI ├── add.cpp ├── adder └── cgi.html ├── README.md ├── test.cpp ├── webServer1.0 ├── 404.html ├── CGI │ ├── add.cpp │ ├── adder │ └── cgi.html ├── favicon.ico ├── img.jpg ├── index.html ├── locker.h ├── makefile ├── sign1.png ├── submit.html ├── task.h ├── test.cpp ├── threadPool.h ├── webServer ├── webServer.cpp └── webServer.h └── webServer2.0 ├── 404.html ├── CGI ├── add.cpp ├── adder └── cgi.html ├── README.md ├── favicon.ico ├── index.html ├── locker.h ├── makefile ├── picture ├── img.jpg └── sign1.png ├── server.cpp ├── submit.html ├── task.cpp ├── task.h ├── threadPool.h ├── webServer.cpp └── webServer.h /CGI/add.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | int main( int argc, char **argv ) { 11 | int a, b, accp_fd; 12 | sscanf( argv[0], "%d+%d,%d", &a, &b, &accp_fd ); 13 | 14 | int t = a+b; 15 | string response = "HTTP/1.1 200 OK\r\nContent-Type: text/html;charset=utf-8\r\n\r\n"; 16 | string body = "niliushall's CGI"; 17 | body += "

The result is " + to_string(a) + "+" + to_string(b) + " = " + to_string(t); 18 | body += "

"; 19 | response += body; 20 | dup2(accp_fd, STDOUT_FILENO); 21 | cout << response.c_str(); 22 | // send( accp_fd, response.c_str(), response.size(), 0 ); 23 | return 0; 24 | } -------------------------------------------------------------------------------- /CGI/adder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/CGI/adder -------------------------------------------------------------------------------- /CGI/cgi.html: -------------------------------------------------------------------------------- 1 | niliushall's CGI

The result is 1+2 = 3

-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 项目概述 2 | 3 | 该项目是一个简单的webServer,使用 `epoll + threadpool` 实现,支持GET、POST方法,可以实现html、jpg、png、ico、MP3、js、css等文件的解析,在POST方法的处理中使用CGI服务器进行计算并返回结果。 4 | 5 | ### 使用说明 6 | 7 | 该项目使用时,需要在目录中放置一个mp3文件,并命名为1.mp3,具体位置为webServer2.0目录下。使用make进行编译连接源文件,使用:`./server port`运行程序。其中,`port`为端口号。 8 | 9 | 以8080端口为例,终端输入: 10 | 11 | ``` 12 | ./server 8080 13 | ``` 14 | 15 | 打开浏览器,输入: 16 | 17 | ``` 18 | localhost:8080 19 | ``` 20 | 21 | 即可看到网页显示。 22 | 23 | ### 版本说明 24 | 25 | - `test.cpp`为最初版本,使用 `epoll + 多线程` 实现,可以完成基础功能,支持GET方法,可传输html、jpg、png、mp3 26 | - `webServer1.0`使用 `threadpool` 实现,支持GET、POST方法,可以解析html、jpg、png、ico、MP3、js、css等,并加入了CGI服务器,可以稳定运行 27 | - `webServer2.0`使用 `epoll + threadpool`实现,支持GET、POST方法,可以解析html、jpg、png、ico、MP3、js、css等,使用CGI服务器进行数据的计算并返回网页给浏览器,可以稳定运行 28 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | /* 采用 epoll+多线程 实现,可使用GET,可传输html、jpg、png、mp3 */ 2 | 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 | 21 | using namespace std; 22 | 23 | const int max_event_num = 20; 24 | 25 | class Server { 26 | private: 27 | int port; 28 | int sock_fd; 29 | int epoll_fd; 30 | struct sockaddr_in server_addr; 31 | public: 32 | Server( int p ) : sock_fd(0), port(p) { memset( &server_addr, 0, sizeof( server_addr ) ); } 33 | ~Server() { close( sock_fd ); } 34 | int accept_connection(); 35 | static void * decode_request( void * accp_fd ); 36 | static int send_file( int accp_fd, const string & filename, const string & type ); 37 | static int send_400( int accp_fd ); 38 | }; 39 | 40 | int setnonblocking( int fd ) { 41 | int old_option = fcntl( fd, F_GETFL ); 42 | int new_option = old_option | O_NONBLOCK; 43 | fcntl( fd, F_SETFL, new_option ); 44 | return old_option; 45 | } 46 | 47 | 48 | void addfd( int epoll_fd, bool oneshot, int fd ) { 49 | epoll_event event; 50 | event.data.fd = fd; 51 | event.events = EPOLLIN | EPOLLET; 52 | if( oneshot ) { 53 | event.events |= EPOLLONESHOT; 54 | } 55 | epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event ); 56 | setnonblocking( fd ); 57 | } 58 | 59 | void removefd( int epollfd, int fd ) { 60 | epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 ); 61 | close( fd ); 62 | } 63 | 64 | int Server::accept_connection() { 65 | server_addr.sin_family = AF_INET; 66 | server_addr.sin_port = htons( port ); 67 | server_addr.sin_addr.s_addr = htonl( INADDR_ANY ); 68 | 69 | sock_fd = socket( AF_INET, SOCK_STREAM, 0 ); 70 | if( sock_fd < 0 ) { 71 | cout << "socket error, line " << __LINE__ << endl; 72 | exit(-1); 73 | } 74 | 75 | int ret = bind( sock_fd, (struct sockaddr *)&server_addr, sizeof( server_addr ) ); 76 | if( ret < 0 ) { 77 | cout << "bind error, line " << __LINE__ << endl; 78 | exit(-1); 79 | } 80 | 81 | ret = listen( sock_fd, 5 ); 82 | if( ret < 0 ) { 83 | cout << "listen error, line " << __LINE__ << endl; 84 | exit(-1); 85 | } 86 | 87 | epoll_event events[max_event_num]; 88 | epoll_fd = epoll_create(1024); 89 | if( epoll_fd < 0 ) { 90 | cout << "epoll_create error, line: " << __LINE__ << endl; 91 | exit(-1); 92 | } 93 | addfd( epoll_fd, false, sock_fd ); 94 | 95 | while( true ) { 96 | ret = epoll_wait( epoll_fd, events, max_event_num, -1 ); 97 | if( ret < 0 ) { 98 | cout << "epoll_wait error, line: " << __LINE__ << endl; 99 | return -1; 100 | } 101 | for( int i = 0; i < ret; i++ ) { 102 | int fd = events[i].data.fd; 103 | if( fd == sock_fd ) { //新连接 104 | struct sockaddr_in client_addr; 105 | socklen_t client_addr_size = sizeof( client_addr ); 106 | int conn_fd = accept( fd, (struct sockaddr *)&client_addr, &client_addr_size ); 107 | if( conn_fd < 0 ) { 108 | cout << "accept error, line: " << __LINE__ << endl; 109 | return -1; 110 | } 111 | addfd(epoll_fd, true, conn_fd); 112 | // cout << "add\n"; 113 | } else if( events[i].events & EPOLLIN ) { 114 | // cout << "\nEPOLLIN\n"; 115 | pthread_t thread; 116 | pthread_create( &thread, NULL, decode_request, (void *)&fd ); 117 | } else { 118 | cout << "\nother\n"; 119 | } 120 | } 121 | } 122 | 123 | close( sock_fd ); 124 | // cout << "exit\n"; 125 | return 0; 126 | } 127 | 128 | void * Server::decode_request( void * conn_fd ) { 129 | char buf[1024] = {0}; 130 | int accp_fd = *(int *)conn_fd; 131 | 132 | int r = recv( accp_fd, buf, 1024, 0 ); 133 | if( !r ) { 134 | cout << "browser exit.\n"; 135 | close( accp_fd ); 136 | // removefd( epoll_fd, accp_fd ); 137 | return 0; 138 | } 139 | 140 | string method, uri, version; 141 | stringstream ss; 142 | ss.clear(); 143 | ss << buf; 144 | ss >> method >> uri >> version; 145 | 146 | cout << "method = " << method << endl; 147 | cout << "uri = " << uri << endl; 148 | cout << "version = " << version << endl << endl; 149 | 150 | cout << "request = ------\n" << buf << "\n------------" << endl; 151 | 152 | // sleep(1); 153 | 154 | if( method == "GET" ) { //为GET 155 | if( uri == "/" || uri == "/index.html" ) { 156 | send_file( accp_fd, "index.html", "text/html" ); 157 | } else if( uri.find( ".jpg" ) != string::npos || uri.find( ".png" ) != string::npos ) { 158 | send_file( accp_fd, uri.substr(1), "image/jpg" ); 159 | } else if( uri.find( ".html" ) != string::npos ) { 160 | send_file( accp_fd, uri.substr(1), "text/html" ); 161 | } else if( uri.find( ".ico" ) != string::npos ) { 162 | send_file( accp_fd, uri.substr(1), "image/x-icon" ); 163 | } else if( uri.find( ".js" ) != string::npos ) { 164 | send_file( accp_fd, uri.substr(1), "yexy/javascript" ); 165 | } else if( uri.find( ".css" ) != string::npos ) { 166 | send_file( accp_fd, uri.substr(1), "text/css" ); 167 | } else if( uri.find( ".mp3" ) != string::npos ) { 168 | send_file( accp_fd, uri.substr(1), "audio/mp3" ); 169 | } else { 170 | string status( "HTTP/1.1 404 Not Found\r\n" ); 171 | string header( "Server: niliushall\r\nContent-Type: text/plain;charset=utf-8\r\n\r\n" ); 172 | status += header; 173 | send( accp_fd, status.c_str(), status.size(), 0 ); 174 | } 175 | } else if( method == "POST" ) { 176 | if( uri.find( "adder" ) != string::npos ) { 177 | string filename = uri.substr(1); 178 | char *tmp = buf; 179 | int len, a, b; 180 | char *l = strstr( tmp, "Content-Length:" ); 181 | sscanf( l, "Content-Length: %d", &len ); 182 | len = strlen( tmp ) - len; 183 | tmp += len; 184 | sscanf( tmp, "a=%d&b=%d", &a, &b ); 185 | sprintf(tmp, "%d+%d,%d", a, b, accp_fd); 186 | if( fork() == 0 ) { 187 | // dup2( accp_fd, STDOUT_FILENO ); 188 | execl( filename.c_str(), tmp ); 189 | } 190 | wait( NULL ); 191 | } 192 | } else { 193 | send_400( accp_fd ); 194 | } 195 | close( accp_fd ); 196 | } 197 | 198 | int Server::send_file( int accp_fd, const string & filename, const string & type ) { 199 | //? header为什么不能是\r\n\r\n 200 | string status( "HTTP/1.1 200 OK\r\n" ); 201 | string header = "Server: niliushall\r\nContent-Type: " + type + ";charset=utf-8\r\n"; 202 | string body, t; 203 | 204 | ifstream in( filename ); 205 | while( getline( in, t ) ) { 206 | body += '\n'; 207 | body += t; 208 | } 209 | in.close(); 210 | // cout << "\nsend:\n" << body << endl << endl; 211 | // send第二个参数只能是c类型字符串,不能使用string 212 | status += header + body; 213 | send( accp_fd, status.c_str(), status.size(), 0 ); 214 | // cout << filename << " send finish to " << accp_fd << endl; 215 | close( accp_fd ); 216 | return 0; 217 | } 218 | 219 | int Server::send_400( int accp_fd ) { 220 | string response = "HTTP/1.1 200 METHOD ERROR\r\nServer: niliushall\r\nContent-Type: text/plain"; 221 | send( accp_fd, response.c_str(), response.size(), 0 ); 222 | close( accp_fd ); 223 | } 224 | 225 | int main( int argc, char **argv ) { 226 | if( argc != 2 ) { 227 | cout << "Usage : ./a.out + port\n"; 228 | return -1; 229 | } 230 | 231 | int port = atoi( argv[1] ); 232 | Server test( port ); 233 | test.accept_connection(); 234 | } -------------------------------------------------------------------------------- /webServer1.0/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 404 4 | 5 | 6 |

WTF

7 |

emmm,迷路了兄弟!!!

8 | 9 | -------------------------------------------------------------------------------- /webServer1.0/CGI/add.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | int main( int argc, char **argv ) { 11 | int a, b, accp_fd; 12 | sscanf( argv[0], "%d+%d,%d", &a, &b, &accp_fd ); 13 | 14 | int t = a+b; 15 | string response = "HTTP/1.1 200 OK\r\nContent-Type: text/html;charset=utf-8\r\n\r\n"; 16 | string body = "niliushall's CGI"; 17 | body += "

The result is " + to_string(a) + "+" + to_string(b) + " = " + to_string(t); 18 | body += "

"; 19 | response += body; 20 | dup2(accp_fd, STDOUT_FILENO); 21 | cout << response.c_str(); 22 | // send( accp_fd, response.c_str(), response.size(), 0 ); 23 | return 0; 24 | } -------------------------------------------------------------------------------- /webServer1.0/CGI/adder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/webServer1.0/CGI/adder -------------------------------------------------------------------------------- /webServer1.0/CGI/cgi.html: -------------------------------------------------------------------------------- 1 | niliushall's CGI

The result is 1+2 = 3

-------------------------------------------------------------------------------- /webServer1.0/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/webServer1.0/favicon.ico -------------------------------------------------------------------------------- /webServer1.0/img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/webServer1.0/img.jpg -------------------------------------------------------------------------------- /webServer1.0/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | niliushall's first webServer 5 | 6 | 7 | 8 | 9 |

10 | Welcome to niluishall's web server! 11 |

12 |

13 | hahaha 14 |

15 | 16 | 17 | 18 | 19 |

最佳歌手

20 | 21 |
22 | submit 23 | 24 | 25 | -------------------------------------------------------------------------------- /webServer1.0/locker.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOCKER_H_ 2 | #define _LOCKER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | /* 线程锁 */ 10 | class MutexLocker { 11 | private: 12 | pthread_mutex_t m_mutex; 13 | public: 14 | MutexLocker() { //初始化 15 | if( pthread_mutex_init( &m_mutex, NULL ) ) { 16 | cout << "mutex init errror __ 1\n"; 17 | throw exception(); 18 | } 19 | } 20 | 21 | ~MutexLocker() { 22 | pthread_mutex_destroy( &m_mutex ); 23 | } 24 | 25 | bool mutex_lock() { 26 | return pthread_mutex_lock( &m_mutex ) == 0; 27 | } 28 | 29 | bool mutex_unlock() { 30 | return pthread_mutex_unlock( &m_mutex ); 31 | } 32 | }; 33 | 34 | 35 | /* 条件变量 */ 36 | class Cond { 37 | private: 38 | pthread_mutex_t m_mutex; 39 | pthread_cond_t m_cond; 40 | public: 41 | Cond() { 42 | if( pthread_mutex_init( &m_mutex, NULL ) ) { 43 | throw exception(); 44 | } 45 | if( pthread_cond_init( &m_cond, NULL ) ) { 46 | pthread_cond_destroy( &m_cond ); 47 | throw exception(); 48 | } 49 | } 50 | 51 | ~Cond() { 52 | pthread_mutex_destroy( &m_mutex ); 53 | pthread_cond_destroy( &m_cond ); 54 | } 55 | 56 | // 等待条件变量,cond与mutex搭配使用,避免造成共享数据的混乱 57 | bool wait() { 58 | pthread_mutex_lock( &m_mutex ); 59 | int ret = pthread_cond_wait( &m_cond, &m_mutex ); 60 | pthread_mutex_unlock( &m_mutex ); 61 | return ret == 0; 62 | } 63 | 64 | // 唤醒等待该条件变量的某个线程 65 | bool signal() { 66 | return pthread_cond_signal( &m_cond ) == 0; 67 | } 68 | 69 | // 唤醒所有等待该条件变量的线程 70 | bool broadcast() { 71 | return pthread_cond_broadcast( &m_cond ) == 0; 72 | } 73 | }; 74 | 75 | #endif -------------------------------------------------------------------------------- /webServer1.0/makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | webServer: webServer.o 4 | g++ -o webServer webServer.cpp -lpthread 5 | 6 | clean: 7 | rm *.o -------------------------------------------------------------------------------- /webServer1.0/sign1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/webServer1.0/sign1.png -------------------------------------------------------------------------------- /webServer1.0/submit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | request test 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /webServer1.0/task.h: -------------------------------------------------------------------------------- 1 | #ifndef _TASK_H_ 2 | #define _TASK_H_ 3 | 4 | #include "threadPool.h" 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 | using namespace std; 21 | 22 | const int buffer_size = 1024; 23 | 24 | class Task { 25 | private: 26 | int accp_fd; 27 | 28 | public: 29 | Task() {} 30 | Task(int fd) : accp_fd(fd) {} 31 | ~Task() { close( accp_fd ); } 32 | 33 | void doit(); 34 | const string construct_header( const int num, const string & info, 35 | const string & type ); 36 | int send_file( const string & filename, const string & type, 37 | const int num = 200, const string & info = "OK" ); 38 | int deal_get( const string & uri ); 39 | int deal_post( const string & uri, char *buf ); 40 | int get_size( const string & filename ); 41 | }; 42 | 43 | void Task::doit() { 44 | char buf[ buffer_size ] = {0}; 45 | 46 | while( int r = recv( accp_fd, buf, 1024, 0 ) ) { 47 | // cout << "r = " << r << endl; 48 | // sleep(1); 49 | if( !r ) { 50 | cout << "browser exit.\n"; 51 | // removefd( epoll_fd, accp_fd ); 52 | return; 53 | } else if( r < 0 ) { //如果接收出错则继续接收数据 54 | continue; 55 | } 56 | 57 | string method, uri, version; 58 | stringstream ss; 59 | // ss.clear(); 60 | ss << buf; 61 | ss >> method >> uri >> version; 62 | 63 | cout << "method = " << method << endl; 64 | cout << "uri = " << uri << endl; 65 | cout << "version = " << version << endl << endl; 66 | 67 | // cout << "request = ------\n" << buf << "\n------------" << endl; 68 | 69 | // sleep(1); 70 | 71 | if( method == "GET" ) { //为GET 72 | deal_get( uri ); 73 | } else if( method == "POST" ) { 74 | deal_post( uri, buf ); 75 | } else { 76 | string header = construct_header( 501, "Not Implemented", "text/plain" ); 77 | send( accp_fd, header.c_str(), header.size(), 0 ); 78 | } 79 | break; // 只要处理完就退出循环,避免浏览器一直处于pending状态 80 | } 81 | // close( accp_fd ); // 任务完成直接close,不能在析构函数close(如果不delete task的话, 82 | // 不delete task不够条用析构函数) 83 | } 84 | 85 | const string Task::construct_header( const int num, 86 | const string & info, const string & type ) { 87 | string response = "HTTP/1.1 " + to_string(num) + " " + info + "\r\n"; 88 | response += "Server: niliushall\r\nContent-Type: " + type + ";charset=utf-8\r\n\r\n"; 89 | return response; 90 | } 91 | 92 | int Task::deal_get( const string & uri ) { 93 | string filename = uri.substr(1); 94 | 95 | if( uri == "/" || uri == "/index.html" ) { 96 | send_file( "index.html", "text/html" ); 97 | } else if( uri.find( ".jpg" ) != string::npos || uri.find( ".png" ) != string::npos ) { 98 | send_file( filename, "image/jpg" ); 99 | } else if( uri.find( ".html" ) != string::npos ) { 100 | send_file( filename, "text/html" ); 101 | } else if( uri.find( ".ico" ) != string::npos ) { 102 | send_file( filename, "image/x-icon" ); 103 | } else if( uri.find( ".js" ) != string::npos ) { 104 | send_file( filename, "yexy/javascript" ); 105 | } else if( uri.find( ".css" ) != string::npos ) { 106 | send_file( filename, "text/css" ); 107 | } else if( uri.find( ".mp3" ) != string::npos ) { 108 | send_file( filename, "audio/mp3" ); 109 | } else { 110 | send_file( "404.html", "text/html", 404, "Not Found" ); 111 | } 112 | } 113 | 114 | int Task::deal_post( const string & uri, char *buf ) { 115 | string filename = uri.substr(1); 116 | if( uri.find( "adder" ) != string::npos ) { 117 | char *tmp = buf; 118 | int len, a, b; 119 | char *l = strstr( tmp, "Content-Length:" ); 120 | sscanf( l, "Content-Length: %d", &len ); 121 | len = strlen( tmp ) - len; 122 | tmp += len; 123 | sscanf( tmp, "a=%d&b=%d", &a, &b ); 124 | sprintf(tmp, "%d+%d,%d", a, b, accp_fd); 125 | if( fork() == 0 ) { 126 | // dup2( accp_fd, STDOUT_FILENO ); 127 | execl( filename.c_str(), tmp, NULL ); 128 | } 129 | wait( NULL ); 130 | } else { 131 | // 132 | } 133 | } 134 | 135 | int Task::send_file( const string & filename, const string & type, 136 | const int num, const string & info ) { 137 | string header = construct_header( num, info, type ); 138 | 139 | // send第二个参数只能是c类型字符串,不能使用string 140 | send( accp_fd, header.c_str(), header.size(), 0 ); 141 | 142 | struct stat filestat; 143 | int ret = stat( filename.c_str(), &filestat ); 144 | if( ret < 0 || !S_ISREG( filestat.st_mode ) ) { 145 | cout << "file not found : " << filename << endl; 146 | send_file( "404.html", "text/html", 404, "Not Found" ); 147 | return -1; 148 | } 149 | 150 | if( !filename.empty() ) { 151 | int fd = open( filename.c_str(), O_RDONLY ); 152 | sendfile( accp_fd, fd, NULL, get_size( filename ) ); 153 | close( fd ); 154 | } 155 | cout << filename << " send finish to " << accp_fd << endl; 156 | return 0; 157 | } 158 | 159 | int Task::get_size( const string & filename ) { 160 | struct stat filestat; 161 | int ret = stat( filename.c_str(), &filestat ); 162 | if( ret < 0 ) { 163 | cout << "file not found : " << filename << endl; 164 | return 0; 165 | } 166 | return filestat.st_size; 167 | } 168 | 169 | #endif -------------------------------------------------------------------------------- /webServer1.0/test.cpp: -------------------------------------------------------------------------------- 1 | /* 采用 epoll+多线程 实现,可使用GET,可传输html、jpg、png、mp3 */ 2 | 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 | 21 | using namespace std; 22 | 23 | const int max_event_num = 20; 24 | 25 | class Server { 26 | private: 27 | int port; 28 | int sock_fd; 29 | int epoll_fd; 30 | struct sockaddr_in server_addr; 31 | public: 32 | Server( int p ) : sock_fd(0), port(p) { memset( &server_addr, 0, sizeof( server_addr ) ); } 33 | ~Server() { close( sock_fd ); } 34 | int accept_connection(); 35 | static void * decode_request( void * accp_fd ); 36 | static int send_file( int accp_fd, const string & filename, const string & type ); 37 | static int send_400( int accp_fd ); 38 | }; 39 | 40 | int setnonblocking( int fd ) { 41 | int old_option = fcntl( fd, F_GETFL ); 42 | int new_option = old_option | O_NONBLOCK; 43 | fcntl( fd, F_SETFL, new_option ); 44 | return old_option; 45 | } 46 | 47 | 48 | void addfd( int epoll_fd, bool oneshot, int fd ) { 49 | epoll_event event; 50 | event.data.fd = fd; 51 | event.events = EPOLLIN | EPOLLET; 52 | if( oneshot ) { 53 | event.events |= EPOLLONESHOT; 54 | } 55 | epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event ); 56 | setnonblocking( fd ); 57 | } 58 | 59 | void removefd( int epollfd, int fd ) { 60 | epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 ); 61 | close( fd ); 62 | } 63 | 64 | int Server::accept_connection() { 65 | server_addr.sin_family = AF_INET; 66 | server_addr.sin_port = htons( port ); 67 | server_addr.sin_addr.s_addr = htonl( INADDR_ANY ); 68 | 69 | sock_fd = socket( AF_INET, SOCK_STREAM, 0 ); 70 | if( sock_fd < 0 ) { 71 | cout << "socket error, line " << __LINE__ << endl; 72 | exit(-1); 73 | } 74 | 75 | int ret = bind( sock_fd, (struct sockaddr *)&server_addr, sizeof( server_addr ) ); 76 | if( ret < 0 ) { 77 | cout << "bind error, line " << __LINE__ << endl; 78 | exit(-1); 79 | } 80 | 81 | ret = listen( sock_fd, 5 ); 82 | if( ret < 0 ) { 83 | cout << "listen error, line " << __LINE__ << endl; 84 | exit(-1); 85 | } 86 | 87 | epoll_event events[max_event_num]; 88 | epoll_fd = epoll_create(1024); 89 | if( epoll_fd < 0 ) { 90 | cout << "epoll_create error, line: " << __LINE__ << endl; 91 | exit(-1); 92 | } 93 | addfd( epoll_fd, false, sock_fd ); 94 | 95 | while( true ) { 96 | ret = epoll_wait( epoll_fd, events, max_event_num, -1 ); 97 | if( ret < 0 ) { 98 | cout << "epoll_wait error, line: " << __LINE__ << endl; 99 | return -1; 100 | } 101 | for( int i = 0; i < ret; i++ ) { 102 | int fd = events[i].data.fd; 103 | if( fd == sock_fd ) { //新连接 104 | struct sockaddr_in client_addr; 105 | socklen_t client_addr_size = sizeof( client_addr ); 106 | int conn_fd = accept( fd, (struct sockaddr *)&client_addr, &client_addr_size ); 107 | if( conn_fd < 0 ) { 108 | cout << "accept error, line: " << __LINE__ << endl; 109 | return -1; 110 | } 111 | addfd(epoll_fd, true, conn_fd); 112 | // cout << "add\n"; 113 | } else if( events[i].events & EPOLLIN ) { 114 | // cout << "\nEPOLLIN\n"; 115 | pthread_t thread; 116 | pthread_create( &thread, NULL, decode_request, (void *)&fd ); 117 | } else { 118 | cout << "\nother\n"; 119 | } 120 | } 121 | } 122 | 123 | close( sock_fd ); 124 | // cout << "exit\n"; 125 | return 0; 126 | } 127 | 128 | void * Server::decode_request( void * conn_fd ) { 129 | char buf[1024] = {0}; 130 | int accp_fd = *(int *)conn_fd; 131 | 132 | int r = recv( accp_fd, buf, 1024, 0 ); 133 | if( !r ) { 134 | cout << "browser exit.\n"; 135 | close( accp_fd ); 136 | // removefd( epoll_fd, accp_fd ); 137 | return 0; 138 | } 139 | 140 | string method, uri, version; 141 | stringstream ss; 142 | ss.clear(); 143 | ss << buf; 144 | ss >> method >> uri >> version; 145 | 146 | cout << "method = " << method << endl; 147 | cout << "uri = " << uri << endl; 148 | cout << "version = " << version << endl << endl; 149 | 150 | cout << "request = ------\n" << buf << "\n------------" << endl; 151 | 152 | // sleep(1); 153 | 154 | if( method == "GET" ) { //为GET 155 | if( uri == "/" || uri == "/index.html" ) { 156 | send_file( accp_fd, "index.html", "text/html" ); 157 | } else if( uri.find( ".jpg" ) != string::npos || uri.find( ".png" ) != string::npos ) { 158 | send_file( accp_fd, uri.substr(1), "image/jpg" ); 159 | } else if( uri.find( ".html" ) != string::npos ) { 160 | send_file( accp_fd, uri.substr(1), "text/html" ); 161 | } else if( uri.find( ".ico" ) != string::npos ) { 162 | send_file( accp_fd, uri.substr(1), "image/x-icon" ); 163 | } else if( uri.find( ".js" ) != string::npos ) { 164 | send_file( accp_fd, uri.substr(1), "yexy/javascript" ); 165 | } else if( uri.find( ".css" ) != string::npos ) { 166 | send_file( accp_fd, uri.substr(1), "text/css" ); 167 | } else if( uri.find( ".mp3" ) != string::npos ) { 168 | send_file( accp_fd, uri.substr(1), "audio/mp3" ); 169 | } else { 170 | string status( "HTTP/1.1 404 Not Found\r\n" ); 171 | string header( "Server: niliushall\r\nContent-Type: text/plain;charset=utf-8\r\n\r\n" ); 172 | status += header; 173 | send( accp_fd, status.c_str(), status.size(), 0 ); 174 | } 175 | } else if( method == "POST" ) { 176 | if( uri.find( "adder" ) != string::npos ) { 177 | string filename = uri.substr(1); 178 | char *tmp = buf; 179 | int len, a, b; 180 | char *l = strstr( tmp, "Content-Length:" ); 181 | sscanf( l, "Content-Length: %d", &len ); 182 | len = strlen( tmp ) - len; 183 | tmp += len; 184 | sscanf( tmp, "a=%d&b=%d", &a, &b ); 185 | sprintf(tmp, "%d+%d,%d", a, b, accp_fd); 186 | if( fork() == 0 ) { 187 | // dup2( accp_fd, STDOUT_FILENO ); 188 | execl( filename.c_str(), tmp ); 189 | } 190 | wait( NULL ); 191 | } 192 | } else { 193 | send_400( accp_fd ); 194 | } 195 | close( accp_fd ); 196 | } 197 | 198 | int Server::send_file( int accp_fd, const string & filename, const string & type ) { 199 | //? header为什么不能是\r\n\r\n 200 | string status( "HTTP/1.1 200 OK\r\n" ); 201 | string header = "Server: niliushall\r\nContent-Type: " + type + ";charset=utf-8\r\n"; 202 | string body, t; 203 | 204 | ifstream in( filename ); 205 | while( getline( in, t ) ) { 206 | body += '\n'; 207 | body += t; 208 | } 209 | in.close(); 210 | // cout << "\nsend:\n" << body << endl << endl; 211 | // send第二个参数只能是c类型字符串,不能使用string 212 | status += header + body; 213 | send( accp_fd, status.c_str(), status.size(), 0 ); 214 | // cout << filename << " send finish to " << accp_fd << endl; 215 | close( accp_fd ); 216 | return 0; 217 | } 218 | 219 | int Server::send_400( int accp_fd ) { 220 | string response = "HTTP/1.1 200 METHOD ERROR\r\nServer: niliushall\r\nContent-Type: text/plain"; 221 | send( accp_fd, response.c_str(), response.size(), 0 ); 222 | close( accp_fd ); 223 | } 224 | 225 | int main( int argc, char **argv ) { 226 | if( argc != 2 ) { 227 | cout << "Usage : ./a.out + port\n"; 228 | return -1; 229 | } 230 | 231 | int port = atoi( argv[1] ); 232 | Server test( port ); 233 | test.accept_connection(); 234 | } -------------------------------------------------------------------------------- /webServer1.0/threadPool.h: -------------------------------------------------------------------------------- 1 | #ifndef _THREADPOOL_H_ 2 | #define _THREADPOOL_H_ 3 | 4 | #include "locker.h" 5 | #include 6 | using namespace std; 7 | 8 | template< typename T > 9 | class ThreadPool { 10 | private: 11 | int thread_number; //线程池的线程数 12 | pthread_t *threads; //线程数组 13 | queue task_queue; //任务队列 14 | MutexLocker queue_mutex_locker; //任务队列的互斥锁 15 | Cond queue_cond_locker; //任务队列的条件变量 16 | bool m_stop; //是否结束线程 17 | public: 18 | ThreadPool( int thread_num = 20 ); 19 | ~ThreadPool(); 20 | bool append( T *task ); 21 | private: 22 | static void *worker( void * ); 23 | void run(); 24 | T *getTask(); 25 | }; 26 | 27 | template< typename T > 28 | ThreadPool::ThreadPool( int thread_num ) :thread_number(thread_num), 29 | threads(NULL), m_stop(false) { 30 | if( thread_number < 0 ) { 31 | cout << "thread_number < 0\n"; 32 | throw exception(); 33 | } 34 | 35 | // 创建数组存放线程号 36 | threads = new pthread_t[ thread_number ]; 37 | if( !threads ) { 38 | cout << "threads is NULL\n"; 39 | throw exception(); 40 | } 41 | 42 | // 创建规定数量的线程 43 | for( int i = 0; i < thread_number; i++ ) { 44 | // 由于pthread_create第三个参数必须指向静态函数,要使用类成员函数和变量,只能通过: 45 | // 1) 类的静态对象 46 | // 2) 将类的对象作为参数传给静态函数 47 | // 这里通过第二种方法实现 48 | if( pthread_create( &threads[i], NULL, worker, this ) ) { // 成功返回0 49 | delete[] threads; // 创建失败则释放所有已分配的空间 50 | cout << "pthread_create error\n"; 51 | throw exception(); 52 | } 53 | 54 | if( pthread_detach( threads[i] ) ) { 55 | delete[] threads; 56 | cout << "pthread_detach error\n"; 57 | throw exception(); 58 | } 59 | } 60 | } 61 | 62 | template< typename T > 63 | ThreadPool::~ThreadPool() { 64 | delete[] threads; 65 | m_stop = true; 66 | queue_cond_locker.broadcast(); 67 | } 68 | 69 | /* 添加任务时需要先上锁,并判断队列是否为空 */ 70 | template< typename T > 71 | bool ThreadPool::append( T *task ) { 72 | queue_mutex_locker.mutex_lock(); 73 | bool need_signal = task_queue.empty(); // 记录添加任务之前队列是否为空 74 | task_queue.push( task ); 75 | queue_mutex_locker.mutex_unlock(); 76 | 77 | // 如果添加任务之前队列为空,即所有线程都在wait,所以需要唤醒某个线程 78 | if( need_signal ) { 79 | queue_cond_locker.signal(); 80 | } 81 | 82 | return true; 83 | } 84 | 85 | template< typename T > 86 | void * ThreadPool::worker( void *arg ) { 87 | ThreadPool *pool = ( ThreadPool * )arg; 88 | pool->run(); 89 | return pool; 90 | } 91 | 92 | template< typename T > 93 | T* ThreadPool::getTask() { 94 | T *task = NULL; 95 | queue_mutex_locker.mutex_lock(); 96 | if( !task_queue.empty() ) { 97 | cout << "count = " << task_queue.size() << endl; 98 | task = task_queue.front(); 99 | task_queue.pop(); 100 | cout << "count = " << task_queue.size() << endl; 101 | } 102 | queue_mutex_locker.mutex_unlock(); 103 | 104 | return task; 105 | } 106 | 107 | template< typename T > 108 | void ThreadPool::run() { 109 | while( !m_stop ) { 110 | T *task = getTask(); 111 | if( !task ) { 112 | queue_cond_locker.wait(); 113 | } else { 114 | task->doit(); 115 | delete task; //task指向的对象在WebServer中new出来,因此需要手动delete 116 | } 117 | } 118 | } 119 | 120 | #endif -------------------------------------------------------------------------------- /webServer1.0/webServer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/webServer1.0/webServer -------------------------------------------------------------------------------- /webServer1.0/webServer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 采用线程池实现,含有GET、POST, 可以发送html、picture、MP3、js、css、ico 3 | 服务器可以稳定运行 4 | */ 5 | 6 | #include "webServer.h" 7 | #include "task.h" 8 | 9 | int main( int argc, char **argv ) { 10 | if( argc != 2 ) { 11 | cout << "Usage : webServer + port\n"; 12 | return -1; 13 | } 14 | 15 | int port = atoi( argv[1] ); 16 | WebServer webServer( port ); 17 | webServer.run(); 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /webServer1.0/webServer.h: -------------------------------------------------------------------------------- 1 | #ifndef _WEBSERVER_H_ 2 | #define _WEBSERVER_H_ 3 | 4 | #include "task.h" 5 | using namespace std; 6 | 7 | const int max_event_num = 20; 8 | 9 | class WebServer { 10 | private: 11 | int port; 12 | int sock_fd; 13 | int epoll_fd; 14 | struct sockaddr_in server_addr; 15 | public: 16 | WebServer( int p ) : sock_fd(0), port(p) { memset( &server_addr, 0, sizeof( server_addr ) ); } 17 | ~WebServer() { close( sock_fd ); } 18 | int run(); 19 | }; 20 | 21 | int setnonblocking( int fd ) { 22 | int old_option = fcntl( fd, F_GETFL ); 23 | int new_option = old_option | O_NONBLOCK; 24 | fcntl( fd, F_SETFL, new_option ); 25 | return old_option; 26 | } 27 | 28 | 29 | void addfd( int epoll_fd, bool oneshot, int fd ) { 30 | epoll_event event; 31 | event.data.fd = fd; 32 | event.events = EPOLLIN | EPOLLET; 33 | if( oneshot ) { 34 | event.events |= EPOLLONESHOT; 35 | } 36 | epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event ); 37 | setnonblocking( fd ); 38 | } 39 | 40 | void removefd( int epollfd, int fd ) { 41 | epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 ); 42 | close( fd ); 43 | } 44 | 45 | int WebServer::run() { 46 | server_addr.sin_family = AF_INET; 47 | server_addr.sin_port = htons( port ); 48 | server_addr.sin_addr.s_addr = htonl( INADDR_ANY ); 49 | 50 | sock_fd = socket( AF_INET, SOCK_STREAM, 0 ); 51 | if( sock_fd < 0 ) { 52 | cout << "socket error, line " << __LINE__ << endl; 53 | return -1; 54 | } 55 | 56 | int ret = bind( sock_fd, (struct sockaddr *)&server_addr, sizeof( server_addr ) ); 57 | if( ret < 0 ) { 58 | cout << "bind error, line " << __LINE__ << endl; 59 | return -1; 60 | } 61 | 62 | ret = listen( sock_fd, 5 ); 63 | if( ret < 0 ) { 64 | cout << "listen error, line " << __LINE__ << endl; 65 | return -1; 66 | } 67 | 68 | ThreadPool threadpool(20); 69 | 70 | /* ThreadPool< Task > *threadpool; 71 | try { 72 | threadpool = new ThreadPool(20); 73 | } catch(...) { 74 | cout << "init threadpool error\n"; 75 | return -1; 76 | } */ 77 | 78 | while(1) { 79 | struct sockaddr_in client_addr; 80 | socklen_t client_addr_size = sizeof( client_addr ); 81 | int conn_fd = accept( sock_fd, (struct sockaddr *)&client_addr, &client_addr_size ); 82 | if( conn_fd < 0 ) { 83 | cout << "accept error, line: " << __LINE__ << endl; 84 | exit(-1); 85 | } 86 | Task *task = new Task(conn_fd); // 不能使用Task task(conn_fd), 87 | // 需要使用new,否则造成线程函数还没运行完,Task就被析构 88 | // 在threadpool中,task结束后,进行delete 89 | threadpool.append( task ); 90 | } 91 | return 0; 92 | } 93 | 94 | #endif -------------------------------------------------------------------------------- /webServer2.0/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 404 4 | 5 | 6 |

WTF

7 |

emmm,迷路了兄弟!!!

8 | 9 | -------------------------------------------------------------------------------- /webServer2.0/CGI/add.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | int main( int argc, char **argv ) { 11 | int a, b, accp_fd; 12 | sscanf( argv[0], "%d+%d,%d", &a, &b, &accp_fd ); 13 | 14 | int t = a+b; 15 | string response = "HTTP/1.1 200 OK\r\nContent-Type: text/html;charset=utf-8\r\n\r\n"; 16 | string body = "niliushall's CGI"; 17 | body += "

The result is " + to_string(a) + "+" + to_string(b) + " = " + to_string(t); 18 | body += "

"; 19 | response += body; 20 | dup2(accp_fd, STDOUT_FILENO); 21 | cout << response.c_str(); 22 | // send( accp_fd, response.c_str(), response.size(), 0 ); 23 | return 0; 24 | } -------------------------------------------------------------------------------- /webServer2.0/CGI/adder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/webServer2.0/CGI/adder -------------------------------------------------------------------------------- /webServer2.0/CGI/cgi.html: -------------------------------------------------------------------------------- 1 | niliushall's CGI

The result is 1+2 = 3

-------------------------------------------------------------------------------- /webServer2.0/README.md: -------------------------------------------------------------------------------- 1 | ### 项目说明 2 | 3 | 该项目 webServer2.0 使用 epoll + 线程池实现,可以实现GET、POST方法的部分内容 4 | 的解析,可以解析的文件类型有html、js、css、jpg、png、ico、mp3等。服务器可以 5 | 稳定的运行,关闭连接由客户端发起。 6 | 7 | ### 使用说明 8 | 9 | 该项目使用时,需要在目录中放置一个mp3文件,并命名为1.mp3,具体位置为 10 | webServer2.0目录下。使用make进行编译连接源文件,使用`./server port`运行 11 | 程序。其中,`port`为端口号。以8080端口为例,终端输入`./server 8080`,打 12 | 开浏览器,输入`localhost:8080`,即可看到网页显示。 13 | -------------------------------------------------------------------------------- /webServer2.0/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/webServer2.0/favicon.ico -------------------------------------------------------------------------------- /webServer2.0/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | niliushall's first webServer 5 | 6 | 7 | 8 | 9 |

10 | Welcome to niluishall's web server! 11 |

12 |

13 | hahaha 14 |

15 | 16 | 17 | 18 | 19 |

最佳歌手

20 | 21 |
22 | submit 23 | 24 | 25 | -------------------------------------------------------------------------------- /webServer2.0/locker.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOCKER_H_ 2 | #define _LOCKER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | /* 线程锁 */ 10 | class MutexLocker { 11 | private: 12 | pthread_mutex_t m_mutex; 13 | public: 14 | MutexLocker() { //初始化 15 | if( pthread_mutex_init( &m_mutex, NULL ) ) { 16 | cout << "mutex init errror __ 1\n"; 17 | throw exception(); 18 | } 19 | } 20 | 21 | ~MutexLocker() { 22 | pthread_mutex_destroy( &m_mutex ); 23 | } 24 | 25 | bool mutex_lock() { 26 | return pthread_mutex_lock( &m_mutex ) == 0; 27 | } 28 | 29 | bool mutex_unlock() { 30 | return pthread_mutex_unlock( &m_mutex ); 31 | } 32 | }; 33 | 34 | 35 | /* 条件变量 */ 36 | class Cond { 37 | private: 38 | pthread_mutex_t m_mutex; 39 | pthread_cond_t m_cond; 40 | public: 41 | Cond() { 42 | if( pthread_mutex_init( &m_mutex, NULL ) ) { 43 | throw exception(); 44 | } 45 | if( pthread_cond_init( &m_cond, NULL ) ) { 46 | pthread_cond_destroy( &m_cond ); 47 | throw exception(); 48 | } 49 | } 50 | 51 | ~Cond() { 52 | pthread_mutex_destroy( &m_mutex ); 53 | pthread_cond_destroy( &m_cond ); 54 | } 55 | 56 | // 等待条件变量,cond与mutex搭配使用,避免造成共享数据的混乱 57 | bool wait() { 58 | pthread_mutex_lock( &m_mutex ); 59 | int ret = pthread_cond_wait( &m_cond, &m_mutex ); 60 | pthread_mutex_unlock( &m_mutex ); 61 | return ret == 0; 62 | } 63 | 64 | // 唤醒等待该条件变量的某个线程 65 | bool signal() { 66 | return pthread_cond_signal( &m_cond ) == 0; 67 | } 68 | 69 | // 唤醒所有等待该条件变量的线程 70 | bool broadcast() { 71 | return pthread_cond_broadcast( &m_cond ) == 0; 72 | } 73 | }; 74 | 75 | #endif -------------------------------------------------------------------------------- /webServer2.0/makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | server: server.o 4 | g++ -o server server.cpp webServer.cpp task.cpp -lpthread 5 | 6 | clean: 7 | rm *.o server -------------------------------------------------------------------------------- /webServer2.0/picture/img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/webServer2.0/picture/img.jpg -------------------------------------------------------------------------------- /webServer2.0/picture/sign1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niliushall/webServer/85396f85100a1e33ddbbb5b42bb0852d5a61e26b/webServer2.0/picture/sign1.png -------------------------------------------------------------------------------- /webServer2.0/server.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 采用 epoll+线程池 实现,含有GET、POST, 可以发送html、picture、MP3、js、css、ico 3 | 服务器可以稳定运行 4 | */ 5 | 6 | #include "webServer.h" 7 | #include "task.h" 8 | 9 | int main( int argc, char **argv ) { 10 | if( argc != 2 ) { 11 | cout << "Usage : ./server + port\n"; 12 | return -1; 13 | } 14 | 15 | int port = atoi( argv[1] ); 16 | WebServer webServer( port ); 17 | webServer.run(); 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /webServer2.0/submit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | request test 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /webServer2.0/task.cpp: -------------------------------------------------------------------------------- 1 | #include "task.h" 2 | 3 | void removefd( int epollfd, int fd ) { 4 | epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 ); 5 | close( fd ); 6 | } 7 | 8 | void reset_oneshot( int epoll_fd, int fd ) { 9 | epoll_event event; 10 | event.data.fd = fd; 11 | event.events = EPOLLIN | EPOLLET | EPOLLONESHOT; 12 | epoll_ctl( epoll_fd, EPOLL_CTL_MOD, fd, &event ); 13 | } 14 | 15 | void Task::doit() { 16 | char buf[ buffer_size ] = {0}; 17 | 18 | while( int r = recv( accp_fd, buf, 1024, 0 ) ) { 19 | if( !r ) { 20 | cout << " browser exit.\n"; 21 | break; 22 | } else if( r < 0 ) { // 如果接收出错则继续接收数据 23 | continue; 24 | } 25 | 26 | int start = 0; 27 | char method[5], uri[100], version[10]; 28 | sscanf( buf, "%s %s %s", method, uri, version ); 29 | 30 | if( char *tmp = strstr( buf, "Range:" ) ) { 31 | tmp += 13; 32 | sscanf( tmp, "%d", &start ); 33 | } 34 | 35 | if( !strcmp( method, "GET" ) ) { // 为GET 36 | deal_get( uri, start ); 37 | } else if( !strcmp( method, "POST" ) ) { // 为POST 38 | deal_post( uri, buf ); 39 | } else { 40 | const char *header = "HTTP/1.1 501 Not Implemented\r\nContent-Type: text/plain;charset=utf-8\r\n\r\n"; 41 | send( accp_fd, header, strlen(header), 0 ); 42 | } 43 | break; // 只要处理完就退出循环,避免浏览器一直处于pending状态 44 | } 45 | // close( accp_fd ); // 任务完成直接close,不能在析构函数close(如果不delete task的话, 46 | // 不delete task不够条用析构函数) 47 | } 48 | 49 | int Task::deal_get( const string & uri, int start ) { 50 | string filename = uri.substr(1); 51 | 52 | if( uri == "/" || uri == "/index.html" ) { 53 | send_file( "index.html", "text/html", start ); 54 | } else if( uri.find( ".jpg" ) != string::npos || uri.find( ".png" ) != string::npos ) { 55 | send_file( filename, "image/jpg", start ); 56 | } else if( uri.find( ".html" ) != string::npos ) { 57 | send_file( filename, "text/html", start ); 58 | } else if( uri.find( ".ico" ) != string::npos ) { 59 | send_file( filename, "image/x-icon", start ); 60 | } else if( uri.find( ".js" ) != string::npos ) { 61 | send_file( filename, "yexy/javascript", start ); 62 | } else if( uri.find( ".css" ) != string::npos ) { 63 | send_file( filename, "text/css", start ); 64 | } else if( uri.find( ".mp3" ) != string::npos ) { 65 | send_file( filename, "audio/mp3", start ); 66 | } else if( uri.find( ".mp4" ) != string::npos ) { 67 | send_file( filename, "audio/mp4", start ); 68 | } else { 69 | send_file( filename, "text/plain", start ); 70 | } 71 | } 72 | 73 | int Task::deal_post( const string & uri, char *buf ) { 74 | string filename = uri.substr(1); 75 | if( uri.find( "adder" ) != string::npos ) { //使用CGI服务器,进行加法运算 76 | char *tmp = buf; 77 | int len, a, b; 78 | char *l = strstr( tmp, "Content-Length:" ); // 获取请求报文主体大小 79 | sscanf( l, "Content-Length: %d", &len ); 80 | len = strlen( tmp ) - len; 81 | tmp += len; 82 | sscanf( tmp, "a=%d&b=%d", &a, &b ); 83 | sprintf(tmp, "%d+%d,%d", a, b, accp_fd); // tmp存储发送到CGI服务器的参数 84 | 85 | // fork产生子进程,执行CGI服务器进行计算(webServer一眼只进行解析、发送数据,不进行相关计算) 86 | if( fork() == 0 ) { 87 | // dup2( accp_fd, STDOUT_FILENO ); 88 | execl( filename.c_str(), tmp, NULL ); 89 | } 90 | wait( NULL ); // 等待子进程结束 91 | } else { 92 | send_file( "html/404.html", "text/html", 0, 404, "Not Found" ); 93 | } 94 | } 95 | 96 | // type对应response的Content-Type,num为状态码,info为状态描述 97 | int Task::send_file( const string & filename, const char *type, 98 | int start, const int num, const char *info ) { 99 | struct stat filestat; 100 | int ret = stat( filename.c_str(), &filestat ); 101 | if( ret < 0 || !S_ISREG( filestat.st_mode ) ) { // 打开文件出错或没有该文件 102 | cout << "file not found : " << filename << endl; 103 | send_file( "html/404.html", "text/html", 0, 404, "Not Found" ); 104 | return -1; 105 | } 106 | 107 | char header[200]; 108 | sprintf( header, "HTTP/1.1 %d %s\r\nServer: niliushall\r\nContent-Length: %d\r\nContent-Type: %s;charset:utf-8\r\n\r\n", num, info, int(filestat.st_size - start), type ); 109 | 110 | // send第二个参数只能是c类型字符串,不能使用string 111 | send( accp_fd, header, strlen(header), 0 ); 112 | 113 | int fd = open( filename.c_str(), O_RDONLY ); 114 | int sum = start; 115 | 116 | while( sum < filestat.st_size ) { 117 | off_t t = sum; 118 | 119 | int r = sendfile( accp_fd, fd, &t, filestat.st_size ); 120 | 121 | if( r < 0 ) { 122 | printf("errno = %d, r = %d\n", errno, r); 123 | // perror("sendfile : "); 124 | if( errno == EAGAIN ) { 125 | printf("errno is EAGAIN\n"); 126 | // reset_oneshot( epoll_fd, accp_fd ); 127 | continue; 128 | } else { 129 | perror( "sendfile " ); 130 | close( fd ); 131 | break; 132 | } 133 | } else { 134 | sum += r; 135 | } 136 | } 137 | close( fd ); 138 | // printf( "sendfile finish, %d\n", accp_fd ); 139 | return 0; 140 | } 141 | 142 | int Task::get_size( const string & filename ) { 143 | struct stat filestat; 144 | int ret = stat( filename.c_str(), &filestat ); 145 | if( ret < 0 ) { 146 | cout << "file not found : " << filename << endl; 147 | return 0; 148 | } 149 | return filestat.st_size; 150 | } 151 | -------------------------------------------------------------------------------- /webServer2.0/task.h: -------------------------------------------------------------------------------- 1 | #ifndef _TASK_H_ 2 | #define _TASK_H_ 3 | 4 | #include "threadPool.h" 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 | using namespace std; 22 | 23 | const int buffer_size = 1024; 24 | 25 | void removefd( int epollfd, int fd ); 26 | 27 | // 封装任务执行的类 28 | class Task { 29 | private: 30 | int accp_fd; // 存储accept的返回值 31 | int epoll_fd; 32 | 33 | public: 34 | Task() {} 35 | Task(int fd, int epoll) : accp_fd(fd), epoll_fd(epoll) {} 36 | ~Task() { removefd( epoll_fd, accp_fd ); } 37 | // Task(int fd, int epoll) : accp_fd(fd), epoll_fd(epoll) { cout << accp_fd << " start\n"; } 38 | // ~Task() { removefd( epoll_fd, accp_fd ); cout << accp_fd << " close\n"; } 39 | 40 | 41 | void doit(); // 执行任务 42 | // 构造HTTP首部 43 | // void construct_header( char *response, const int num, const char * info, 44 | // const char * type ); 45 | 46 | // 发送文件 47 | int send_file( const string & filename, const char *type, 48 | int start, const int num = 200, const char *info = "OK" ); 49 | int deal_get( const string & uri, int start = 0 ); // 处理GET请求 50 | int deal_post( const string & uri, char *buf ); // 处理POST请求 51 | int get_size( const string & filename ); // 获取文件大小 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /webServer2.0/threadPool.h: -------------------------------------------------------------------------------- 1 | #ifndef _THREADPOOL_H_ 2 | #define _THREADPOOL_H_ 3 | 4 | #include "locker.h" 5 | #include 6 | using namespace std; 7 | 8 | template< typename T > 9 | class ThreadPool { 10 | private: 11 | int thread_number; // 线程池的线程数 12 | pthread_t *threads; // 线程数组 13 | queue task_queue; // 任务队列 14 | MutexLocker queue_mutex_locker; // 任务队列的互斥锁 15 | Cond queue_cond_locker; // 任务队列的条件变量 16 | bool m_stop; // 是否结束线程 17 | public: 18 | ThreadPool( int thread_num = 20 ); 19 | ~ThreadPool(); 20 | bool append( T *task ); // 向任务队列添加任务 21 | private: 22 | static void *worker( void * ); // 工作线程 23 | void run(); // 线程池中线程开始运行的函数 24 | T *getTask(); // 从任务队列中获取队首的任务 25 | }; 26 | 27 | template< typename T > 28 | ThreadPool::ThreadPool( int thread_num ) :thread_number(thread_num), 29 | threads(NULL), m_stop(false) { 30 | if( thread_number < 0 ) { 31 | cout << "thread_number < 0\n"; 32 | throw exception(); 33 | } 34 | 35 | // 创建数组存放线程号 36 | threads = new pthread_t[ thread_number ]; 37 | if( !threads ) { 38 | cout << "threads is NULL\n"; 39 | throw exception(); 40 | } 41 | 42 | // 创建规定数量的线程 43 | for( int i = 0; i < thread_number; i++ ) { 44 | // 由于pthread_create第三个参数必须指向静态函数,要使用类成员函数和变量,只能通过: 45 | // 1) 类的静态对象 46 | // 2) 将类的对象作为参数传给静态函数 47 | // 这里通过第二种方法实现 48 | if( pthread_create( &threads[i], NULL, worker, this ) ) { // 成功返回0 49 | delete[] threads; // 创建失败则释放所有已分配的空间 50 | cout << "pthread_create error\n"; 51 | throw exception(); 52 | } 53 | 54 | // 将线程进行脱离,线程运行完后自动回收,避免使用主线程进行join等待其结束 55 | if( pthread_detach( threads[i] ) ) { 56 | delete[] threads; 57 | cout << "pthread_detach error\n"; 58 | throw exception(); 59 | } 60 | } 61 | } 62 | 63 | // 析构函数中,将m_stop置true,此时将阻塞中的所有线程唤醒 64 | // 由于 !m_stop 为false,线程会退出循环,线程结束被回收( 详见函数run() ) 65 | // 若不唤醒线程,则在程序退出后,线程非正常结束,同时会导致 66 | template< typename T > 67 | ThreadPool::~ThreadPool() { 68 | delete[] threads; 69 | m_stop = true; 70 | queue_cond_locker.broadcast(); 71 | } 72 | 73 | /* 添加任务时需要先上锁,并判断队列是否为空 */ 74 | template< typename T > 75 | bool ThreadPool::append( T *task ) { 76 | queue_mutex_locker.mutex_lock(); 77 | bool need_signal = task_queue.empty(); // 记录添加任务之前队列是否为空 78 | task_queue.push( task ); 79 | queue_mutex_locker.mutex_unlock(); 80 | 81 | // 如果添加任务之前队列为空,即所有线程都在wait,所以需要唤醒某个线程 82 | if( need_signal ) { 83 | queue_cond_locker.signal(); 84 | } 85 | 86 | return true; 87 | } 88 | 89 | // 线程函数,调用run()来使线程开始工作 90 | template< typename T > 91 | void * ThreadPool::worker( void *arg ) { 92 | ThreadPool *pool = ( ThreadPool * )arg; 93 | pool->run(); 94 | return pool; 95 | } 96 | 97 | // 获取处于队首的任务,获取时需要加锁,避免发生错误 98 | // 若队列为空,则返回NULL,该线程成为等待状态(详见函数run()) 99 | template< typename T > 100 | T* ThreadPool::getTask() { 101 | T *task = NULL; 102 | queue_mutex_locker.mutex_lock(); 103 | if( !task_queue.empty() ) { 104 | task = task_queue.front(); 105 | task_queue.pop(); 106 | } 107 | queue_mutex_locker.mutex_unlock(); 108 | 109 | return task; 110 | } 111 | 112 | template< typename T > 113 | void ThreadPool::run() { 114 | while( !m_stop ) { // 当线程池没有结束时,线程循环获取任务进行执行 115 | T *task = getTask(); 116 | if( !task ) { 117 | queue_cond_locker.wait(); // 队列为空,线程开始等待 118 | } else { 119 | // printf("a thread start work\n"); 120 | task->doit(); // 开始执行任务 121 | delete task; //task指向的对象在WebServer中new出来,因此需要手动delete 122 | } 123 | } 124 | } 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /webServer2.0/webServer.cpp: -------------------------------------------------------------------------------- 1 | #include "webServer.h" 2 | 3 | // 设置为非阻塞 4 | int setnonblocking( int fd ) { 5 | int old_option = fcntl( fd, F_GETFL ); 6 | int new_option = old_option | O_NONBLOCK; 7 | fcntl( fd, F_SETFL, new_option ); 8 | return old_option; 9 | } 10 | 11 | void addfd( int epoll_fd, bool oneshot, int fd ) { 12 | epoll_event event; 13 | event.data.fd = fd; 14 | event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; 15 | if( oneshot ) { 16 | event.events |= EPOLLONESHOT; 17 | } 18 | epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event ); 19 | setnonblocking( fd ); 20 | } 21 | 22 | int WebServer::run() { 23 | // 忽略SIGPIPE信号 24 | signal( SIGPIPE, SIG_IGN ); 25 | 26 | server_addr.sin_family = AF_INET; 27 | server_addr.sin_port = htons( port ); 28 | server_addr.sin_addr.s_addr = htonl( INADDR_ANY ); 29 | 30 | sock_fd = socket( AF_INET, SOCK_STREAM, 0 ); 31 | if( sock_fd < 0 ) { 32 | 33 | cout << "socket error, line " << __LINE__ << endl; 34 | return -1; 35 | } 36 | 37 | int ret = bind( sock_fd, (struct sockaddr *)&server_addr, sizeof( server_addr ) ); 38 | if( ret < 0 ) { 39 | cout << "bind error, line " << __LINE__ << endl; 40 | return -1; 41 | } 42 | 43 | ret = listen( sock_fd, 1024 ); 44 | if( ret < 0 ) { 45 | cout << "listen error, line " << __LINE__ << endl; 46 | return -1; 47 | } 48 | 49 | ThreadPool threadpool(50); // 创建线程池,并运行 50 | 51 | /* ThreadPool< Task > *threadpool; 52 | try { 53 | threadpool = new ThreadPool(20); 54 | } catch(...) { 55 | cout << "init threadpool error\n"; 56 | return -1; 57 | } */ 58 | 59 | epoll_event events[max_event_num]; 60 | epoll_fd = epoll_create(1024); 61 | 62 | if( epoll_fd < 0 ) { 63 | cout << "epoll_create error, line: " << __LINE__ << endl; 64 | exit(-1); 65 | } 66 | 67 | // addfd( epoll_fd, false, sock_fd ); 68 | //////修改addfd LT模式 69 | epoll_event event; 70 | event.data.fd = sock_fd; 71 | event.events = EPOLLIN | EPOLLRDHUP; 72 | epoll_ctl( epoll_fd, EPOLL_CTL_ADD, sock_fd, &event ); 73 | // setnonblocking( sock_fd ); 74 | ////// 75 | 76 | 77 | while( true ) { 78 | ret = epoll_wait( epoll_fd, events, max_event_num, -1 ); 79 | // printf("epoll_wait recv a connect %d\n", ret); 80 | if( ret < 0 ) { 81 | perror( "epoll_wait:" ); 82 | return -1; 83 | } 84 | for( int i = 0; i < ret; i++ ) { 85 | int fd = events[i].data.fd; 86 | 87 | if( fd == sock_fd ) { //新连接 88 | struct sockaddr_in client_addr; 89 | socklen_t client_addr_size = sizeof( client_addr ); 90 | int conn_fd = accept( fd, (struct sockaddr *)&client_addr, &client_addr_size ); 91 | if( conn_fd < 0 ) { 92 | cout << "accept error, line: " << __LINE__ << endl; 93 | return -1; 94 | } 95 | addfd(epoll_fd, true, conn_fd); 96 | 97 | } else if( events[i].events & ( EPOLLRDHUP | EPOLLHUP | EPOLLERR ) ) { 98 | // close( fd ); 99 | removefd( epoll_fd, fd ); 100 | } else if( events[i].events & EPOLLIN ) { //有数据写入 101 | Task *task = new Task(fd, epoll_fd); // 新建任务 102 | threadpool.append( task ); // 添加任务 103 | // printf("append a task, %d\n", fd); 104 | } else { 105 | cout << "\nother\n"; 106 | } 107 | } 108 | } 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /webServer2.0/webServer.h: -------------------------------------------------------------------------------- 1 | #ifndef _WEBSERVER_H_ 2 | #define _WEBSERVER_H_ 3 | 4 | #include "task.h" 5 | #include "threadPool.h" 6 | using namespace std; 7 | 8 | const int max_event_num = 20; 9 | 10 | class WebServer { 11 | private: 12 | int port; 13 | int sock_fd; 14 | int epoll_fd; 15 | struct sockaddr_in server_addr; 16 | public: 17 | WebServer( int p ) : sock_fd(0), port(p) { memset( &server_addr, 0, sizeof( server_addr ) ); } 18 | ~WebServer() { close( sock_fd ); } 19 | int run(); 20 | }; 21 | 22 | #endif --------------------------------------------------------------------------------