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;
    }
相关推荐
板鸭〈小号〉2 小时前
进程间关系(linux)
linux·运维·服务器
洛_尘3 小时前
JAVA EE初阶 6: 网络编程套接字
网络·1024程序员节
alwaysuzybaiyy3 小时前
物联网定位技术实验报告|实验一 Wi-Fi指纹定位
网络·人工智能·物联网
脏脏a3 小时前
【Linux】冯诺依曼体系结构与操作系统概述
linux·服务器
拾忆,想起4 小时前
Dubbo异步调用实战指南:提升微服务并发性能
java·服务器·网络协议·微服务·云原生·架构·dubbo
adnyting4 小时前
【Linux日新月异(三)】CentOS 7软件管理深度指南:从YUM到源码编译
linux·运维·centos
知识分享小能手4 小时前
openEuler入门学习教程,从入门到精通,云计算与 Linux 操作系统概述(1)
linux·云计算·openeuler
励志成为编程高手4 小时前
在ubuntu中创建根文件系统
linux·ubuntu·rootfs
HIT_Weston4 小时前
31、【Ubuntu】【远程开发】内网穿透:反向隧道建立(三)
linux·运维·ubuntu