【Linux】http协议

文章目录

  • 前言
  • 1. 认识URL && 引入http协议
  • 2. http协议格式
    • 2.1 宏观格式:
    • 2.2 实验演示:
  • 3. http的方法
    • 3.1 GET方法:
    • 3.2 POST方法:
    • 3.3 GET vs POST:
  • 4. HTTP的报头和状态码
  • 5. http的cookie
    • 5.1 http协议的无状态:
    • 5.2 cookie的安全问题:
  • 6. http1.1长连接

前言

从本章开始,我们要自顶向下的学习网络协议,有了前面的基础,我们再学习http协议就更容易理解了。本章的目标是学习http协议,熟悉http的协议格式,方法,状态码等概念,并且对http常见的报头进行学习。目标已经确定,准备开讲啦… 【网络层状结构复习】


1. 认识URL && 引入http协议

平时我们俗称的 "网址" 其实就是说的 URL。

URL是(Uniform Resource Locator)的缩写,意为统一资源定位符:

【Linux】http协议_第1张图片
我们上一章节学习的 序列化与反序列化 所做的工作是在那一层呢?—— 应用层!

我们再来复盘一下之前的网络编程的过程:

  • 基本系统socket接口的使用
  • 指定协议
  • 编写业务

那么有没有已经非常成熟的场景,自定义的协议,这个协议写的不错,然后成了应用层特定协议的标准被别人直接使用呢?有的!!

比如说:http、https、smpt、ftp、DNS…

在解析完协议之后需要被反序列化,变成结构化数据。之后才形成完整的请求,最后才能交给业务被计算。

使用确定协议的时候,一般显示的时会缺省端口号:

  • 缺省的意思是在URL上,或者分享的链接上,不用带端口号。
  • 并不代表发送请求的时候是没有的,它是有的!而且是必须得有!
  • 任何一个浏览器或者手机app,收到链接点开的时候,浏览器或者app必须自动添加上端口号!
  • 只不过没有在URL中体现出来。

所以,浏览器访问指定的URL的时候,浏览器必须给我们自动添加port。

浏览器如何得知,URL匹配的port是谁呢?特定的众所周知服务,端口号必须是确定的!!

例如:httpserver->80httpsServer->443ssh->22

服务和端口是必须绑定的,用户自己写的网络服务bind端口的时候,只能绑定1024之后的端口[1024 ~ n]

域名和DNS:

  • 域名是指互联网上某个服务器的地址名称,用于标识和定位一个互联网上的资源。
  • 域名通常由多个部分组成,以点号(.)分隔,例如:baidu.com
  • 域名系统 (DNS) 可以将域名转换为IP地址以便计算机能够访问该服务器上的资源。
  • 使用域名比直接使用IP地址更方便易记,因此被广泛应用于互联网上的各种服务。

浏览器加一个服务,DNS服务自动帮我们做的域名解析。域名必须被转化成为IP访问网络服务,服务端必须具有port。网络通信的本质:socket。(ip + port)

URL编码问题:

  • urlencodeurldecode是用于对URL中的特殊字符进行编码和解码的函数。
  • urlencode函数将URL中的特殊字符转换为%后面跟着两位十六进制数的形式。
  • urldecode函数则进行相反的操作,它将编码过的URL字符串解码回原始的字符串形式。

在这里插入图片描述
发起http请求的时候,有些字段时需要编码的,注意:编码并不等于加密。一个%后面跟两个16进制数,代表的就是一个字符,汉字是三个字符代表一个汉字的,所以两个汉字是6个百分号。

信息在本地编码之后,交给服务器端,服务器再对编码进行解码。所以就需要encode和decode。编码是浏览器做的,而解码是服务端做的。

http是做什么的?

http超文本传输协议,为什么叫超文本的,因为网页是文本的,超在哪呢?超在可以传图片,视频音频。

查阅文档,看音视频都是以网页的形式呈现的,http获取网页资源的视频,音频等也都是文件。
【Linux】http协议_第2张图片

  • http是向特定的服务器申请特定的“资源"的,获取到本地,进行展示或者某种使用的。
  • 如果我们客户端没有获取的时候,资源在哪里呢?
    • 就在的网路服务器(软件)所在的服务器,硬件,计算机上。
  • 服务器都是什么系统呢?
    • Linux!
  • 这些资源都是文件,都在Linux服务器上,要打开资源文件、读取、发送会给客户端的前提:
    • 软件服务器,必先找到这个文件!
  • Linux要如何找到文件呢?
    • 通过路径!

