[项目]搭建一个HTTP_SERVER

打印 上一主题 下一主题

主题 556|帖子 556|积分 1668

1.项目先容

   该项目是一个基于http和tcp协议自主实现的WebServer,用于实现欣赏器对服务器的简单哀求,然后吸收服务器返回的处理哀求数据,目的是为了让学习者更好的对网络连接和相关协议举行明确,同时制作出像网络在线盘算机,音视频哀求播放,大概雷同博客的功能,该夺目利用到的紧张技能有网络编程(socket),多线程编程,cgi技能,管道通信等。
  2.详细编码实现

2.1 tcp_server

  1. #pragma  once
  2. #include <iostream>
  3. #include "LOG.hpp"
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <cstdlib>
  7. #include <cstring>
  8. #include <pthread.h>
  9. #include <netinet/in.h>
  10. #include <arpa/inet.h>
  11. #define PORT 8081
  12. #define BACKLOG 5
  13. class TcpServer{
  14.     private:
  15.         int listen_sock;
  16.         int port;
  17.         static TcpServer* tcpserver;
  18.     private:
  19.         TcpServer(int _port = PORT):listen_sock(),port(_port){ }
  20.         ~TcpServer(){}
  21.     public:
  22.         static TcpServer* GetInstance(int _port = PORT){
  23.             static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
  24.             if(tcpserver == nullptr){
  25.                 pthread_mutex_lock(&lock);
  26.                 if(tcpserver == nullptr){
  27.                     tcpserver = new TcpServer(_port);
  28.                     tcpserver->InitTcpServer();
  29.                 }
  30.                 pthread_mutex_unlock(&lock);
  31.             }
  32.             return tcpserver;
  33.         }
  34.         void InitTcpServer(){
  35.             CreateSock();
  36.             BindSock();
  37.             ListenSock();
  38.         }
  39.         void CreateSock(){
  40.             listen_sock = socket(AF_INET,SOCK_STREAM,0);
  41.             if(listen_sock < 0){
  42.                 LOG(INFO,"Create Sock Unsucess!");
  43.                 exit(1);
  44.             }
  45.             int opt = 1;
  46.             setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
  47.             LOG(INFO,"Create Sock Sucess!");
  48.         }
  49.         void BindSock(){
  50.             struct sockaddr_in local;
  51.             memset(&local,0,sizeof(local));
  52.             local.sin_family = AF_INET;
  53.             local.sin_port = htons(port);
  54.             local.sin_addr.s_addr = INADDR_ANY;  //云服务器不可以直接绑定ip
  55.             if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0){
  56.                 LOG(INFO,"Bind Sock Unsucess!");
  57.                 exit(2);
  58.             }
  59.             LOG(INFO,"Bind Sock Sucess!");
  60.         }
  61.         void ListenSock(){
  62.             if(listen(listen_sock,BACKLOG)<0){
  63.                 LOG(INFO,"Listen Sock Unsucess!");
  64.                 exit(3);
  65.             }
  66.             LOG(INFO,"Listen Sock Sucess!");
  67.         }
  68.         int GetListenSock(){
  69.             return listen_sock;
  70.         }
  71.         
  72. };
  73. TcpServer*  TcpServer::tcpserver = nullptr;
复制代码
 2.2 http_server

  1. #pragma once
  2. #include "TcpServer.hpp"
  3. #include "Protocol.hpp"
  4. #include "LOG.hpp"
  5. #include "Task.hpp"
  6. #include "ThreadPool.hpp"
  7. #define PORT 8081
  8. class HttpServer{
  9.     private:
  10.         int sock;
  11.         int port;
  12.         int stop;
  13.         TcpServer* tcpserver;
  14.     public:
  15.         HttpServer(int _port = PORT):port(_port),stop(false),tcpserver(nullptr){}
  16.         void InitHttpServer(){
  17.             // 防止对端(client and server)断开发送SIGPIPE信号时,执行默认动作
  18.             signal(SIGPIPE,SIG_IGN);
  19.         }
  20.         void Loop(){
  21.             tcpserver = TcpServer::GetInstance(port);
  22.             while(!stop){
  23.                 struct sockaddr_in peer;
  24.                 socklen_t len = sizeof(peer);
  25.                 memset(&peer,0,sizeof(peer));
  26.                 sock = accept(tcpserver->GetListenSock(),(struct sockaddr*)&peer,&len);
  27.                 LOG(INFO,"Begin Get A New Link!");
  28.                 if(sock < 0){
  29.                     LOG(INFO,"Accept Socket Unsucess!");
  30.                     continue;
  31.                 }
  32.                 LOG(INFO,"Begin Proess Request!");
  33.                 TASK task(sock);
  34.                 ThreadPool::GetInstance()->PushTask(task);
  35.             }
  36.         }
  37.        ~HttpServer(){}
  38. };
