18.HTTP协议(一)

一.HTTP协议

二.URL的认识

应用层协议往往和端口号是强关联的,比如说http(80),mysql(3306),所以我们不需要体现这个端口号

浏览器会自动添加端口号

当服务器端得到了对应的URL的时候,要将字符重新进行decode(解码)

下面是URL编码的工具,你们可以尝试一下:

https://tool.chinaz.com/Tools/urlencode.aspx

三.HTTP协议请求与响应格式

1.协议的格式(大致了解)

a.Request

本质上http_request就是一个类

b.Response

状态码如404,状态码描述如Not Found

只要我们的数据格式是符合这个协议的,我们就能向浏览器发送请求

2.收发完整性 -- tcp是面向字节流的!

前面已经强调过了,这里我们就不进行强调了

四.http_server封装

1.Socket.hpp的封装

a.Socket设计框架

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>


//模板方法模式

//基类, 规定创建套接字的方法
class Socket
{
public:
    virtual ~Socket() = default;
    virtual int SocketOrDie() = 0;
    virtual int SetSocketOpt() = 0;
    virtual bool BindOrDie(int listenscokfd) = 0;
    virtual bool ListenOrDie(int listenscokfd) = 0;
    virtual int Accepter(int listenscokfd) = 0;
    virtual void Close(int fd) = 0;

    //其他方法,需要时再加

    //提供一个创建listensockfd的固定实现
    void BuildTcpSocket()
    {
        SocketOrDie();
        SetSocketOpt();
        BindOrDie();
        ListenOrDie();
    }

};

class TcpSocket : public Socket
{
public:
    virtual ~TcpSocket() = default;
    virtual int SocketOrDie() = 0;
    virtual int SetSocketOpt() = 0;
    virtual bool BindOrDie(int listenscokfd) = 0;
    virtual bool ListenOrDie(int listenscokfd) = 0;
    virtual int Accepter(int listenscokfd) = 0;
    virtual void Close(int fd) = 0;


private:

};


int main()
{
    Socket* sk = new TcpSocket();
    sk->BuildTcpSocket();
}

b.Socket的实现

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

namespace SocketModule
{
    using namespace LogModule;
    // 模板方法模式

    // 基类, 规定创建套接字的方法
    class Socket
    {
    public:
        virtual ~Socket() = default;
        virtual int SocketOrDie() = 0;
        virtual int SetSocketOpt() = 0;
        virtual bool BindOrDie(int port) = 0;
        virtual bool ListenOrDie() = 0;
        virtual int Accepter() = 0;
        virtual void Close() = 0;

        // 其他方法,需要时再加

        // 提供一个创建listensockfd的固定实现
        void BuildTcpSocket(int port)
        {
            SocketOrDie();
            SetSocketOpt();
            BindOrDie(port);
            ListenOrDie();
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket():_sockfd(gdefaultsockfd)
        {

        }
        virtual ~TcpSocket()
        {
        }
        virtual int SocketOrDie() override
        {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(LogLevel::ERROR) << "socket error";
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::DEBUG) << "socket create success: " << _sockfd;
        }
        virtual int SetSocketOpt()override
        {

        }
        virtual bool BindOrDie(int port)override
        {
            if(_sockfd == gdefaultsockfd)
            {
                return false;
            }
            InetAddr addr(port);
            int n = ::bind(_sockfd,addr.NetAddr(),addr.NetAddrLen());
            if(n < 0)
            {
                LOG(LogLevel::ERROR) << "bind error";
                exit(BIND_ERR);
            }
            LOG(LogLevel::DEBUG) << "bind create success: " << _sockfd;
            return true;
        }
        virtual bool ListenOrDie()override
        {
            if(_sockfd == gdefaultsockfd)
            {
                return false;
            }
            int n = ::listen(_sockfd,gbacklog);
            if(n < 0)
            {
                LOG(LogLevel::ERROR) << "listen error";
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::DEBUG) << "listen create success: " << _sockfd;
            return true;
        }
        virtual int Accepter()override
        {

        }
        virtual void Close()override
        {
            if(_sockfd == gdefaultsockfd)
            {
                return ;
            }
            ::close(_sockfd);
        }

    private:
        int _sockfd;
    };

    //for test
    int main()
    {
        Socket* sk = new TcpSocket();
        sk->BuildTcpSocket(8080);
    }
}

