【Linux】网络之http协议

文章目录

URL

我们所说的网址就是URL

如果在参数中出现?/特殊字符,会自动转义,/ ?字符已经被url特殊处理了

'+'被转义为%2b

🚩HTTP协议格式

请求格式


请求行:方法+URL+版本号+\r\n

请求报头:每行一对键值对+\r\n
\r\n空行,(没有空行的话无法区分报头和正文)

正文,(怎么确定正文的大小?报头中content-length确定)

例如

HTTP常用方法


常用的就GET,POST

响应格式

与请求类似,

常见状态码


常用状态码:200(OK),404 (Not Found),403 (Forbidden),302(Redirect,重定向(跳转网站)),504(Bad Gateway)

HTTP常见Header

Content-Type: 数据类型(text/html等)

Content-Length: Body的长度

Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;

User-Agent: 声明用户的操作系统和浏览器版本信息;

referer: 当前页面是从哪个页面跳转过来的;

location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;

Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;

🚩模拟实现HTTP服务器

目录结构

c 复制代码
//socket.hpp
#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "log.hpp"
#include <string.h>
#include <unistd.h>
using namespace std;

Log lg;
const int backlog=10;

enum{
    SocketErr=2,
    BindErr,
    ListenErr,
};

class Sock{
public:
    Sock()
    {}
    ~Sock()
    {}

    void Socket()
    {
        sockfd_=socket(AF_INET,SOCK_STREAM,0);
        if(sockfd_<0)
        {
            lg(Fatal,"socket error, strerror: %s, errno: %d",strerror(errno),errno);
            exit(SocketErr);
        }
    }
    void Bind(uint16_t port)
    {
        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;

        if(bind(sockfd_,(struct sockaddr*)&local,sizeof(local))<0)
        {
            lg(Fatal,"Bind error, strerror: %s, errno: %d",strerror(errno),errno);
            exit(BindErr);
        }

    }
    void Listen()
    {
        if(listen(sockfd_,backlog)<0)
        {
            lg(Fatal,"Listen Error, strerror: %s, errno: %d",strerror(errno),errno);
            exit(ListenErr);
        }
    }
    int Accept(std::string* ip,uint16_t *port)
    {
        struct sockaddr_in peer;
        socklen_t len=sizeof(peer);
        int newfd=accept(sockfd_,(struct sockaddr*)&peer,&len);
        if(newfd<0)
        {
            lg(Warning,"accept error, strerror: %s, errno: %d",strerror(errno),errno);
            return -1;
        }
        char ipstr[64];
        inet_ntop(AF_INET,&peer.sin_addr,ipstr,sizeof(ipstr));
        *ip=ipstr;
        *port=ntohs(peer.sin_port);
        
        return newfd;
    }
    bool Connect(const string& ip,const uint16_t& port)
    {
        struct sockaddr_in peer;
        memset(&peer,0,sizeof(peer));
        peer.sin_family=AF_INET;
        peer.sin_port=htons(port);
        inet_pton(AF_INET,ip.c_str(),&(peer.sin_addr));

        int n=connect(sockfd_,(struct sockaddr*)&peer,sizeof(peer));
        if(n<0)
        {
            cerr<<"Connect to"<<ip<<":"<<port<<endl;
            return false;
        }
        return true;
    }
    void Close()
    {
        close(sockfd_);
    }
    int Fd()
    {
        return sockfd_;
    }
private:
    int sockfd_;
};

//server.hpp
#pragma once
#include <iostream>
#include "Socket.hpp"
#include <string>
#include <pthread.h>
#include <fstream>
#include <vector>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unordered_map>

std::string blank_line="\r\n";
std::string wwwroot="./wwwroot";
std::string homepage="index.html";

class HttpServer;

class ThreadData
{
public:
    ThreadData(int sockfd,HttpServer* hs)
    :_sockfd(sockfd),_hs(hs)
    {

    }
public:
    int _sockfd;
    HttpServer* _hs;
};