【Linux】http协议_第3张图片

  • 网络视角下,Linux服务器上的所有的东西,我们都称之为资源。
  • 无论是图片视频音频还是网页,都叫做资源。
  • http就是从本地向服务端获取资源的,这就是http协议。

举个栗子:

【Linux】http协议_第4张图片

抓到了百度的网页,这个网页,就是通过百度对应的服务器,通过一些客户端,可以是浏览器或者是一些工具,把它的网页拿下来了,就是文件。

为什么URL可以定位唯一的资源:

  • 索要访问的某种资源,一定是在全球特定的主机上的,以特定的服务的方式,在特定的路径下保存着的。
  • 因为ip地址定位到了全球当中唯一的主机,端口号定位了主机上唯一的服务,所以ip + port定位了互联网中唯一的服务。
  • 路径一定是在该主机上的,所以路径本身就具备唯一性。
  • 所以就能使用URL定位在全球当中的任意一个资源。

2. http协议格式

2.1 宏观格式:

【Linux】http协议_第5张图片
这么多行,主体是很多http请求的报头属性,请求报头的报头属性非常多,所有的字段都是Key:空格value

http是一个基于行的一个协议:

  • 报头最后一行是一个空行,能够保证,前面的内容不会是空行,而且必须有字段。
  • 按照http协议在读取的时候,可以按行去读,直到读到空行,就能证明将报头和请求行读完了。
  • 相当于用一个空行来作为一个分隔符,让自己的报头能够全被解析出去。
  • 任何一个协议都要解决一个问题, 将自己的有效载荷和自己的报头分离的问题。

任何协议的request or response都是:报头 + 有效载荷。

  • http如何保证自己的报头和有效载荷被全部读取呢?(无论是请求还是响应)
    • 读取完整报头:按行读取,直到读取到空行!
  • 你又如何保证,你能读取到完整的正文呢?
    • 报头能读取完毕,请求或者响应属性中,一定要包含正文的长度!

当对http有了宏观结构上的认识之后,也有一些不规范的写法,http请求当中可能会少一些报头,可能会没有正文,但是都必须有请求行和空行。空行存在的最大意义就是将报头和有效载荷分离。

ip具有唯一性,端口在该服务器上也具有唯一性,那么路径就更具有唯一性了。诸多唯一性构建起来就构成了一个URL。

2.2 实验演示:

有了之前的套接字编程经验,我们直接写一个简易版的服务端(TCP套接字):

class ServerTcp
{
public:
    ServerTcp(uint16_t port, const std::string &ip = "")
        : port_(port),
          ip_(ip),
          listenSock_(-1)
    {
        quit_ = false;
    }
    ~ServerTcp()
    {
        if (listenSock_ >= 0)
            close(listenSock_);
    }

public:
    void init()
    {
        // 1. 创建socket
        listenSock_ = socket(PF_INET, SOCK_STREAM, 0);
        if (listenSock_ < 0)
        {
            exit(1);
        }

        // 2. bind绑定
        // 2.1 填充服务器信息
        struct sockaddr_in local; // 用户栈
        memset(&local, 0, sizeof local);
        local.sin_family = PF_INET;
        local.sin_port = htons(port_);
        ip_.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(ip_.c_str(), &local.sin_addr));

        // 2.2 本地socket信息,写入sock_对应的内核区域
        if (bind(listenSock_, (const struct sockaddr *)&local, sizeof local) < 0)
        {
            exit(2);
        }

        // 3. 监听socket,为何要监听呢?tcp是面向连接的!
        if (listen(listenSock_, 5 /*后面再说*/) < 0)
        {
            exit(3);
        }
        // 运行别人来连接你了
    }

    void loop()
    {
        signal(SIGCHLD, SIG_IGN); // only Linux
        while (!quit_)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);

            int serviceSock = accept(listenSock_, (struct sockaddr *)&peer, &len);
            if (quit_)
                break;

            if (serviceSock < 0)
            {
                // 获取链接失败
                cerr << "accept error ...." << endl;
                continue;
            }

            // 多进程版本
            pid_t id = fork();
            assert(id != -1);
            if (id == 0)
            {
                close(listenSock_); // 建议
                if (fork() > 0)
                    exit(0);
                // 孙子进程
                handlerHttpRequest(serviceSock);
                exit(0); // 进入僵尸
            }
            close(serviceSock);
            wait(nullptr);
        }
    }