复制代码
2.3 日志体系

  1. #pragma  once
  2. #include <string>
  3. #include <iostream>
  4. #include <cstdio>
  5. #include <time.h>
  6. #define INFO    1
  7. #define WARNING 2
  8. #define ERROR   3
  9. #define FATAL   4
  10. #define LOG(level,message) log(#level,message,__FILE__,__LINE__)
  11. void log(std::string level,std::string message,std::string fname,int eline){
  12.         struct tm t;
  13.         time_t now = time(NULL);
  14.         localtime_r(&now,&t);
  15.         printf("[%-7s][%-4d-%02d-%02d %02d:%02d:%02d][%-30s][%-15s][%-4d]\n",\
  16.                 level.c_str(),t.tm_year+1900,t.tm_mon+1,t.tm_mday,t.tm_hour,t.tm_min,t.tm_sec,\
  17.                 message.c_str(),fname.c_str(),eline);
  18.     }
复制代码
2.4 Protocol协议的搭建

  1. class EndPoint{
  2.     private:
  3.         int sock;
  4.         bool stop;
  5.         HttpRequest http_request;
  6.         HttpResbonse http_resbonse;
  7.     public:
  8.         EndPoint(int _sock):sock(_sock),stop(false){}
  9.         int IsStop(){
  10.             return stop;
  11.         }
  12.         void RecvHttpRequest(){
  13.             if((!Recv_Parse_HttpRequest_Line()) && \
  14.                 (!Recv_Parse_HttpRequest_Head()) && \
  15.                 (!Recv_HttpRequest_Body())){}
  16.             }
  17.         int  Recv_Parse_HttpRequest_Line(){
  18.             LOG(INFO,"Begin Recv Request");
  19.             //recv
  20.             if(Utility::ReadLine(sock,http_request.request_line)> 0){
  21.                 http_request.request_line.back() = 0;
  22.                 LOG(INFO,http_request.request_line);
  23.             }
  24.             else{
  25.                 stop = true;
  26.                 return stop;
  27.             }
  28.             //parse
  29.             std::stringstream ss(http_request.request_line);
  30.             ss>>http_request.method>>http_request.uri>>http_request.version;
  31.             //toupper
  32.             auto& mt = http_request.method;
  33.             std::transform(mt.begin(),mt.end(),mt.begin(),::toupper);
  34.             size_t pos = http_request.path.rfind(".");
  35.             if(pos != std::string::npos){
  36.                 http_request.suffix = http_request.path.substr(pos+1);
  37.             }
  38.             return stop;
  39.         }
  40.         int Recv_Parse_HttpRequest_Head(){
  41.             //recv
  42.             std::string line = "";
  43.             while(true){
  44.                 line.clear();
  45.                 if(Utility::ReadLine(sock,line)<=0){
  46.                     stop = true;
  47.                     return stop;
  48.                 }
  49.                 if(line == "\n"){
  50.                     http_request.blank = line;
  51.                     break;
  52.                 }
  53.                 line.back() = 0;
  54.                 http_request.request_header.push_back(line);
  55.                 LOG(INFO,line);
  56.             }
  57.             //parse
  58.             for(auto& item:http_request.request_header) {
  59.                 std::string key,value;
  60.                 Utility::CutString(item,key,value,": ");
  61.                 http_request.request_header_kv[key] = value;
  62.             }
  63.             return stop;
  64.         }
  65.         int Recv_HttpRequest_Body(){
  66.             auto& mt = http_request.method;
  67.             if(mt == "POST"){
  68.                 auto item = http_request.request_header_kv.find("Content-Length");
  69.                 if(item != http_request.request_header_kv.end()){
  70.                     http_request.content_length = atoi(item->second.c_str());
  71.                     int size = http_request.content_length;
  72.                     while(size){
  73.                         char ch;
  74.                         size_t s = recv(sock,&ch,1,0);
  75.                         if(s > 0){
  76.                             http_request.request_body.push_back(ch);
  77.                             size--;
  78.                         }
  79.                         else if(s == 0){
  80.                             break;
  81.                         }
  82.                         else {
  83.                             stop = true;
  84.                             return stop;
  85.                             LOG(ERROR,"Recv RequestBody Error");
  86.                             exit(1);
  87.                         }
  88.                     }
  89.                 }
  90.             }
  91.             return stop;
  92.         }
  93.         void Build_HttpResbonse(){
  94.             http_request.path = "wwwroot";
  95.             auto& mt = http_request.method;
  96.             std::string tmp;
  97.             struct stat buf;
  98.             //检查方法是否合法
  99.             if(mt!= "GET" && mt != "POST"){
  100.                 LOG(INFO,"Request Method Is Not Law!");
  101.                 http_resbonse.status_code = NOT_FOUND;
  102.                 goto END;
  103.             }
  104.             if(mt == "GET"){
  105.                 //如果是get方法,检查是否带参
  106.                 size_t pos = http_request.uri.find("?");
  107.                 if(pos != std::string::npos){
  108.                     Utility::CutString(http_request.uri,tmp,http_request.query,"?");
  109.                     http_request.cgi = true;
  110.                 }
  111.                 else {
  112.                     tmp = http_request.uri;
  113.                 }
  114.             }
  115.             else if(mt == "POST"){
  116.                 tmp = http_request.uri;
  117.                 http_request.cgi = true;
  118.             }
  119.             else{}
  120.             //拼接web根目录
  121.             http_request.path += tmp;
  122.             if(http_request.path.back() == '/'){
  123.                 http_request.path += "index.html";
  124.             }
  125.             //检查资源是否存在
  126.             if(!stat(http_request.path.c_str(),&buf)){
  127.                 //检查资源是否是目录
  128.                 if(S_ISDIR(buf.st_mode)){
  129.                     http_request.path += "/";
  130.                     http_request.path += "index.html";
  131.                     stat(http_request.path.c_str(),&buf);
  132.                 }//检查资源是否是可自行文件
  133.                 else if((S_IXUSR & buf.st_mode)||(S_IXGRP & buf.st_mode)||(S_IXOTH & buf.st_mode)){
  134.                     http_request.cgi = true;
  135.                 }
  136.                 http_request.size = buf.st_size;
  137.             }
  138.             else {
  139.                 LOG(INFO,http_request.path +"Resorce Is Not Exist");
  140.                 http_resbonse.status_code = NOT_FOUND;
  141.                 goto END;
  142.             }
  143.             if(http_request.cgi){
  144.                 http_resbonse.status_code = HandlerCgi();
  145.             }
  146.             else{
  147.                 http_resbonse.status_code = HandlerNonCgi();
  148.             }
  149. END:
  150.             Build_HttpResbonseHelper();
  151.         }
  152.         int HandlerNonCgi(){
  153.             auto& path = http_request.path;
  154.             http_resbonse.fd = open(path.c_str(),O_RDONLY);
  155.             if(http_resbonse.fd > 0){
  156.                 return OK;
  157.             }
  158.             return NOT_FOUND;
  159.         }
  160.         int HandlerCgi(){
  161.             auto& bin = http_request.path;
  162.             auto& query_get = http_request.query;
  163.             auto& query_post = http_request.request_body;
  164.             int upfd[2],downfd[2];
  165.             if(pipe(upfd)<0 || pipe(downfd)<0){
  166.                 LOG(ERROR,"PIPE ERROR");
  167.                 return ERROR_SERVER;
  168.             }
  169.             pid_t pid = fork();
  170.             if(pid == 0){
  171.                 close(upfd[0]);
  172.                 close(downfd[1]);
  173.                 dup2(downfd[0],0);
  174.                 dup2(upfd[1],1);
  175.                  std::string method = "METHOD=";
  176.                 method += http_request.method;
  177.                 putenv((char*)method.c_str());         
  178.                 if(http_request.method == "GET"){
  179.                     std::string query = "QUERY=";
  180.                     query += query_get;
  181.                     putenv((char*)query.c_str());
  182.                 }
  183.                 else if(http_request.method == "POST"){
  184.                     int cl = http_request.content_length;
  185.                     std::string content_length = "CONTENT_LENGTH=";
  186.                     content_length += std::to_string(cl);
  187.                     putenv((char*)content_length.c_str());
  188.                 }
  189.                 execl(bin.c_str(),bin.c_str(),nullptr);
  190.                 exit(1);
  191.             }
  192.             else if(pid > 0){
  193.                 close(upfd[1]);
  194.                 close(downfd[0]);
  195.                 std::string& method = http_request.method;
  196.                 if(method == "POST"){
  197.                     const char* query = query_post.c_str();
  198.                     ssize_t total = 0,size = 0;
  199.                     while(total < http_request.content_length && \
  200.                             (size = write(downfd[1],query+total,http_request.content_length - total)) > 0){
  201.                         total += size;
  202.                     }
  203.                 }
  204.                 std::string& body = http_resbonse.resbonse_body;
  205.                 char ch = 0;
  206.                 while(read(upfd[0],&ch,1) > 0){
  207.                     body.push_back(ch);
  208.                 }
  209.                 int status = 0;
  210.                 int ret = waitpid(pid,&status,0);
  211.                 if(ret == pid){
  212.                     if(WIFEXITED(status) && !WEXITSTATUS(status)){
  213.                         return OK;
  214.                     }
  215.                     else {
  216.                         LOG(ERROR,"----------");
  217.                         return ERROR_SERVER;
  218.                     }
  219.                 }
  220.                 close(upfd[0]);
  221.                 close(downfd[1]);
  222.             }
  223.             else{
  224.                 LOG(ERROR,"Create SonProcess Unsucess");
  225.                 return 404;
  226.             }
  227.         }
  228.         void Build_HttpResbonseHelper(){
  229.             int code = http_resbonse.status_code;
  230.             auto& status_line = http_resbonse.resbonse_line;
  231.             status_line = VERSION;
  232.             status_line += " ";
  233.             status_line += std::to_string(code);
  234.             status_line += " ";
  235.             status_line += StatusCodeDesc(code);
  236.             status_line += LINE_END;
  237.             if(code == 200){
  238.                 BuildOKResbonse();
  239.             }
  240.             else {
  241.                 BuildErrorResbonse(code);
  242.             }
  243.         }
  244.         void BuildOKResbonse(){
  245.             std::string line = "Content-Length: ";
  246.             if(http_request.cgi){
  247.                 line += std::to_string(http_resbonse.resbonse_body.size());
  248.             }
  249.             else {
  250.                 line += std::to_string(http_request.size);
  251.             }
  252.             line+=LINE_END;
  253.             http_resbonse.resbonse_header.push_back(line);
  254.             line = "Content-Type: ";
  255.             line += SuffixDesc(http_request.suffix);
  256.             line += LINE_END;
  257.             http_resbonse.resbonse_header.push_back(line);
  258.         }
  259.         void BuildErrorResbonse(int code){
  260.             http_request.cgi = false;
  261.             std::string page = GetErrorFile(code);
  262.             std::cout<<"page"<<page<<std::endl;
  263.             http_resbonse.fd = open(page.c_str(),O_RDONLY);
  264.             if(http_resbonse.fd > 0){
  265.                 struct stat buf;
  266.                 stat(page.c_str(),&buf);
  267.                 http_request.size = buf.st_size;
  268.                 std::string line = "Content-type: text/html";
  269.                 line += LINE_END;
  270.                 http_resbonse.resbonse_header.push_back(line);
  271.                 line = "Content-Length: ";
  272.                 line += std::to_string(buf.st_size);
  273.                 line += LINE_END;
  274.                 http_resbonse.resbonse_header.push_back(line);
  275.             }
  276.             
  277.         }
  278.         void Send_HttpResbonse(){
  279.             auto& line = http_resbonse.resbonse_line;
  280.             send(sock,line.c_str(),line.size(),0);
  281.             for(auto& item : http_resbonse.resbonse_header){
  282.                 send(sock,item.c_str(),item.size(),0);
  283.             }
  284.             send(sock,http_resbonse.blank.c_str(),http_resbonse.blank.size(),0);
  285.             if(http_request.cgi){
  286.                 send(sock,http_resbonse.resbonse_body.c_str(),http_resbonse.resbonse_body.size(),0);
  287.             }
  288.             else {
  289.                 sendfile(sock,http_resbonse.fd,nullptr,http_request.size);
  290.                 close(http_resbonse.fd);
  291.             }
  292.         }
  293.         ~EndPoint(){
  294.             close(sock);
  295.         }
  296. };
