个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创应用层协议HTTP
收录于专栏【计算机网络】
本专栏旨在分享学习计算机网络的一点学习笔记,欢迎大家在评论区交流讨论💌
目录
[1. HTTP 简单介绍](#1. HTTP 简单介绍)
[2. 认识 URL](#2. 认识 URL)
[urlencode 和 urldecode](#urlencode 和 urldecode)
[3. HTTP 协议请求与响应格式](#3. HTTP 协议请求与响应格式)
[HTTP 请求](#HTTP 请求)
[编辑 HTTP 响应](#编辑 HTTP 响应)
[编辑4. HTTP方法](#编辑4. HTTP方法)
[HTTP 常见方法](#HTTP 常见方法)
[GET 方法](#GET 方法)
[POST 方法](#POST 方法)
[PUT 方法](#PUT 方法)
[HEAD 方法](#HEAD 方法)
[DELETE 方法](#DELETE 方法)
[OPTIONS 方法](#OPTIONS 方法)
[5. HTTP 的状态码](#5. HTTP 的状态码)
[6. HTTP 常见 Header](#6. HTTP 常见 Header)
[关于 connection 报头](#关于 connection 报头)
[7. 实现最简单的 HTTP 服务器](#7. 实现最简单的 HTTP 服务器)
1. HTTP 简单介绍
虽然我们说, 应用层协议是我们程序猿自己定的, 但实际上, 已将有大佬定义了一些现成的, 有非常好用的应用层协议, 供我们直接参考使用, **HTTP (超文本传输协议)**就是其中之一.
在互联网世界中, HTTP (HyperText Transfer Protocol , 超文本传输协议) 是一个至关重要的协议, 它定义了客户端 (如浏览器) 与服务器之间如果通信, 以交换或传输超文本 (如 HTML文档)
HTTP 协议是客户端与服务器之间通信的基础, 客户端通过 HTTP 协议向服务器发送请求, 服务器收到请求后处理并返回响应, HTTP 协议是一个无连接, 无状态的协议, 及每次请求都需要建立连接, 且服务器不会保存客户端的信息状态.
2. 认识 URL
平时我们俗称的 "网址" 其实就是说的 "URL"

urlencode 和 urldecode
像 /? : 等这样的字符, 已经被 url 当做特殊意义理解了. 因此这些字符不能随意出现.
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
转义的规则如下 :
将需要转码的字符转为 : 16 进制, 然后从右到左, 取 4 位 (不足 4 位直接处理), 每 2 位做一位, 前面加上 %, 编码成 %XY 格式
例如 :

"+" 被转义成了 "%2B"
urldecode 就是 urlencode 的逆过程
3. HTTP 协议请求与响应格式
HTTP 请求
首行 : [方法] + [url] + [版本]
Header : 请求的属性, 冒号分割的键值对, 每组属性之间使用 \r\n 分隔, 遇到空行表示 Header 部分结束
**Body :**空行后面的内容都是 Body. Body 允许为空字符串. 如果 Body 存在, Header 中会有一个 Content-Lengths 属性来标识 Body 的长度
HTTP 响应
首行 :[版本号] + [状态码] + [状态码解释]
**Header :**请求的属性, 冒号分割的键值对, 每组属性之间使用 \r\n 分隔, 遇到空行表示 Header 部分结束
**Body :**空行后面的内容都是 Body. Body 允许为空字符串, 如果 Body 存在, 则在 Header 中会有一个 Content-Length 属性来标识 Body 的长度, 如果服务器返回了一个 html 页面, 那么 html 页面内容就是在 body 中.
4. 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 |
CONNECT | 要求用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINE | 断开连接关系 | 1.0 |
其中常用的就是 GET 方法和 POST 方法.
HTTP 常见方法
GET 方法
**用途 :**用于请求 URL 指定的资源
**示例 :**GET /index.html HTTP/1.1
**特性 :**指定资源服务器端解析后返回响应内容
**from表单 :**https://www.runoob.com.html/html-froms.html
POST 方法
**用途 :**用于传输实体的主体, 通常用于提交表单数据.
**示例 :**POST /submit.cgi HTTP/1.1
特性 : 可以发送大量的数据给服务器, 并且数据包含在请求中.
from表单 : https://www.runoob.com.html/html-froms.html
PUT 方法
用途 : 用于传输文件, 将请求文主体中的文件保存到请求 URL 指定的位置
**示例 :**PUT /example.html HTTP/1.1
**特性 :**不太常用, 但在某些情况下, 如 RESTful API 中, 用于更新资源.
HEAD 方法
**用途 :**与 GET 方法类似, 烦不烦会报文主体部分, 仅返回响应头
**示例 :**HEAD /index.html HTTP/1.1
**特性 :**用于确认 URL 的有效性以及资源更新的日期时间等
DELETE 方法
**用途 :**用于删除文件, 是 PUT 的相反方法
**示例 :**DELETE /example.html HTTP/1.1
**特性 :**按请求 URL 删除指定的资源
OPTIONS 方法
**用途 :**用于查询针对请求 URL 指定的资源支持的方法
**示例 :**OPTIONS * HTTP/1.1
特性 : 返回允许的方法, 如 GET, POST 等
5. HTTP 的状态码
类别 | 原因短语 | |
---|---|---|
1XX | Informational (信息性状态码) | 接受的请求正在处理 |
2XX | Success (成功状态码) | 请求正常处理完毕 |
3XX | Redirection (重定向状态码) | 需要进行附加操作加以完成请求 |
4XX | Cilent Error (客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error (服务器错误状态码) | 服务器处理请求出错 |
最常见状态码, 比如200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)
HTTP 状态码 301 (永久重定向) 和 302 (临时重定向) 都依赖 Location 选项, 以下是关于两者依赖 Location 选项的详细说明 :
HTTP 状态码 301 (永久重定向) :
当服务器返回 HTTP 301 状态码时, 表示请求的资源已经被永久移动到新的位置
这种情况下, 服务器会在响应中添加一个 Location 头部, 用于指定资源的新位置, 这个 Location 头部包含了新的 URL 地址, 浏览器会自动重定向到该地址.
例如, 在 HTTP 响应中, 可能会看到类似于以下的头部信息 :
cpp
HTTP/1.1 301 Moved Permanently\r\n
Location: https://www.new-url.com\r\n
HTTP 状态码 302 (临时重定向) :
当服务器返回 HTTP 302 状态码时, 表示请求的资源临时被移动到新的位置.
同样地, 服务器也会在响应中添加一个 Location 头部来指定资源的新位置, 浏览器会暂时使用新的 URL 进行后续的请求, 但不会缓存这个重定向.
例如 : 在 HTTP 响应中, 可能会看到类似于以下的头部信息 :
cpp
HTTP/1.1 302 Found\r\n
Location: https://www.new-url.com\r\n
**总结 :**无论是 HTTP 301 还是 HTTP 302 重定向, 都需呀依赖 Location 选项来指定资源的新位置. 这个 Location 选项是一个标准的 HTTP 响应头部, 用于告诉浏览器应该将请求重定向到哪个新的 URL 地址.
6. HTTP 常见 Header
**Content-Type :**数据类型 (text/html 等)
**Content-Length :**Body 的长度
**Host :**客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上
**User-Agent :**声明用户的操作系统和浏览器版本信息
**referer :**当前页面是从哪个页面跳转过来的
**Location :**搭配 3XX 状态码使用, 告诉客户端接下来要去哪里访问
**Cookie :**用于在客户端存储少量信息, 通常用于实现会话 (session) 的功能
关于 connection 报头
HTTP 中的 Connection 字段是 HTTP 报文头的一部分, 它主要用于控制和管理客户端与服务器之间的连接状态
核心作用
**管理持久连接 :**Connection 字段还用于管理持久连接 (也称为长连接). 持久连接允许客户端和服务器在请求/响应完成之后不立即关闭 TCP 连接, 以便在同一个连接上发送多个请求和接收多个响应.
持久连接 (长连接)
**HTTP/1.1 :**在HTTP/1.1 协议中, 默认使用持久连接, 当客户端和服务器都不明确指定关闭连接时, 连接将保持打开状态, 以便后续的请求和响应可以复用同一个连接
**HTTP/1.0 :**在 HTTP/1.0 协议中, 默认连接是非持久的. 如果希望在 HTTP/1.0 上实现持久连接, 需要在请求头中显示设置 Connection : keep-alive
语法格式 :
Connection : keep-alive : 表示希望保持连接以服用 TCP 连接
Connection : close : 表示请求/响应完成后, 应该关闭 TCP 连接
7. 实现最简单的 HTTP 服务器
实现一个最简单的 HTTP 服务器, 只在网页上输出 "hello world", 只要我们按照 HTTP 协议的要求构造数据, 就很容易做到
cpp
#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("0.0.0.0");
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;
}
