HTTP协议

本章Gitee地址:http

1. 域名和url

在浏览器的url输入框当中,我们输入类似https://www.4399.com/这样的域名,就能够直接访问到这个客户端。

从技术上我们客户端要访问服务端,是直接通过ip:port直接访问的,但是日常中,并不会采用这种方式,而是采用上面域名。

为什么不拿ip:port直接访问呢?

因为这样用户体验不好,安全性不高。比如说给我们一个ip地址,我们并不知道它只指哪儿,擅自点击可能会出问题,而使用域名,我们一看到就能知道它是谁的官网,例如www.baidu.comwww.aliyun.con

不过这个域名,是会被域名解析这个服务解析成ip地址的,所以最终访问,也是通过ip地址进行访问的。

image-20240222141130974

访问是通过ip:port,但这里并没端口号,http://45.113.192.102/这里会在前面默认加上http或者https这个前缀,这个前缀就是httphttps协议,这种知名的服务,它们的端口号一般是固定的:

  • http端口一般是80
  • https端口一般是443

而我们平时说说的网址,例如https://blog.csdn.net/Dirty_artist这种叫做url,域名表示互联网中唯一一台主机,然后协议+默认端口号就能找到唯一的服务。

image-20240222144636104

这里如果搜索的内容,与它们特定的分隔符冲突了,此时要求BS(Browser/Server)双方要进行编码encode和解码decode

image-20240222144839348

这里会将冲突的字符转成对应的ASCII码表的16进制,然后从右向左取4位,每2位做一位,前面加上%,编码成%XY格式。

例如:假设冲突字符的ASCII码是65,其十六进制表示是41,相应的二进制表示是01000001。我们从右向左取出四位,得到01000001。然后,将每两位二进制数转换为对应的十六进制数,得到41,最后将每个新的十六进制数前面加上%符号,得到编码后的格式为%41

image-20240222145946283

2. http请求和响应

http的请求和响应是以行为分式

http request

  • 请求行,空格为分隔符:

    1. Method(请求方法):大部分都是GET或者POST
    2. URL:需要请求的资源
    3. HTTP Version(请求协议版本):1.0、1.1(常见)、2.0
  • 请求报头,这里由多行内容构成,每一行都是http的请求属性,大多数都是Key:Value式的

  • 空行:这里也涉及到需要之后将报文和报头分开,http协议规定在报头和正文中间,用空行为分隔符

  • 请求正文:正文部分需要读多少内容,报头里面有一个Content-Length:xxx,表明需要在请求正文读多少内容

image-20240222160151319

http response

它的格式和request格式几乎一样

image-20240222162902181

3. http请求方法

方法说明支持HTTP协议版本
GET获取远端资源1.0、1.1
POST可以获取网页,但更多是用来上传资源1.0、1.1
PUT传文件1.0、1.1
HEAD获取报头1.0、1.1
DELETE删除文件1.0、1.1
OPTIONS询问支持的方法1.1
TRACE追踪路径1.1
CONNECTfiddler的原理类似1.1

其中GET方法用的是最多的,其次就是POST,其他的方法要么是过期被淘汰,要么人家web服务器把其他方法关闭,不让用。

我们想服务器提交数据,就是采用的POST方法,例如我们在搜索引擎检索,这个在前端上叫做表单

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <form action="1/2/ikun.html" method="get">
        username: <input type="text" name="name"><br>
        password: <input type="password" name="passwd"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

image-20240224233135788

这里发现,如果提供get方法提交的时候,参数是通过url提交的

get方法改为post方法

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <form action="1/2/ikun.html" method="post">
        username: <input type="text" name="name"><br>
        password: <input type="password" name="passwd"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

image-20240224235540211

这里发现post方法也支持参数提交,但是它采用的是请求中文提交参数

如果form表单不指定,则默认采用get方法:

  • get方法通过url提交,参数数量是受限制的;而且get方法提参的时候,会将参数回显,不私密
  • post方法采用正文提交,不会回显,相对私密,但是内容也会被抓包抓到

如果要数据安全,就得做加密——https

4. http状态码

状态码类别含义
1XXInformation(信息状态码)收到的请求在处理
2XXSuccess(成功状态码)请求正常处理完毕
3XXRedirection(重定向状态码)需要进行附加操作完成请求
4XXClient Error(客户端错位状态码)服务器无法处理请求
5XXServer Error(服务器错位状态码)服务器处理请求错误

常见状态码:

  • 200:OK
  • 404:Not Found
  • 403:Forbidden
  • 302:Redirect
  • 504:Bad Getaway

