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设计框架
#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的实现
#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 testint main(){Socket* sk = new TcpSocket();sk->BuildTcpSocket(8080);}
}



2.TcpServer.hpp的实现
"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 infovirtual 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 testint main(){Socket* sk = new TcpSocket();sk->BuildTcpSocket(8080);}
}



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方法实现
#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.acceptauto 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:// 一定要有一个 Listensockstd::unique_ptr<Socket> _listensockp;bool _running;tcphandler_t _handler;};}


3.HttpServer.hpp的实现
#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;
};


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 的网页
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;}