class HttpResponse
{
public:
    void Derialize(std::string req)
    {
        while(true)
        {
            std::size_t pos=req.find(blank_line);
            if(pos==string::npos)break;
            std::string temp=req.substr(0,pos);
            if(temp.empty())//空行
            break;
            vc.push_back(temp);
            req.erase(0,pos+blank_line.size());
        }
        text+=req;
        
    }
    void Parse()
    {
        std::stringstream ss(vc[0]);
        ss>>method>>url>>version;
        
        file_path=wwwroot;
        if(url=="/"||url=="index.html")
        {
            file_path+="/";
            file_path+=homepage; //wwwroot/index.html
        }
        else{
            file_path+=url; //wwwroot/a/b/t/t
        }
    }
    void Dubugprint()
    {
        for(auto line:vc)
        {
            std::cout<<line<<std::endl;
            std::cout<<"------------"<<std::endl;
        }
        std::cout<<"method: "<<method<<std::endl;
        std::cout<<"url: "<<url<<std::endl;
        std::cout<<"version: "<<version<<std::endl;
        std::cout<<"file_path: "<<file_path<<std::endl;
        std::cout<<"text: "<<text<<std::endl;
    }   
public:
    vector<string> vc;
    string text;

    std::string method;
    std::string url;
    std::string version;

    std::string file_path;
};

class HttpServer
{
public:
    HttpServer(uint16_t port)
    :_port(port)
    {

    }
    void start()
    {
        _listen.Socket();
        _listen.Bind(_port);
        _listen.Listen();
        lg(Info,"server create success");
        for(;;)
        {
            std::string clientip;
            uint16_t port;
            int sockfd=_listen.Accept(&clientip,&port);
            lg(Info,"get a newfd:%d",sockfd);
            pthread_t tid;
            ThreadData* td=new ThreadData(sockfd,this);
            pthread_create(&tid,nullptr,ThreadRun,td);
        }
    }
    static std::string ReadHtmlContent(const std::string& htmlpath)
    {
        std::ifstream in(htmlpath);
        if(!in.is_open())
        {
            return "404";
        }
        std::string content;
        std::string line;
        while(getline(in,line))
        {
            content+=line;
        }
        in.close();
        return content;
    }
    static void HandlerHttp(int sockfd)
    {
        char buffer[10240];
        ssize_t n=recv(sockfd,buffer,sizeof(buffer)-1,0);
        if(n>0)
        {
            buffer[n]=0;
            //std::cout<<buffer<<endl;
            //返回响应过程
           

            HttpResponse rep;
            rep.Derialize(buffer);
            rep.Parse();
            rep.Dubugprint();

            std::string text=ReadHtmlContent(rep.file_path);
            
            std::string response_line="HTTP/1.0 299 OK\r\n";
            std::string response_header ="Content-Length: ";
            response_header+=std::to_string(text.size()); //"Content-length: 11"

            std::string response=response_line;
            response+=response_header; 
            response+="\r\n";
            response += blank_line;

            response+=text;
            send(sockfd,response.c_str(),response.size(),0);
        }
        close(sockfd);
    }
    static void *ThreadRun(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td=static_cast<ThreadData*>(args);

        HandlerHttp(td->_sockfd);
        
        close(td->_sockfd);
        delete td;
        return nullptr;
    }
Sock _listen;
uint16_t _port;

//index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>这个是我们的首页</h1>


    <a href="http://115.175.6.15:8080/a/b/hello.html">到第二张网页</a>

    <a href="http://115.175.6.15:8080/c/d/hello2.html">到第三张网页</a>
</body>
</html>
};

//hello.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>第二张图片</h1>
    <h1>第二张图片</h1>
    <h1>第二张图片</h1>

    <a href="http://115.175.6.15:8080">回到首页</a>

    <a href="http://115.175.6.15:8080/c/d/hello2.html">到第三张网页</a>
</body>
</html>
//hello2.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>第三张图片</h1>

    <a href="http://115.175.6.15:8080">回到首页</a>

    <a href="http://115.175.6.15:8080/a/b/hello.html">到第三张网页</a>
</body>
</html>


//Log.hpp
#pragma once
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

using namespace std;
#define SIZE 1024

#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define Screen    1
#define Onefile   2
#define Classfile 3