private:
    // sock
    int listenSock_;
    // port
    uint16_t port_;
    // ip
    std::string ip_;
    // 安全退出
    bool quit_;
};

不清楚的小伙伴看过来 TCP套接字复习。
我们用的是多进程版本的服务器。

提前搞一些宏标识:

#define CRLF "\r\n"
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define HOME_PAGE "index.html"
#define ROOT_PATH "wwwroot"

服务端响应:

void handlerHttpRequest(int sock)
{
    char buffer[1024];
    ssize_t s = read(sock, buffer, sizeof buffer);
    if (s > 0)
        cout << buffer;
        
    std::string path = getPath(buffer);
    // path = "/a/b/index.html";
    // recource = "./wwwroot"; // 我们的web根目录
    // recource += path; // 最终拼出来了--> ./wwwroot/a/b/index.html

    // 1. 文件在哪里? 在请求的请求行中,第二个字段就是你要访问的文件
    // 2. 如何读取
    std::string recource = ROOT_PATH;
    recource += path;
    cout << "[recoure]: " << recource << std::endl;

    std::string html = readFile(recource);
    std::size_t pos = recource.rfind(".");
    std::string suffix = recource.substr(pos);
    cout << "[suffix]: " <<suffix << endl;

    // 开始响应
    std::string response;
    // 两百这个状态码代表这次请求时OK的
    response = "HTTP/1.0 200 OK\r\n";
    if (suffix == ".jpg")
        response += "Content-Type: image/jpeg\r\n"; // Content-type标定了正文的类型是什么
    else
        response += "Content-Type: text/html\r\n";
    response += ("Content-Length: " + std::to_string(html.size()) + "\r\n");
    response += "\r\n"; // 空行
    response += html; // 正文

    send(sock, response.c_str(), response.size(), 0);
}

首先我们读取收到的序列化数据,提取要获取的数据的路径,根据路径在服务器上找对应的文件,然后将文件提取出来。
随后开始响应,填写响应报头,根据文件的格式填好每个报头字段,将提取到的文件拼接到最后,然后发送给客户端。

Content-type标定了正文的类型是什么,要根据不同的格式来填Content-type。 常用对照表

获取路径函数:

std::string getPath(std::string http_request)
{
    std::size_t pos = http_request.find(CRLF);
    if (pos == std::string::npos)
        return "";

    // 请求行
    std::string request_line = http_request.substr(0, pos);

    // GET /a/b/c http/1.1
    std::size_t first = request_line.find(SPACE);
    if (first == std::string::npos)
        return "";
    std::size_t second = request_line.rfind(SPACE);
    if (second == std::string::npos)
        return "";

    std::string path = request_line.substr(first + SPACE_LEN, second - (first + SPACE_LEN));

    if (path.size() == 1 && path[0] == '/')
        path += HOME_PAGE;

    return path;
}

注意:

  • 在http请求中默认访问的文件路径是根目录,也称为网站的根目录或文档根目录。
  • 但是这个根目录并不是服务器的根目录,难不成将服务器所有内容返回给客户端吗,不现实!

读取文件函数:

std::string readFile(const std::string &recource)
{
    std::ifstream in(recource, std::ifstream::binary);
    // 检测文件是否成功打开了
    if (!in.is_open())
        return "404";
        
    std::string content;
    std::string line;
    while (std::getline(in, line))
        content += line;
    in.close();
    return content;
}

资源文件:

DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>测试title>
head>

<body>
    <h3>hello my server!h3>
    <p>我终于测试完了我的代码p>
    <form action="/a/b/c.html" method="post">
        Username: <input type="text" name="user"><br>
        Password: <input type="password" name="passwd"><br>
        <input type="submit" value="Submit">
    form>
body>

html>

当然了,我们也不懂前端,随便写一点样例。

主函数:

#include "server.hpp"

static void Usage(std::string proc)
{
    std::cerr << "Usage:\n\t" << proc << " port" << std::endl;
    std::cerr << "example:\n\t" << proc << " 8080\n"
              << std::endl;
}

// ./ServerTcp local_port local_ip
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    uint16_t port = atoi(argv[1]);

    ServerTcp svr(port);
    svr.init();
    svr.loop();
    return 0;
}

用浏览器获取响应:

【Linux】http协议_第6张图片
用tenlnet获取响应:

【Linux】http协议_第7张图片
telnet是个命令,可以远程的以协议的方式,去登录某种服务:
【Linux】http协议_第8张图片
一般我们浏览器中请求的服务,是一定有人曾经写过这样的服务,部署在Linux上,然后才可以请求。


3. http的方法

方法 描述
GET 请求指定的资源。返回响应主体
POST 向指定资源提交数据进行处理请求。通常用于提交表单或上传文件
PUT 从客户端向服务器传送的数据取代指定的文档的内容
DELETE 请求服务器删除指定的资源
HEAD 获取对应的HTTP报头信息,但不返回响应主体
OPTIONS 返回服务器支持的HTTP方法列表
PATCH 对资源进行部分修改

http的请求方法除了GET和POST还有其他方法,但是用的特别少,而且在大部分的服务器当中,基本上很多的方法被注释掉了,被禁掉了。

input标签,会在网页当中给我们构建一个输入框。

URL可以在?后面带参数,参数是KV的,K对应的就是表单当中的name,V就是往表单当中输入的东西。

【Linux】http协议_第9张图片
我们的网络行为无非有两种:

  1. 我想把远端的资源拿到你的本地:GET /index.html http/1.1
  2. 我们想把本地属性字段,提交到远端:GET or POST

3.1 GET方法:

在HTTP中GET会以明文方式将我们对应的参数信息,拼接到URL中,以这样的方式完成了提参的过程。
【Linux】http协议_第10张图片
【Linux】http协议_第11张图片
【Linux】http协议_第12张图片

HTTP GET方法通常不包含请求正文。HTTP GET方法用于从服务器请求资源,通常通过在URL中指定参数来传递请求参数,而不是在请求正文中传递参数。GET方法将请求参数附加到URL的末尾,形成查询字符串,并将其发送到服务器。

3.2 POST方法:

POST方法提交参数,会将参数以明文的方式,拼接到http的正文中来进行提交!

【Linux】http协议_第13张图片
【Linux】http协议_第14张图片
POST方法,请求正文,一般携带的是http请求所带的参数。这个参数类似于C语言C++的字符串。
在浏览器中提交表单的时候,表单的内容以KV的方式拼接到正文了。

3.3 GET vs POST:

小结:

  1. GET通过URL传参
  2. POST通过正文传参
  3. GET方法传参不私密
  4. POST方法因为通过正文传参,所以相对比较私密一些
  5. GET通过URL传参,POST通过正文传参,所以一般一些比较大的内容都是通过POST方式传参的

http传输数据能有功能上的满足,但是数据却在网络当中裸奔。所以,这个数据无论如何都是不安全的。

如何选择:

  • URL没有类型字段,而是在请求和响应报文的头部中使用Content-Type字段来指定数据类型。
  • 所以就决定了,正文部分可以传递更丰富的字段类型。
  • URL要是传一些二进制内容,而且量太大了,挺不合适的。
  • 所以如果我们平时传一些文件,对私密性有要求,并且体积比较大的,我们一般就用POST。

比如网页端上传一些简历,上传一些视频,或者是上传百度云盘的资料,我们都用的是POST来传,因为POST有详细的字段类型说明。


4. HTTP的报头和状态码

http常见的Header:

Header 描述
Content-Type 数据类型,指示服务器返回的正文部分的数据类型
Content-Length 有效载荷的长度
Host 客户端告知服务器所请求的资源在哪个主机的哪个端口
User-Agent 声明用户的操作系统和浏览器版本信息
Referer 当前页面是从哪个页面跳转过来的
Location 搭配3xx状态码使用,告诉客户端接下来要去哪里访问
Cookie 在客户端存储少量信息,通常用于实现会话功能

Linux网页的跳转本质是Linux目录的跳转,referer代表上一个目录是什么。

http这样的协议几乎是基于纯文本的,而且可展性还非常强,如果未来http想要新功能,新属性,直接在请求和响应报头里添加KV字符串行就可以。

状态码:

状态码范围 类别 描述
1XX 信息性状态码 服务器已接受请求,需要客户端继续操作
2XX 成功状态码 请求已成功处理
3XX 重定向状态码 需要完成进一步的操作以完成请求
4XX 客户端错误状态码 请求包含语法错误或无法完成请求
5XX 服务器错误状态码 服务器在处理请求时发生内部错误或超时等