2.TcpServer.hpp的实现

cpp 复制代码
"Socket.hpp"

#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

namespace SocketModule
{
    using namespace LogModule;
    // 模板方法模式

    using SockPtr = std::shared_ptr<Socket>;
    // 基类, 规定创建套接字的方法
    class Socket
    {
    public:
        virtual ~Socket() = default;
        virtual int SocketOrDie() = 0;
        virtual int SetSocketOpt() = 0;
        virtual bool BindOrDie(int port) = 0;
        virtual bool ListenOrDie() = 0;
        virtual SockPtr Accepter(InetAddr* client) = 0;
        virtual void Close() = 0;

        // 其他方法,需要时再加

        // 提供一个创建listensockfd的固定实现
        void BuildTcpSocket(int port)
        {
            SocketOrDie();
            SetSocketOpt();
            BindOrDie(port);
            ListenOrDie();
        }
    };


    class TcpSocket : public Socket
    {
    public:
        TcpSocket():_sockfd(gdefaultsockfd)
        {

        }
        TcpSocket(int sockfd):_sockfd(sockfd)
        {

        }
        virtual ~TcpSocket()
        {
        }
        virtual int SocketOrDie() override
        {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(LogLevel::ERROR) << "socket error";
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::DEBUG) << "socket create success: " << _sockfd;
        }
        virtual int SetSocketOpt()override
        {

        }
        virtual bool BindOrDie(int port)override
        {
            if(_sockfd == gdefaultsockfd)
            {
                return false;
            }
            InetAddr addr(port);
            int n = ::bind(_sockfd,addr.NetAddr(),addr.NetAddrLen());
            if(n < 0)
            {
                LOG(LogLevel::ERROR) << "bind error";
                exit(BIND_ERR);
            }
            LOG(LogLevel::DEBUG) << "bind create success: " << _sockfd;
            return true;
        }
        virtual bool ListenOrDie()override
        {
            if(_sockfd == gdefaultsockfd)
            {
                return false;
            }
            int n = ::listen(_sockfd,gbacklog);
            if(n < 0)
            {
                LOG(LogLevel::ERROR) << "listen error";
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::DEBUG) << "listen create success: " << _sockfd;
            return true;
        }
        //1.文件描述符 2.client info
        virtual SockPtr Accepter(InetAddr* client)override
        {
            if(!client) return nullptr;
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int newsockfd = ::accept(_sockfd,CONV(&peer),&len);
            if(newsockfd < 0)
            {
                LOG(LogLevel::ERROR) << "accept error";
                return nullptr;
            }
            client->SetAddr(peer,len);
            return std::make_shared<TcpSocket>(newsockfd);
        }
        virtual void Close()override
        {
            if(_sockfd == gdefaultsockfd)
            {
                return ;
            }
            ::close(_sockfd);
        }

    private:
        int _sockfd;
    };

    //for test
    int main()
    {
        Socket* sk = new TcpSocket();
        sk->BuildTcpSocket(8080);
    }
}
cpp 复制代码
virtual int Recv(std::string* out)override
        {
            char buffer[1024];
            auto size = ::recv(_sockfd,buffer,sizeof(buffer)-1,0);
            if(size > 0)
            {
                buffer[size] = 0;
                *out = buffer;
            }
            return size;
        }
        virtual int Send(const std::string& in)override
        {
            auto size = ::send(_sockfd,in.c_str(),in.size(),0);
            return size;
        }

send和recv方法实现

cpp 复制代码
#pragma once

#include <iostream>
#include <memory>
#include <functional>
#include <sys/wait.h>
#include "Socket.hpp"

namespace TcpServerModule
{
    using namespace SocketModule;
    using namespace LogModule;
    using tcphandler_t = std::function<bool(SockPtr, InetAddr)>;

