帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器vps租用、韩国高防服务器租用、新加坡服务器、日本服务器租用 一站式全球网络解决方案提供商!专业运营维护IDC数据中心,提供高质量的服务器托管,服务器机房租用,服务器机柜租用,IDC机房机柜租用等服务,稳定、安全、高性能的云端计算服务,实时满足您的多样性业务需求。 香港大带宽稳定可靠,高级工程师提供基于服务器硬件、操作系统、网络、应用环境、安全的免费技术支持。
服务器资讯 / 香港服务器租用 / 香港VPS租用 / 香港云服务器 / 美国服务器租用 / 台湾服务器租用 / 日本服务器租用 / 官方公告 / 帮助文档
【基于C++HTTP 服务器的epoll 改造】
发布时间:2024-02-27 22:58:47   分类:帮助文档
【基于C++HTTP 服务器的epoll 改造】 打印模块 Log.hpp 方便使用 #pragma once #include #include #include #define INFO 1 #define WARNING 2 #define ERROR 3 #define FATAL 4 #define LOG(level, message) Log(#level, message, __FILE__, __LINE__) void Log(std::string level, std::string message, std::string file_name, int line) { std::cout<<"["< #include #include #include #include #include #include #include #include "Log.hpp" #define BACKLOG 5 class TcpServer{ private: int _port; //端口号 int _listen_sock; //监听套接字 static TcpServer* _svr; int epollfd; private: TcpServer(int port) :_port(port) ,_listen_sock(-1) {} TcpServer(const TcpServer&) {} public: static TcpServer* GetInstance(int port) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; if(_svr == nullptr){ pthread_mutex_lock(&lock); if(_svr == nullptr){ _svr = new TcpServer(port); _svr->InitServer(); } pthread_mutex_unlock(&lock); } return _svr; } void InitServer() { Socket(); Bind(); Listen(); LOG(INFO, "tcp_server init ... success"); } void Socket() { _listen_sock = socket(AF_INET, SOCK_STREAM, 0); if(_listen_sock < 0){ LOG(FATAL, "socket error!"); exit(1); } int opt = 1; setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); LOG(INFO, "create socket ... success"); } void Bind() { struct sockaddr_in local; memset(&local, 0, sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(_port); local.sin_addr.s_addr = INADDR_ANY; //云服务器不能直接绑定公网IP if(bind(_listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){ LOG(FATAL, "bind error!"); exit(2); } LOG(INFO, "bind socket ... success"); } void Listen() { if(listen(_listen_sock, BACKLOG) < 0){ LOG(FATAL, "listen error!"); exit(3); } LOG(INFO, "listen socket ... success"); } int Sock() { return _listen_sock; } ~TcpServer() { if(_listen_sock >= 0){ close(_listen_sock); } } }; TcpServer* TcpServer::_svr = nullptr; HttpServer.hpp #pragma once #include #include #include #include "TcpServer.hpp" #include "Task.hpp" #include "ThreadPool.hpp" #include "Log.hpp" #define PORT 8081 #define MAX_EVENT_NUMBER 1024 class HttpServer{ private: int _port; bool _stop; int epollfd; struct epoll_event events[MAX_EVENT_NUMBER]; public: HttpServer(int port) :_port(port) { _stop = false; epollfd = -1; } void InitServer() { signal(SIGPIPE, SIG_IGN); //忽略SIGPIPE信号,防止写入时崩溃 } int setnonblocking(int fd) { int option = fcntl(fd, F_GETFL) | O_NONBLOCK; fcntl(fd, F_SETFD, option); return fcntl(fd, F_GETFL); } void addfd(int fd, bool enable_et){ struct epoll_event ev; ev.data.fd = fd; ev.events = EPOLLIN; if(enable_et){ ev.events |= EPOLLET; } epoll_ctl(this->epollfd, EPOLL_CTL_ADD, fd, &ev); setnonblocking(fd); } void work(int nread, int listenfd) { for(int i = 0; i < nread; i++) { int sockfd = events[i].data.fd; if(sockfd == listenfd){ struct sockaddr_in peer; memset(&peer, 0, sizeof(peer)); socklen_t len = sizeof(peer); int connfd = accept(listenfd, (struct sockaddr*)&peer, &len); addfd(connfd, true); }else if(events[i].events & EPOLLIN){ LOG(INFO, "event trigger once"); LOG(INFO, "get a new link"); Task task(sockfd); ThreadPool::GetInstance()->PushTask(task); }else{ LOG(INFO, "something else happened"); } } } void Loop() { LOG(INFO, "loop begin"); TcpServer* tsvr = TcpServer::GetInstance(_port); int listen_sock = tsvr->Sock(); epollfd = epoll_create(5); addfd(listen_sock, true); while(!_stop){ int nread = epoll_wait(this->epollfd, events, MAX_EVENT_NUMBER, -1); if(nread < 0){ continue; } // struct sockaddr_in peer; // memset(&peer, 0, sizeof(peer)); // socklen_t len = sizeof(peer); // int sock = accept(listen_sock, (struct sockaddr*)&peer, &len); // if(sock < 0){ // continue; // } // LOG(INFO, "get a new link"); // Task task(sock); // ThreadPool::GetInstance()->PushTask(task); //lt(nread, listen_sock); work(nread, listen_sock); //int* p = new int(sock); //pthread_t tid; //pthread_create(&tid, nullptr, Entrance::HandlerRequest, (void*)p); //pthread_detach(tid); } } ~HttpServer() {} }; Task.hpp #pragma once #include #include #include "Protocol.hpp" class Task{ private: int _sock; CallBack _handler; //回调 public: Task() {} Task(int sock) :_sock(sock) {} //处理任务 void ProcessOn() { _handler(_sock); //调用CallBack的仿函数 } ~Task() {} }; ThreadPool.hpp #pragma once #include #include #include #include "Task.hpp" #include "Log.hpp" #define NUM 6 class ThreadPool{ private: std::queue _task_queue; //任务队列 int _num; bool _stop; pthread_mutex_t _mutex; pthread_cond_t _cond; static ThreadPool* _inst; private: //构造函数私有 ThreadPool(int num = NUM) :_num(num) ,_stop(false) { pthread_mutex_init(&_mutex, nullptr); pthread_cond_init(&_cond, nullptr); } //拷贝构造函数私有或删除 ThreadPool(const ThreadPool&)=delete; bool IsEmpty() { return _task_queue.empty(); } bool IsStop() { return _stop; } void LockQueue() { pthread_mutex_lock(&_mutex); } void UnLockQueue() { pthread_mutex_unlock(&_mutex); } void ThreadWait() { pthread_cond_wait(&_cond, &_mutex); } void ThreadWakeUp() { pthread_cond_signal(&_cond); } public: static ThreadPool* GetInstance() { static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; if(_inst == nullptr){ pthread_mutex_lock(&mtx); if(_inst == nullptr){ _inst = new ThreadPool(); _inst->InitThreadPool(); } pthread_mutex_unlock(&mtx); } return _inst; } static void* ThreadRoutine(void* arg) { pthread_detach(pthread_self()); ThreadPool* tp = (ThreadPool*)arg; while(true){ tp->LockQueue(); while(tp->IsEmpty()){ tp->ThreadWait(); } Task task; tp->PopTask(task); tp->UnLockQueue(); task.ProcessOn(); //处理任务 } } bool InitThreadPool() { pthread_t tid; for(int i = 0;i < _num;i++){ if(pthread_create(&tid, nullptr, ThreadRoutine, this) != 0){ LOG(FATAL, "create thread pool error!"); return false; } } LOG(INFO, "create thread pool success!"); return true; } void PushTask(const Task& task) { LockQueue(); _task_queue.push(task); UnLockQueue(); ThreadWakeUp(); } void PopTask(Task& task) { task = _task_queue.front(); _task_queue.pop(); } ~ThreadPool() { pthread_mutex_destroy(&_mutex); pthread_cond_destroy(&_cond); } }; ThreadPool* ThreadPool::_inst = nullptr; Util.hpp #pragma once #include #include #include //工具类 class Util{ public: static int ReadLine(int sock, std::string& out) { char ch = 'X'; //随便设置一个字符,只要不是\n即可 while(ch != '\n'){ ssize_t size = recv(sock, &ch, 1, 0); if(size > 0){ if(ch == '\r'){ //窥探 recv(sock, &ch, 1, MSG_PEEK); if(ch == '\n'){ //窥探成功 //\r\n->\n recv(sock, &ch, 1, 0); } else{ //\r->\n ch = '\n'; } } //普通字符或\n out.push_back(ch); } else if(size == 0){ return 0; } else{ return -1; } } return out.size(); } static bool CutString(std::string& target, std::string& sub1_out, std::string& sub2_out, std::string sep) { size_t pos = target.find(sep, 0); if(pos != std::string::npos){ sub1_out = target.substr(0, pos); sub2_out = target.substr(pos + sep.size()); return true; } return false; } }; Protocol.hpp #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Util.hpp" #include "Log.hpp" #define SEP ": " #define WEB_ROOT "wwwroot" #define HOME_PAGE "index.html" #define HTTP_VERSION "HTTP/1.0" #define LINE_END "\r\n" #define PAGE_400 "400.html" #define PAGE_404 "404.html" #define PAGE_500 "500.html" #define OK 200 #define BAD_REQUEST 400 #define NOT_FOUND 404 #define SERVER_ERROR 500 static std::string CodeToDesc(int code) { std::string desc; switch(code){ case 200: desc = "OK"; break; case 404: desc = "Not Found"; break; default: break; } return desc; } static std::string SuffixToDesc(const std::string& suffix) { static std::unordered_map suffix_to_desc = { {".html", "text/html"}, {".css", "text/css"}, {".js", "application/x-javascript"}, {".jpg", "application/x-jpg"}, {".xml", "text/xml"} }; auto iter = suffix_to_desc.find(suffix); if(iter != suffix_to_desc.end()){ return iter->second; } return "text/html"; } class HttpRequest{ public: std::string _request_line; //请求行 std::vector _request_header; //请求报头 std::string _blank; //空行 std::string _request_body; //请求正文 //解析完毕之后的结果 std::string _method; //请求方法 std::string _uri; //URI std::string _version; //版本号 std::unordered_map _header_kv; //请求报头中的键值对 int _content_length; //正文长度 std::string _path; //请求资源的路径 std::string _query_string; //uri中携带的参数 bool _cgi; //是否需要使用CGI模式 public: HttpRequest() :_content_length(0) ,_cgi(false) {} ~HttpRequest() {} }; class HttpResponse{ public: std::string _status_line; //状态行 std::vector _response_header; //响应报头 std::string _blank; //空行 std::string _response_body; //响应正文 int _status_code; //状态码 int _fd; //响应的文件 int _size; //响应文件的大小 std::string _suffix; //响应文件的后缀 public: HttpResponse() :_blank(LINE_END) ,_status_code(OK) ,_fd(-1) ,_size(0) {} ~HttpResponse() {} }; //读取请求、分析请求、构建响应 //IO通信 class EndPoint{ private: int _sock; HttpRequest _http_request; HttpResponse _http_response; bool _stop; private: //读取请求行 bool RecvHttpRequestLine() { auto& line = _http_request._request_line; if(Util::ReadLine(_sock, line) > 0){ line.resize(line.size() - 1); LOG(INFO, line); } else{ _stop = true; //读取出错,不予处理 } return _stop; } //读取请求报头和空行 bool RecvHttpRequestHeader() { std::string line; while(true){ line.clear(); //每次读取之前清空line if(Util::ReadLine(_sock, line) <= 0){ _stop = true; break; } if(line == "\n"){ _http_request._blank = line; break; } line.resize(line.size() - 1); _http_request._request_header.push_back(line); //LOG(INFO, line); } return _stop; } //解析请求行 void ParseHttpRequestLine() { auto& line = _http_request._request_line; std::stringstream ss(line); ss>>_http_request._method>>_http_request._uri>>_http_request._version; auto& method = _http_request._method; std::transform(method.begin(), method.end(), method.begin(), toupper); } //解析请求报头 void ParseHttpRequestHeader() { std::string key; std::string value; for(auto& iter : _http_request._request_header){ if(Util::CutString(iter, key, value, SEP)) { _http_request._header_kv.insert({key, value}); } } } //判定是否需要读取请求正文 bool IsNeedRecvHttpRequestBody() { auto& method = _http_request._method; if(method == "POST"){ auto& header_kv = _http_request._header_kv; auto iter = header_kv.find("Content-Length"); if(iter != header_kv.end()){ _http_request._content_length = atoi(iter->second.c_str()); return true; } } return false; } //读取请求正文 bool RecvHttpRequestBody() { if(IsNeedRecvHttpRequestBody()){ int content_length = _http_request._content_length; auto& body = _http_request._request_body; char ch = 0; while(content_length){ ssize_t size = recv(_sock, &ch, 1, 0); if(size > 0){ body.push_back(ch); content_length--; } else{ _stop = true; break; } } } return _stop; } //CGI处理 int ProcessCgi() { int code = OK; auto& bin = _http_request._path; //要让子进程执行的目标程序 auto& method = _http_request._method; //父进程的数据 auto& query_string = _http_request._query_string; //GET auto& request_body = _http_request._request_body; //POST int content_length = _http_request._content_length; auto& response_body = _http_response._response_body; //站在父进程角度 int input[2]; int output[2]; if(pipe(input) < 0){ LOG(ERROR, "pipe input error!"); code = SERVER_ERROR; return code; } if(pipe(output) < 0){ LOG(ERROR, "pipe output error!"); code = SERVER_ERROR; return code; } pid_t pid = fork(); if(pid == 0){ //child close(input[0]); close(output[1]); //将请求方法通过环境变量传参 std::string method_env = "METHOD="; method_env += method; putenv((char*)method_env.c_str()); if(method == "GET"){ //通过环境变量传参 std::string query_env = "QUERY_STRING="; query_env += query_string; putenv((char*)query_env.c_str()); LOG(INFO, "GET Method, Add Query_String env"); } else if(method == "POST"){ //导入正文参数长度 std::string content_length_env = "CONTENT_LENGTH="; content_length_env += std::to_string(content_length); putenv((char*)content_length_env.c_str()); LOG(INFO, "POST Method, Add Content_Length env"); } else{ //Do Nothing } dup2(output[0], 0); dup2(input[1], 1); execl(bin.c_str(), bin.c_str(), nullptr); exit(1); } else if(pid < 0){ LOG(ERROR, "fork error!"); code = SERVER_ERROR; return code; } else{ //father close(input[1]); close(output[0]); if(method == "POST"){ //将数据写入到管道当中 const char* start = request_body.c_str(); int total = 0; int size = 0; while(total < content_length && (size = write(output[1], start + total, request_body.size() - total)) > 0){ total += size; } } //std::string test_string = "2021dragon"; //send(output[1], test_string.c_str(), test_string.size(), 0); char ch = 0; while(read(input[0], &ch, 1) > 0){ response_body.push_back(ch); } //不会一直读,当另一端关闭后会继续执行下面的代码 int status = 0; pid_t ret = waitpid(pid, &status, 0); if(ret == pid){ if(WIFEXITED(status)){ //正常退出 LOG(INFO, "正常退出"); if(WEXITSTATUS(status) == 0){ //结果正确 LOG(INFO, "正常退出,结果正确"); code = OK; } else{ LOG(INFO, "正常退出,结果不正确"); code = BAD_REQUEST; } } else{ LOG(INFO, "异常退出"); code = SERVER_ERROR; } } //释放文件描述符 close(input[0]); close(output[1]); } return code; } //非CGI处理 int ProcessNonCgi() { //打开待发送的文件 _http_response._fd = open(_http_request._path.c_str(), O_RDONLY); if(_http_response._fd >= 0){ //打开文件成功再构建 return OK; } return NOT_FOUND; } void BuildOkResponse() { //构建响应报头 std::string content_type = "Content-Type: "; content_type += SuffixToDesc(_http_response._suffix); content_type += LINE_END; _http_response._response_header.push_back(content_type); std::string content_length = "Content-Length: "; if(_http_request._cgi){ //以CGI方式请求 content_length += std::to_string(_http_response._response_body.size()); } else{ //以非CGI方式请求 content_length += std::to_string(_http_response._size); } content_length += LINE_END; _http_response._response_header.push_back(content_length); } void HandlerError(std::string page) { _http_request._cgi = false; //正常的网页返回,非CGI返回 _http_response._fd = open(page.c_str(), O_RDONLY); std::cout< 0){ std::cout< 0){ total += size; } } else{ sendfile(_sock, _http_response._fd, nullptr, _http_response._size); //关闭文件 close(_http_response._fd); } } ~EndPoint() {} }; class CallBack{ public: CallBack() {} void operator()(int sock) { HandlerRequest(sock); } void HandlerRequest(int sock) { LOG(INFO, "handler request begin"); std::cout<<"get a new link..."<RecvHttpRequest(); if(!ep->IsStop()){ LOG(INFO, "Recv No Error, Begin Build And Send"); ep->BuildHttpResponse(); ep->SendHttpResponse(); } else{ LOG(WARNING, "Recv Error, Stop Build And Send"); } close(sock); delete ep; #endif LOG(INFO, "handler request end"); } ~CallBack() {} }; Makefile bin=httpserver cgi=test_cgi cc=g++ LD_FLAGS=-std=c++11 -lpthread #-DDEBUG=1 curr=$(shell pwd) src=main.cc ALL:$(bin) $(cgi) .PHONY:ALL $(bin):$(src) $(cc) -o $@ $^ $(LD_FLAGS) $(cgi):cgi/test_cgi.cc $(cc) -o $@ $^ -std=c++11 .PHONY:clean clean: rm -f $(bin) $(cgi) rm -rf output .PHONY:output output: mkdir -p output cp $(bin) output cp -rf wwwroot output cp $(cgi) output/wwwroot cp ./cgi/shell_cgi.sh output/wwwroot cp ./cgi/python_cgi.py output/wwwroot cp ./cgi/test.html output/wwwroot main.cc #include #include #include #include "HttpServer.hpp" static void Usage(std::string proc) { std::cout<<"Usage:\n\t"< svr(new HttpServer(port)); svr->InitServer(); svr->Loop(); return 0; } 后续加上post 请求 原文链接: link
香港云服务器租用推荐
服务器租用资讯
·广东云服务有限公司怎么样
·广东云服务器怎么样
·广东锐讯网络有限公司怎么样
·广东佛山的蜗牛怎么那么大
·广东单位电话主机号怎么填写
·管家婆 花生壳怎么用
·官网域名过期要怎么办
·官网邮箱一般怎么命名
·官网网站被篡改怎么办
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价