复制代码
2.5 CGI技能
  1.    int HandlerCgi(){
  2.             auto& bin = http_request.path;
  3.             auto& query_get = http_request.query;
  4.             auto& query_post = http_request.request_body;
  5.             int upfd[2],downfd[2];
  6.             if(pipe(upfd)<0 || pipe(downfd)<0){
  7.                 LOG(ERROR,"PIPE ERROR");
  8.                 return ERROR_SERVER;
  9.             }
  10.             pid_t pid = fork();
  11.             if(pid == 0){        //子进程导入环境变量
  12.                 close(upfd[0]);
  13.                 close(downfd[1]);
  14.                 dup2(downfd[0],0);
  15.                 dup2(upfd[1],1);
  16.                  std::string method = "METHOD=";
  17.                 method += http_request.method;
  18.                 putenv((char*)method.c_str());         
  19.                 if(http_request.method == "GET"){
  20.                     std::string query = "QUERY=";
  21.                     query += query_get;
  22.                     putenv((char*)query.c_str());
  23.                 }
  24.                 else if(http_request.method == "POST"){
  25.                     int cl = http_request.content_length;
  26.                     std::string content_length = "CONTENT_LENGTH=";
  27.                     content_length += std::to_string(cl);
  28.                     putenv((char*)content_length.c_str());
  29.                 }
  30.                 execl(bin.c_str(),bin.c_str(),nullptr);
  31.                 exit(1);
  32.             }
  33.             else if(pid > 0){    //父进程写入数据 和 读取数据
  34.                 close(upfd[1]);
  35.                 close(downfd[0]);
  36.                 std::string& method = http_request.method;
  37.                 if(method == "POST"){
  38.                     const char* query = query_post.c_str();
  39.                     ssize_t total = 0,size = 0;
  40.                     while(total < http_request.content_length && \
  41.                             (size = write(downfd[1],query+total,http_request.content_length - total)) > 0){
  42.                         total += size;
  43.                     }
  44.                 }
  45.                 std::string& body = http_resbonse.resbonse_body;
  46.                 char ch = 0;
  47.                 while(read(upfd[0],&ch,1) > 0){
  48.                     body.push_back(ch);
  49.                 }
  50.                 int status = 0;
  51.                 int ret = waitpid(pid,&status,0);
  52.                 if(ret == pid){
  53.                     if(WIFEXITED(status) && !WEXITSTATUS(status)){
  54.                         return OK;
  55.                     }
  56.                     else {
  57.                         LOG(ERROR,"----------");
  58.                         return ERROR_SERVER;
  59.                     }
  60.                 }
  61.                 close(upfd[0]);
  62.                 close(downfd[1]);
  63.             }
  64.             else{
  65.                 LOG(ERROR,"Create SonProcess Unsucess");
  66.                 return 404;
  67.             }
  68.         }
