自己写Http服务器(二)主体框架

本文详细介绍了HTTP服务器的开发过程,包括服务器如何接收并处理HTTP请求,使用TCP协议进行数据传输,以及通过创建线程来处理每个客户端请求,确保服务器的高效运行。

在正式写Http服务器之前,我们先简单的了解一下一个http服务器实际运行时候的一个大致过程:一个http请求从客户端发出,服务器接收到客户端的请求进行处理,再响应给客户端。

所以我们首先对服务器接收请求这一方面进行编写,保证服务器首先能够接收到请求,其次再对接收到的请求处理以及进一步的响应。

其次我们要明白的http协议是属于应用层的协议,这就意味着我们的数据在传输时候肯定要先从 应用层由定向下逐层封装到物理层,再到目标主机,再由底向上解包分用交给对应的进程。

我们知道在应用层以下是 传输层,接着再往下网络层,数据链路层,再到物理层。

应用层以下的数据传输已经由操作系统以及相应的硬件设备为我们实现,系统为我们提供了相应的接口让我们可以从 传输层 拿取数据以及发送数据。

而在传输层有两个协议可以供我们使用,TCP和UDP,这里我们使用TCP协议作为传输层协议进行我们http服务器的开发

 

为了方便我们对于服务器运行过程中状态的监测,我们可以专门写一个Log.h的头文件,对我们各个状态进行输出

Log.h

#ifndef __LOG_HPP__
#define __LOG_HPP__

#include<iostream>
#include<string>
#include<sys/time.h>

#define INFO 0
#define WARNING 1
#define ERROR 2

uint64_t GetNowTimeSteamp()
{
    struct timeval nowtime;
    gettimeofday(&nowtime, NULL);
    return nowtime.tv_sec;
}

std::string GetLogLevel(int level)
{
    switch(level)
    {
        case 0:
            return "INFO";
        case 1:
            return "WARNING";
        case 2:

            return "ERROR";

        default:
            return "UNKNOW";
    }
}



void Log(int level, std::string message, std::string file, int line)
{

    std::cout << "[ " << GetNowTimeSteamp() << " ] [ " << GetLogLevel(level) << " ] [ " << file << " ] [ " << line << " ]";
    std::cout << " [ " << message << " ]" << std::endl;
}

#define LOG(level,message) Log(level,message,__FILE__,__LINE__);
#endif

接着在HttpdServer.hpp中对我们服务器的主体HttpdServer类进行实现

HttpServer.hpp:

#ifndef __HTTPD_SERVER_HPP__
#define __HTTPD_SERVER_HPP__

#include<pthread.h>
#include"ProtocolUtil.hpp"

class HttpdServer
{
private:    
    int _listen_sock;    
    int _prot;    //端口号

public:
    HttpdServer(int prot)
    {
        _prot = prot;
        _listen_sock = -1;
    }

    void Init()
    {

        // 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
        //int socket(int domain, int type, int protocol);
        
        //监听
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0);

        if(_listen_sock < 0)
        {
            LOG(ERROR, "Creat Socket Error!");
            exit(2);
        }

        /*int setsockopt(
        SOCKET s,
        int level,
        int optname,
        const char* optval,
        int optlen
        );*/

        //close socket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:

        bool Reuseaddr = true;
        setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&Reuseaddr, sizeof(Reuseaddr));
      
        sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(_prot);
        local.sin_addr.s_addr = INADDR_ANY;
        
        // 绑定端⼝口号 (TCP/UDP, 服务器)      
        //int bind(int socket, const struct sockaddr *address,socklen_t address_len);

        if( bind( _listen_sock, (const struct sockaddr*)&local, sizeof(local)) < 0)
        {
            LOG(ERROR, "Bind Socket Error!!");
            exit(3);
        }
       
        // 开始监听socket (TCP, 服务器)
        //int listen(int socket, int backlog);
       
        if( listen(_listen_sock, 5) < 0)
        {
            LOG(ERROR, "Listen Socket Error!!");
            exit(4);
        }
       
        LOG(INFO, "Init Server Success!!");
    }
   
    void Start()
    {
        LOG(INFO, "Start Server Begin!!");
        while(1)
        {
            //接收请求 (TCP, 服务器)
            //int accept(int socket, struct sockaddr* address, socklen_t* address_len);
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sock = accept(_listen_sock, (struct sockaddr*)&client, &len);

            if(sock < 0)
            {
                LOG(WARNING, "Accept Error!!");
                continue;
            }

            LOG(INFO, "Get New Client, Create Thread Handler Rquest...");

            pthread_t tid;
            int *sock_p = new int;
            *sock_p = sock;

            //int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*star t_routine)(void*), void *arg);
            //这里我们接收到新的请求以后创建线程对请求做处理,这样保证我们的主进程不会因为要处理请求而阻塞
            //HandlerRequest为我们线程的入口函数,我们将accept到的文件描述符作为参数传递给线程,后面我们在ProtocolUtil.hpp中实现相关的处理

            pthread_create(&tid, NULL, Entry::HandlerRequest, (void*)sock_p);
        }
    }

    ~HttpdServer()
    {
        if(_listen_sock != -1)
        {
            close(_listen_sock);
        }
        _prot = -1;
    }
};

#endif

 

然后我们在主函数中创建HttpdServer对象,然后让它运行

HttpdServer.cc

#include"HttpdServer.hpp"
#include<unistd.h>

void Usage(std::string procname)
{
    std::cout<< "How to use: " << procname << " port" << std::endl;
}

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);    //打印使用手册
        exit(1);
    }
   
    HttpdServer* server = new HttpdServer(atoi(argv[1]));    //new HttpdServer对象
    server->Init();        //对象初始化
    server->Start();    //启动server

    delete server;        

    return 0;

}

测试

以上就是对于Http服务器主体上的设计

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值