#define Logfile "log.txt"
class Log{
public:
    Log()
    {
        printmethod=Screen;
        path="./log/";
        mkdir(path.c_str(), 0777);
    }
    void Enable(int method)
    {
        printmethod=method;
    }
    string levelToString(int level)
    {
         switch(level)
        {
            case Info:
            return "Info";
            case Debug:
            return "Debug";
            case Warning:
            return "Warning";
            case Error:
            return "Error";
            case Fatal:
            return "Fatal";
       }
    }
/*
void logmassage(int level,char* format, ...)
{
    time_t t=time(nullptr);
    struct tm *ctime = localtime(&t);
    char leftbuffer[SIZE];
    snprintf(leftbuffer,sizeof(leftbuffer),"[%s][%d-%d-%d %d %d %d]",levelToString(level).c_str(),ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,ctime->tm_min,ctime->tm_sec);

    va_list(s);
    va_start(s,format);

    char rightbuffer[SIZE];
    vsnprintf(rightbuffer,sizeof(rightbuffer),format,s);
    va_end(s);

    char logtxt[SIZE*2];
    snprintf(logtxt,sizeof(logtxt),"%s %s\n",leftbuffer,rightbuffer);

    printf("%s\n",logtxt);

}*/
    void operator()(int level,const char* format, ...)
    {
    time_t t=time(nullptr);
    struct tm *ctime = localtime(&t);
    char leftbuffer[SIZE];
    snprintf(leftbuffer,sizeof(leftbuffer),"[%s][%d-%d-%d %d %d %d]",levelToString(level).c_str(),ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,ctime->tm_hour,ctime->tm_min,ctime->tm_sec);

    va_list(s);
    va_start(s,format);

    char rightbuffer[SIZE];
    vsnprintf(rightbuffer,sizeof(rightbuffer),format,s);
    va_end(s);

    char logtxt[SIZE*2];
    snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);

    //printf("%s\n",logtxt);
    printlog(level,logtxt);
    }
    void printlog(int level,const string& logtxt)
    {
        switch(printmethod)
        {
            case Screen:
            cout<<logtxt<<endl;
            break;
            case Onefile:
            printOnefile(Logfile,logtxt);
            break;
            case Classfile:
            printClassfile(level,logtxt);
            break;
        }
    }
    void printOnefile(const string& failname,const string& logtxt)
    {
        string _failname = path + failname;
        int fd=open(_failname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
        if(fd<0)
        {
            return;
        }
        write(fd,logtxt.c_str(),logtxt.size());
        close(fd);
    }
    void printClassfile(int level,const string& logtxt)
    {
        string _failname=Logfile;
        _failname += ".";
        _failname += levelToString(level);
        printOnefile(_failname,logtxt); 
    }
    ~Log()
    {}
private:
    int printmethod;
    string path;

};

//server.cc
#include <iostream>
#include "HttpServer.hpp"
using namespace std;
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        exit(1);
    }
    HttpServer* svr=new HttpServer(stoi(argv[1]));
    svr->start();
    return 0;
}

启动服务端

打开浏览器输入 ip:端口号

实现成功,点击第二张网页

xhell查看浏览器发送的请求

我们可以看到域名也发生了变化,其实就是跳转到特定的文件

⭐Cookie

为什么我们第一次访问b站要登陆账户,但是第二次就不需要再登陆了?

:因为浏览器帮我们记住账户了,账户信息存储在Cookie中,此外,服务器为我们分配了ID=,我们每次访问b站视频==,浏览器都会带着Cookie和ID让服务器确认账号信息

Cookie信息在浏览器左上角,如果我们删除Cookie就会让我们再次输入账户信息

客户端第一次向服务端发送账号和密码,服务器为客户端建立Cookie并分配ID,之后客户端每次访问服务端都带着Cookie和ID,服务端在数据库查找确认

在我们服务器代码加入Cookie报头

c 复制代码
 response_header+="Set-Cookie: name=haha&&passward=123456";
 response_header+="\r\n";

左上角打开我们的浏览器Cookie信息

相关推荐
创世宇图1 小时前
Alibaba Cloud Linux 安装生产环境-Tomcat
linux·tomcat
蜜獾云1 小时前
Elastic Stack基础概念
运维·jenkins
2501_945424801 小时前
高性能计算资源调度
开发语言·c++·算法
fakerth1 小时前
【Linux】调度器底层原理深入探索
linux·c++·操作系统
野犬寒鸦1 小时前
JVM垃圾回收机制深度解析(G1篇)(垃圾回收过程及专业名词详解)(补充)
java·服务器·开发语言·jvm·后端·面试
TTTrees2 小时前
C++学习笔记(33):智能指针(工厂函数)
c++
2301_776508722 小时前
C++中的组合模式变体
开发语言·c++·算法
历程里程碑2 小时前
44. TCP -23Linux聊天室实现命令符功能
java·linux·开发语言·数据结构·c++·排序算法·tcp
暮色千里.5322 小时前
多因素认证中的漏洞
网络·安全·web安全