目前被主流浏览器所接受的重定向是301和302。

301和302通常是做什么的呢?

  • 301:永久重定向
  • 302: 临时重定向

临时重定向:

  • 假设要访问某个网站,但是这个网站局部性的有内容要升级,并不想让用户访问这个服务。
  • 当有用户来访问时,我们就将其重定向到备份的服务当中,当内容升级完了,再将临时重定向去掉。
  • 客户端再来访问时,就可以访问到了。

永久重定向:

  • 老网站的用户数量非常的多但是想要用户去访问新的网站。
  • 所以将新网站推出,让别人能够访问,其次老网站不关。
  • 将所有的服务全部下线,并且只提供一个功能,在用户请求它时重定向就可以了。

【Linux】http协议_第15张图片
重定向代码:

void handlerHttpRequest(int sock)
{
    char buffer[1024];
    ssize_t s = read(sock, buffer, sizeof buffer);
    if (s > 0)
        cout << buffer;
        
    std::string response = "HTTP/1.1 302 Temporarily Moved\r\n";
    // std::string response = "HTTP/1.1 301 Permanently Moved\r\n";

    // Location后面填的一定是个网址
    response += "Location: https://www.qq.com/\r\n"; 
    response += "\r\n";
    send(sock, response.c_str(), response.size(), 0);
}

在响应时状态码填的是301或者302,代表的意思是重定向,给客户端响应,还会给一个新的网址。

因为我们是服务端将从客户端收到的报文打印出来,所以当客户端请求时,会将请求报文打印出来,然后再将带有location的报文发给客户端。
但是我们是打印不出来带有location字段的报文的,因为客户端重定向到了别的服务器了。

浏览器演示图:

【Linux】http协议_第16张图片

telnet演示图:

【Linux】http协议_第17张图片

通过Telnet可以获取远端服务器的响应。当你使用Telnet连接到远程服务器后,你可以输入命令并发送给服务器,然后等待服务器的响应。服务器会返回相应的结果,包括命令执行的输出、状态信息等。

小结:

  • 永久和临时的区别,它俩的应用场景是不一样的,对客户端的影响也是不一样的。
  • 也有其他的重定向例如307,因为其他重定向对应的浏览器的支持并不好,不一定被所有的浏览器所接受。
  • 但是301和302是被所有浏览器接受的。

5. http的cookie

5.1 http协议的无状态:

http协议特点之一:无状态

是什么意思呢?

—— 用户的各种资源请求行为,http本身并不做任何记录!!

HTTP的无状态(Stateless)指的是:

  • 每个HTTP请求都是独立的,服务器不会保存与之前请求相关的任何信息。
  • 每个请求都包含足够的信息以便服务器能够理解和处理该请求,而无需依赖于之前的请求。

但是用户需要会话保持!!

HTTP的Cookie(也称为HTTP Cookie、Web Cookie或浏览器Cookie)

是一种用于网站与浏览器之间进行状态管理的技术。它通过在用户浏览器中存储少量数据,并在以后的请求中发送给同一网站,实现对用户状态的追踪和识别。

  • 当用户第一次访问一个网站时,网站服务器可以通过HTTP响应报头中的Set-Cookie字段将一个Cookie发送给用户。
  • 浏览器会将这个Cookie存储起来,并在用户以后对同一网站发送请求时,通过HTTP请求头中的Cookie字段将这个Cookie发送回服务器。

编写响应报文:

【Linux】http协议_第18张图片
获取服务器的响应:

【Linux】http协议_第19张图片
第二次以后的请求会携带一个字段叫做cookie,就代表我们曾经写的内容:

【Linux】http协议_第20张图片

  • 当写入一个cookie之后,在进行后续的访问时,浏览器请求,都会帮我们携带曾经写入到浏览器对应的cookie信息。
  • cookie文件内容,所以后端认证时,就不需要再输入用户名和密码了,就可以始终以登录状态,来进行访问。
  • 通过使用Cookie,服务端可以保持跟踪客户端的会话信息、用户认证状态等。同时,客户端也可以根据需要修改和删除Cookie值。

cookie就是浏览器帮我们维护的一个文件:

  • 浏览器维护的文件,存在磁盘或者内存级。
  • 只要不关闭这个浏览器,cookie文件依旧在,但是如果把浏览器结束掉,那么cookie文件就没了。
  • 不同的浏览器和不同的设备(如电脑、手机等)可能具有不同的Cookie策略和存储方式。