复制代码
test_cgi
  1. #include <iostream>
  2. #include <cstdlib>
  3. #include <string>
  4. #include <unistd.h>
  5. bool GetQuery(std::string& query){
  6.     std::string method = getenv("METHOD");
  7.     if(method == "GET"){
  8.         query = getenv("QUERY");
  9.     }
  10.     else if(method == "POST"){
  11.         int content_length = atoi(getenv("CONTENT_LENGTH"));
  12.         char ch = 0;
  13.         while(content_length){
  14.             ssize_t s = read(0,&ch,1);
  15.             if(s>0){
  16.                 content_length--;
  17.                 query.push_back(ch);
  18.             }
  19.             else if(s == 0){
  20.                 break;
  21.             }
  22.             else{
  23.                 return false;
  24.             }
  25.         }
  26.     }
  27.     return true;
  28. }
  29. void CutQuery(std::string& tar,std::string& key,std::string& value,const std::string& sep){
  30.     size_t pos = tar.find(sep);
  31.     if(pos != std::string::npos){
  32.         key = tar.substr(0,pos);
  33.         value = tar.substr(pos+sep.size());
  34.     }
  35. }
  36. int main(){
  37.     std::string query;
  38.     GetQuery(query);
  39.     std::string str1,str2;
  40.     std::string name1,name2;
  41.     std::string value1,value2;
  42.     CutQuery(query,str1,str2,"&");
  43.     CutQuery(str1,name1,value1,"=");
  44.     CutQuery(str2,name2,value2,"=");
  45.     std::cout<<name1<<":"<<value1<<std::endl;
  46.     std::cout<<name2<<":"<<value2<<std::endl;
  47.     return 0;
  48. }
