当前位置: 首页 > news >正文

从零实现HTTP服务器

响应:

第一部分测试代码,读取请求

Makefile

bin=httpserver #生成的可执行程序
cc=g++ #编译器名称
LD_FLAGS=-std=c++11 -lpthread #-DDEBUG=1 #链接选项
src=main.cc

$(bin):$(src)
	$(cc) -o $@ $^ $(LD_FLAGS)

.PHONY:clean
clean:
	rm -f $(bin)

1111111

main.cc

#include <iostream>
#include <string>
#include <memory>
#include "HttpServer.hpp"
static void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " port" << std::endl;;
}

int main(int argc, char *argv[])
{
    if( argc != 2 )
    {
        Usage(argv[0]);
        exit(4);
    }
    int port = atoi(argv[1]);

    std::shared_ptr<HttpServer> http_server(new HttpServer(port));
    http_server->InitServer();
    http_server->Loop();

    return 0;
}

11111

Log.hpp

#pragma once

#include <iostream>
#include <string>
#include <ctime>

#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 << "[" << level << "]" << "[" << time(nullptr) << "]" 
        << "[" << message << "]" << "[" << file_name << "]" << "[" << line << "]" << std::endl;
}

111

TcpServer.hpp

#pragma once

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "Log.hpp"

#define BACKLOG 7

class TcpServer
{
    private:
        int _port;
        int _listen_sock;
        static TcpServer* _svr; // 指向单例对象的static指针,懒汉模式
    private: // 单例模式
        TcpServer(int port)
            : _port(port)
            , _listen_sock(-1)
        {}
        TcpServer()
        {}
        TcpServer(const TcpServer& s) = delete;
        TcpServer* operator=(const TcpServer& s) = delete;
    public:
        static TcpServer *GetInstance(int port)
        {
            static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
            if(nullptr == _svr)
            {
                pthread_mutex_lock(&lock);
                if(nullptr == _svr)
                {
                    _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 socket 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;

11111111

HttpServer.hpp

#pragma once

#include <iostream>
#include <pthread.h>
#include <signal.h>
#include "Log.hpp"
#include "TcpServer.hpp"
#include "Protocol.hpp"

#define PORT 7777

class HttpServer
{
    private:
        int _port;
        bool _stop;
    public:
        HttpServer(int port = PORT)
            : _port(port)
            , _stop(false)
        {}
        void InitServer()
        {
            // 信号SIGPIPE进行忽略,否则在写入时候,可能直接崩溃server
            signal(SIGPIPE, SIG_IGN); 
        }
        void Loop()
        {
            TcpServer *tsvr = TcpServer::GetInstance(_port);
            LOG(INFO, "Loop begin");
            while(!_stop)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(tsvr->Sock(), (struct sockaddr*)&peer, &len);
                if(sock < 0) // 获取套接字失败
                {
                    continue;
                }
                LOG(INFO, "Get a new link");
                int *_sock = new int(sock); // 暂时方案
                pthread_t pid;
                pthread_create(&pid, nullptr, Entrance::HandlerRequest, _sock);
                pthread_detach(pid);
            }
        }
        ~HttpServer()
        {}
};

1111

Protocol.hpp

#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"
#include "Util.hpp"

#define DEBUG
class Entrance // 线程入口
{
public:
    Entrance()
    {}
    ~Entrance()
    {}
    static void* HandlerRequest(void * _sock)
    {
        int sock = *(int*)_sock; // 临时测试方案
        LOG(INFO, "Hander Request Begin");
#ifdef DEBUG
        //For Test
        char buffer[4096];
        recv(sock, buffer, sizeof(buffer), 0);
        std::cout << "-------------begin----------------" << std::endl;
        std::cout << buffer << std::endl;
        std::cout << "-------------end----------------" << std::endl;
#else 
        EndPoint* ep = new EndPoint(sock);
        ep->RecvHttpRequest();
        ep->BuildHttpResponse();
        ep->SendHttpResponse();
        delete ep;
#endif
        LOG(INFO, "Hander Request End");
    }
};

测试结果

11111

第二部分测试代码,解析请求,返回静态网页

过程截图:

读取请求行和报头

解析uri:

返回了静态网页:

telnet测试:

加上了响应类型和长度:

第二份代码链接:

Linux_Code: 存放linux学习代码 - Gitee.com


相关文章:

  • Quartz修仙指南:从定时任务萌新到调度大能的终极奥义
  • 题目 2701: 蓝桥杯2022年第十三届决赛真题-取模(C/C++/Java组)
  • 计算机网络-传输层基础概念
  • 【5】深入学习npm-Nodejs开发入门
  • 【Amazon EC2】为何基于浏览器的EC2 Instance Connect 客户端连接不上EC2实例
  • 第一个Qt开发的OpenCV程序
  • 关税扰动下市场波动,如何寻找确定性的长期之锚?
  • 一周学会Pandas2 Python数据处理与分析-Pandas2读取CSV
  • 榕壹云无人共享系统:基于SpringBoot+MySQL+UniApp的物联网共享解决方案
  • 可以使用多种AI模型自动化制作web和手机应用软件的利器:bolt.diy
  • (十五)安卓开发中不同类型的view之间继承关系详解
  • 去重新闻数据中重复的正文内容(body 字段),并把唯一的新闻内容保存到一个新的 JSON 文件中
  • Ubuntu 软件卸载与清理终极指南
  • 在项目中,引入【全局异常处理器】
  • Spring IoC深度解析:掌控Bean存储艺术与分层架构的智慧​​
  • 通过代码获取接口文档工具
  • 智膳优选 | AI赋能的智慧食堂管理专家 —— 基于飞书多维表格和扣子(Coze)的智能解决方案
  • 医院PACS系统源码
  • 初级社会工作者考试精选题库
  • react+Tesseract.js实现前端拍照获取/选择文件等文字识别OCR
  • 免费淘宝网站建设/seo优化师
  • wordpress的配置文件/seo推广服务哪家好
  • 韩国网站怎么打开/山东网页定制
  • 典型的b2b网站有哪些/网站策划书模板
  • 至尊传奇手游官方正版下载/娄底seo
  • 陕西网站建设报价/长春网站建设团队