5. http常见Header

  • Content-Type:数据类型

    要读取的是文件类型:HTTP Content-Type对照表

  • Content-Length:正文部分的长度

  • Host:要请求的主机

  • User-Agent:客户端的身份

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

  • location:搭配3XX状态码使用,告诉客户端去哪里

    image-20240229121948306

  • Cookie

    原理:

    有的网站第一次访问需要注册登录,通过post方法将账号密码提交给目标主机,然后主机做认证认,之后可能就重定向到首页,此时服务器返回的报头当中就含有Set-Cookie这个字段,里面就包含了账号密码,然后浏览器就回将这个保存起来,这就是cookie,之后访问这个网站的时候,每一次请求都会包含cookie的文件内容。

    保存cookie文件有2种保存方法:文件级、内存级

    这样可能有一个风险就是,如果一个黑客将我们的cookie文件扫码走了,那么就可以和我们访问一样的资源,也就是信息泄露。

    有一种解决办法就是服务端提供一个session id,这个在服务器是唯一的,然后给浏览器返回这个session id,它是有期限的,这样本来有浏览器自己管理的cookie交给了服务端。

  • Connection:选择长连接或短连接

    一个页面包含了许多的元素,这里的一个元素就代表着一个资源,如果发起一个请求响应一次资源,然后关闭连接,这就是短连接;如果建立一个tcp连接,发起多个请求返回多次资源,这就是长连接。

    在一起网页并没有那么多的资源,所以大多数都是短连接http/1.0,而现在网页的资源越来越多,采用短连接的方式效率就十分低下,所以要采用长连接http/1.1,请求全部完毕之后再关闭。

    但是可能存在版本不同的情况,所以这时候就要协商是采用哪种连接方式。

    image-20240229123213038

6. 简单httpserver

http协议是应用层,应用层底层协议可以选择tcpudp,而http底层协议是tcp协议

#pragma once

#include<iostream>
#include<pthread.h>
#include<unordered_map>
#include"Log.hpp"
#include"Socket.hpp"
#include"ReadHtml.hpp"
#include"Protocol.hpp"

static const int defaultport = 8899;
class HttpServer;

class ThreadData
{
public:
    ThreadData(int fd, HttpServer *s)
    :sockfd(fd), svr(s)
    {}

public:
    int sockfd;
    HttpServer *svr;
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport)
    :port_(port)
    {
        content_type_.insert({".html", "text/html"});
        content_type_.insert({".png", "image/png"});
        content_type_.insert({".mp4", "video/mp4"});
    }

    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 << std::endl;
            Request req;
            //收到的内容反序列化
            req.Deserialize(buffer);
            req.Parse();
            //req.PrintDebug();

            //返回响应
            std::string text = Html().ReadContent(req.file_path_);    //模型响应正文
            bool isOk = true;
            if(text.empty())
            {
                //无法处理请求
                isOk = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = Html().ReadContent(err_html);
            }

            std::string response_line;
            if(isOk)
            {
                response_line = "HTTP/1.0 200 OK\r\n";  //模拟状态行
            }
            else
            {
                response_line = "HTTP/1.0 404 Not Found\r\n";
            }

            //重定向
            //response_line = "HTTP/1.1 302 Found\r\n";


            //模拟报头
            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size());
            response_header += "\r\n";
            response_header += "Content-Type: ";
            response_header += Html().SuffixToDesc(content_type_, req.suffix_);
            response_header += "\r\n";
            response_header += "Set-Cookie: name=Pyh";
            response_header += "\r\n";
            //response_header += "Location: https://gitee.com\r\n";
            std::string blank_line = "\r\n";

            //完整回复
            std::string response = response_line;
            response += response_header;
            response += blank_line;
            response += text;

            //发送信息
            send(sockfd, response.c_str(), response.size(), 0);

        }
        //防止挂太多链接,处理一次链接直接关掉
        close(sockfd);
    }

    //内类有this指针,线程执行函数返回值为void*,所以设置为static
    static void *ThreadRun(void *args)
    {
        // 线程分离
        pthread_detach(pthread_self());

        ThreadData *td = static_cast<ThreadData*>(args);
        td->svr->HandlerHttp(td->sockfd);
        delete td;
        return nullptr;
    }
    
    bool Start()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        while(true)
        {
            std::string clientip;
            uint16_t clientport;

            int sockfd = listensock_.Accept(&clientip, &clientport);
            if(sockfd < 0)  continue;
            log(Info, "get a new link, sockfd: %d", sockfd);
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }

    }
    ~HttpServer()
    {}

private:
    MySocket listensock_;
    uint16_t port_;
    std::unordered_map<std::string, std::string> content_type_;
};

关于访问:

当使用浏览器进行访问一个web站点的时候,浏览器会默认加上/,这个表示访问web根目录 。以如果在访问的时候,加上路径,就可以访问到我们服务器上的特定文件。

这里的根目录,并不是指的linux的根目录,是**web根目录**!!!

但是有可能根目录里面有很多内容,但是我们并不能全部给人家,所以一般情况下的/表面访问的是网站的首页内容index.html

wget的底层就是http协议,它能获取网页的内容,例如:

image-20240224204401584

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加法器+

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值