复制代码
2.5 线程池技能


  1. #pragma once
  2. #include <iostream>
  3. #include "Task.hpp"
  4. #include <pthread.h>
  5. #include <queue>
  6. #define NUM 10
  7. class ThreadPool{
  8.     private:
  9.         int num;
  10.         std::queue<TASK> TaskQueue;
  11.         pthread_mutex_t lock;
  12.         pthread_cond_t cond;
  13.         ThreadPool(int _num = NUM):num(_num){
  14.             pthread_mutex_init(&lock,nullptr);
  15.             pthread_cond_init(&cond,nullptr);
  16.         }
  17.         ThreadPool(const ThreadPool& TP){}
  18.         ~ThreadPool(){
  19.             pthread_mutex_destroy(&lock);
  20.             pthread_cond_destroy(&cond);
  21.         }
  22.         static ThreadPool* tp;
  23.     public:
  24.         static ThreadPool* GetInstance(int _num = NUM){
  25.            static pthread_mutex_t t = PTHREAD_MUTEX_INITIALIZER;
  26.             if(tp == nullptr){
  27.                 pthread_mutex_lock(&t);
  28.                 if(tp == nullptr){
  29.                     tp = new ThreadPool(_num);
  30.                     tp->InitThreadPool();
  31.                 }
  32.                 pthread_mutex_unlock(&t);
  33.             }
  34.             return tp;
  35.         }
  36.         static void* ThreadRoutine(void* arg){
  37.             ThreadPool* tp = (ThreadPool*)arg;
  38.             while(true){
  39.                 TASK task;
  40.                 tp->Lock();
  41.                 while(tp->IsEmpty()){
  42.                     tp->ThreadWait();
  43.                 }
  44.                 tp->PopTask(task);
  45.                 tp->Unlock();
  46.                 task.ProcessOn();
  47.             }
  48.         }
  49.         bool InitThreadPool(){
  50.             for(int i = 0; i< num;i++){
  51.                 pthread_t tid;
  52.                 if(0 != pthread_create(&tid,nullptr, ThreadRoutine,this)){
  53.                     LOG(FATAL,"create pthread unsucess");
  54.                     return false;
  55.                 }
  56.             }
  57.             return true;
  58.         }
  59.         void Lock(){
  60.             pthread_mutex_lock(&lock);
  61.         }
  62.         void Unlock(){
  63.             pthread_mutex_unlock(&lock);
  64.         }
  65.         void ThreadWait(){
  66.             pthread_cond_wait(&cond,&lock);
  67.         }
  68.         void ThreadWakeup(){
  69.             pthread_cond_signal(&cond);
  70.         }
  71.         bool IsEmpty(){
  72.             return TaskQueue.size() == 0 ? true:false;
  73.         }
  74.         void PushTask(const TASK& task){
  75.             Lock();
  76.             TaskQueue.push(task);
  77.             Unlock();                             
  78.             ThreadWakeup();
  79.             //Routine函数中cond_wait用while循环判断主要是解锁和唤醒代码的顺序问题
  80.             //如果先解锁,那么就会有其他进程抢到锁开始执行后续,但不一定是等待进程在执行
  81.             //所以当后面唤醒等待进程时候,可能会发现任务队列仍然为空,但却继续往下执行,这不符合我们的逻辑
  82.         }
  83.         void PopTask(TASK& task){
  84.             task = TaskQueue.front();
  85.             TaskQueue.pop();
  86.         }
  87. };
  88. ThreadPool* ThreadPool::tp = nullptr;
复制代码
3、项目总结

   聚焦于处理HTTP的哀求和构建对应响应; 我们紧张研究基于 HTTP/1.0 短连接 的GET和POST方法;获得哀求,分析哀求,错误处理等; 制定特定的网页src用于返回; 引入简单的日志体系
  搭建CGI机制;父子管道,计划dup2重定向,情况变量传参等
  引入线程池;接纳多线程技能,缓解内存开销.

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大号在练葵花宝典

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表