HTTP协议超详解

130 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天

网络基础之HTTP协议

一、什么是HTTP协议

超文本传输协议(英语:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议,是因特网上应用最为广泛的一种网络传输协议,所有的 WWW 文件都必须遵守这个标准。HTTP是一种请求/应答协议。客户的浏览器一般通过TCP的80号端口向web服务器发送对某一页面的请求信息,web服务器接收该请求,并给客户返回其指定的页面作应答。

二、认识URL

URL全称Uniform Resource Locator,即统一资源定位符

URL的完整格式由以下几部分组成

protocol://hostname[:port]/path[?query][#fragment]
  • protocol:网络传输协议,一般有http、https、file、ftp
  • hostname:主机域名,即IP地址
  • port:端口号,取值范围在0~65535
  • path:路由地址,一般用来表示主机上的一个目录或文件地址
  • query:查询字符串,从?到#,参数与参数之间用&分隔
  • fragment:片段标识符,是#后的name

urlencode和urldecode

URL编码,将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式

image-20230211214155997

如上图,“+”被转义成了“%2B”

urldecode就是urlencode的逆过程

三、HTTP工作原理

  1. 客户端连接到Web服务器 一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接
  2. 发送HTTP请求 通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成
  3. 服务器接受请求并返回HTTP响应 Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成
  4. 释放连接TCP连接 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求
  5. 客户端浏览器解析HTML内容 客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示

四、HTTP格式

HTTP请求格式

  • 首行: [方法] + [url] + [版本]
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个 Content-Length属性来标识Body的长度

image-20230211215047155

  • Host :请求的资源在哪个主机的端口上

  • Connection:该请求支持长连接

  • Content-Length:正文内容长度,及Body的长度

  • Content-Type:数据类型

  • User-Agent:声明用户的操作系统和浏览器版本信息,就是告诉服务端用什么发送的请求

  • Accept:发起了请求

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

  • Accept-Encoding:接受的编码

  • Accept-Language:接受的语言类型

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

    image-20230211215510115

HTTP响应格式

image-20230211220122093

五、HTTP方法

image-20230211220558723

其中,最常用的就是post和get方法

GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.

六、HTTP状态码

image-20230211220821015

最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

image-20230211221041542

上图就是post请求成功的例子

七、最简单的HTTP服务器

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Usage()
{
    printf("usage: ./server [ip] [port]\n");
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage();
        return 1;
    }
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        perror("socket");
        return 1;
    }
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    addr.sin_port = htons(atoi(argv[2]));
    int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0)
    {
        perror("bind");
        return 1;
    }
    ret = listen(fd, 10);
    if (ret < 0)
    {
        perror("listen");
        return 1;
    }
    for (;;)
    {
        struct sockaddr_in client_addr;
        socklen_t len;
        int client_fd = accept(fd, (struct sockaddr *)&client_addr, &len);
        if (client_fd < 0)
        {
            perror("accept");
            continue;
        }
        char input_buf[1024 * 10] = {0}; // 用一个足够大的缓冲区直接把数据读完.
        ssize_t read_size = read(client_fd, input_buf, sizeof(input_buf) - 1);
        if (read_size < 0)
        {
            return 1;
        }
        printf("[Request] %s", input_buf);
        char buf[1024] = {0};
        const char *hello = "<h1>hello world</h1>";
        sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello), hello);
        write(client_fd, buf, strlen(buf));
    }
    return 0;
}

编译后,在运行端输入

./test 0 8080 //一定要输入3个参数,最后一个为指定的端口号

image-20230211230023247

然后在网页中输入http://[ip]:[port] (自己的云服务器的公网IP及自己设置的端口号),就可以看到结果

image-20230211230149759