    // 它只负责进行IO,不对协议进行任何的处理
    class TcpServer
    {
    public:
        TcpServer(int port, tcphandler_t handler) : _listensockp(std::make_unique<TcpSocket>()),
                                                    _running(false),
                                                    _handler(handler)
        {
            _listensockp->BuildTcpSocket(port);
        }
        void Loop()
        {
            _running = true;
            while (_running)
            {
                InetAddr clientaddr;
                // 1.accept
                auto sockfd = _listensockp->Accepter(&clientaddr);
                if (sockfd == nullptr)
                {
                    continue;
                }
                // 2.IO处理
                LOG(LogLevel::DEBUG) << "get a new client, info is: " << clientaddr.Addr();

                pid_t id = fork();
                if (id == 0)
                {
                    _listensockp->Close();
                    if (fork() > 0)
                    {
                        exit(0);
                    }
                    _handler(sockfd, clientaddr);
                    exit(0);
                }
                sockfd->Close();
                waitpid(id, nullptr, 0);
            }
            _running = false;
        }
        ~TcpServer()
        {
            _listensockp->Close();
        }

    private:
        // 一定要有一个 Listensock
        std::unique_ptr<Socket> _listensockp;
        bool _running;
        tcphandler_t _handler;
    };

}

3.HttpServer.hpp的实现

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include "TcpSever.hpp"

using namespace TcpServerModule;


class HttpServer
{
public:
    HttpServer(int port):
        _tsvr(std::make_unique<TcpServer>(port))
    {

    }
    void Start()
    {
        _tsvr->InitServer([this](SockPtr sockfd,InetAddr client){
            return this->HandlerHttpRequest(sockfd,client);
        });
        _tsvr->Loop();
    }
    bool HandlerHttpRequest(SockPtr sockfd,InetAddr client)
    {
        LOG(LogLevel::DEBUG) << "HttpServer: get a new client: " << sockfd->Fd() 
        << " addr info: " << client.Addr();
        return true;
    }
    ~HttpServer()
    {

    }
private:
    std::unique_ptr<TcpServer> _tsvr;
};
cpp 复制代码
bool HandlerHttpRequest(SockPtr sockfd,InetAddr client)
    {
        LOG(LogLevel::DEBUG) << "HttpServer: get a new client: " << sockfd->Fd() 
        << " addr info: " << client.Addr();
        std::string http_request;
        sockfd->Recv(&http_request);
        std::cout << http_request;
        return true;
    }

我们现在将读到的消息进行打印

4.hello 的网页

cpp 复制代码
bool HandlerHttpRequest(SockPtr sockfd,InetAddr client)
    {
        LOG(LogLevel::DEBUG) << "HttpServer: get a new client: " << sockfd->Fd() 
        << " addr info: " << client.Addr();
        std::string http_request;
        //目前我们读取到的是最原始的信息
        sockfd->Recv(&http_request);
        std::cout << http_request;
        //读取请求,进行文本处理
        //1.读取请求的完整性  --  暂时不做了
        //2.完整http反序列化,http response序列化...
        std::string status_line = "HTTP/1.1 200 OK" + Sep + BlankLine;
        std::string body = "<!DOCTYPE html>\
                            <html>\
                            <head>\
                            <meta charset = \"UTF-8\">\
                            <title> Hello World</title>\
                            </head>\
                            <body>\
                            <p> Hello World</p>\
                            </body> </html>";
        std::string httpresponse = status_line + body;
        sockfd->Send(httpresponse);

        return true;
    }
相关推荐
piaoroumi3 小时前
UVC调试
linux·运维·前端
VekiSon3 小时前
Linux系统编程——标准IO
linux·运维·服务器
kaikaile19953 小时前
LDPC编解码与未编码信息的误码率比较
网络
老蒋新思维3 小时前
创客匠人 2025 万人峰会核心:AI 驱动知识产品变现革新
大数据·人工智能·网络协议·tcp/ip·创始人ip·创客匠人·知识变现
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ3 小时前
aspect实现请求校验,但是WebSocket 端点类不能被 AOP 代理解决方案
网络·websocket·网络协议
数字护盾(和中)3 小时前
从边界突破到物理破坏:APT 工控攻击链路与防御闭环
网络
Saniffer_SH4 小时前
【每日一题】PCIe答疑 - 接大量 GPU 时主板不认设备或无法启动和MMIO的可能关系?
运维·服务器·网络·人工智能·驱动开发·fpga开发·硬件工程
大白的编程日记.4 小时前
【计算网络学习笔记】Socket编程UDP实现简单聊天室
网络·笔记·学习
织元Zmetaboard4 小时前
什么是态势感知大屏?
网络·数据库
爱宇阳4 小时前
Linux 安全加固:禁用 IPv4 ICMP 重定向发送
linux·运维·安全