首页
最新活动
服务器租用
香港服务器租用
台湾服务器租用
美国服务器租用
日本服务器租用
新加坡服务器租用
高防服务器
香港高防服务器
台湾高防服务器
美国高防服务器
裸金属
香港裸金属服务器
台湾裸金属服务器
美国裸金属服务器
日本裸金属服务器
新加坡裸金属服务器
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
CDN
CDN节点
CDN带宽
CDN防御
CDN定制
行业新闻
官方公告
香港服务器资讯
帮助文档
wp博客
zb博客
服务器资讯
联系我们
关于我们
机房介绍
机房托管
登入
注册
帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器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
上一篇
服务器与电脑的区别-
下一篇
云服务器租用什么意思
相关文章
Linux ps命令详解,Linux查看进程
阿里云上怎么买服务器
福建福人家居科技有限公司怎么样
racknerd服务器怎么样
服务器变矿机,该如何应对?
2024年雾锁王国Enshrouded服务器【免费搭建】教程
Vue3 启动项目失败error when starting dev server- Error- listen EACCES- permission denied 127.0.0.1-80
servers怎么看谁占的内存大
HTTP状态码汇总(常见)
香港云服务器租用推荐
服务器租用资讯
·广东云服务有限公司怎么样
·广东云服务器怎么样
·广东锐讯网络有限公司怎么样
·广东佛山的蜗牛怎么那么大
·广东单位电话主机号怎么填写
·管家婆 花生壳怎么用
·官网域名过期要怎么办
·官网邮箱一般怎么命名
·官网网站被篡改怎么办
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价
7*24H在线售后
高可用资源,安全稳定
1v1专属客服对接
无忧退款试用保障
德讯电讯股份有限公司
电话:00886-982-263-666
台湾总部:台北市中山区建国北路一段29号3楼
香港分公司:九龙弥敦道625号雅兰商业二期906室
服务器租用
香港服务器
日本服务器
台湾服务器
美国服务器
高防服务器购买
香港高防服务器出租
台湾高防服务器租赁
美国高防服务器DDos
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
行业新闻
香港服务器租用
服务器资讯
香港云服务器
台湾服务器租用
zblog博客
香港VPS
关于我们
机房介绍
联系我们
Copyright © 1997-2024 www.hkstack.com All rights reserved.