在浏览器中查看Cookie:

【Linux】http协议_第21张图片

5.2 cookie的安全问题:

当第一次客户端请求服务端的时候,将用户账号和密码发给服务端,服务端完成匹配之后,将携带用户账号和密码的Cookie文件返回给客户端,但是这是极其不安全的!

一旦有中间人在服务端返回给客户端的时候,将返回内容截取,那么用户的账号和密码就会泄露。

现在主流的是:cookie + session

服务端在收到客户端请求时,在内部验证信息,一旦验证通过,不再给客户端返回携带用户账号和密码的Cookie文件,而是在服务端本地形成一个session文件!

  • 用户的临时私密信息,保存在自动形成的session文件中。
    • session_id是具有唯一性的,是一个用于标识和跟踪用户会话状态的唯一标识符。
  • 用户的所有信息,此时就不在客户端cookie维护了,而是在服务端将用户所有私有信息维护起来。
  • 实际上,用户的个人信息通常存储在服务器端的一个数据结构(如内存、数据库)中。
    • 服务器会根据Session ID来查找相应的会话数据,从而获取用户的个人信息。

session里包含了文件的私密信息,包括了这个用户是谁,浏览痕迹是什么,最近一次访问时间是什么时候session有没有过期等等。只要拿着session_id找到session文件,就证明用户处于登录状态,然后服务器就允许该用户去访问指定资源。

依旧存在安全问题:

  • 从此往后,客户再去访问时,都会携带自己的session_id,从而来保持这个客户处于在线状态。
  • 但是还是没有彻底解决相应问题,这样只解决了私密信息不会泄露了。
  • 因为用户名和密码在服务端,黑客只能拿到session_id,对于用户来讲,账号至少是安全的。

6. http1.1长连接

用户所看到的完整的网页内容,背后可能是无数次http清求!http底层主流采用的就是tcp协议!

长连接(Connection: keep-alive):

  • 一次http请求,获取一张给用户呈现的完整网页时,http建立一 个连接。
  • 此时服务端和客户端双方,都要进行http版本协商,所以这二者都要携带上自己http协议的版本。
  • 请求时是http1.1,响应时也是http1.1,同时还携带了一个Connection字段,叫做keep-alive
  • 双方如果都是http1.1,并且字段都携带keep-alive,意味着我们在进行底层连接协商的时候,双方都同意采用长连接方案。

http1.0当中是基于短连接的,短连接一次只处理一个http请求!Connection: closed

对于长连接的理解:

一张网页有若干个元素构成,我们在底层发起请求,这个连接暂时不断开,发起若干次http请求,同时把若干次http请求全部都发送到服务器,服务器从一个连接里读到的,不仅仅是一个请求,它可以读到很多请求,然后按照顺序,再把若干个响应全部都返回给客户端,当客户端拿到了完整的网页时,这个连接才断开。
也就是说一次获取若干个元素时,这些若干个元素用一条连接全部请求和响应,不用重复的建立TCP底层的三次握手,四次挥手。这样就能大大的提高效率。

其中双方支持长连接协商的时候,Connection如果是keep-alive,客户端发的请求里带了,服务端的响应里带了,就说明双发协商成功,我们双方都认可采用keep-alive,也叫做连接保活的策略。

因为http1.1有了keep-alive,为了区分http1.0,所以原来的Connection如果是close,就叫做只支持短连接。

一个连接有多个请求了,如何保证服务端读取请求时,都是一个完整的请求呢?

  • 根据报文中的Content-length来读取。
  • 能保证读到的是一个完整的请求,也能保证不会读到别的请求。
  • 经过报头和有效载荷合理分离,就能做到将报文和报文之间做到完整分离。
  • 那么多个http请求能够向服务端同时发起多个请求。

http是基于tcp的,tcp协议是面向连接的,但是http是无连接的,怎么解释?(重点)

  • http协议和tcp协议是两层协议,是毫无相关的,tcp的面向连接和http无关。
  • http是无连接的,只是用了一下tcp的能力,把信道建立好,用tcp来完成http的工作,比如说发起http请求。
  • 但是http在通信时,并不会建立连接。

所以,http最核心的特点是超文本传输协议,是一个无连接,无状态的一个应用层协议,这就是http的定义。

你可能感兴趣的:(Linux,linux,http)