【网络编程】深入 HTTP:从报文交互到服务构建,洞悉核心机制

作者:半桔日期:2025/10/3

请添加图片描述

半桔个人主页

🔥 个人专栏: 《Linux手册》《手撕面试算法》《网络编程》

🔖有些鸟儿注定是不会被关在牢笼里的,因为它们的每一片羽毛都闪耀着自由的光辉。


文章目录

  • 一. 前言
  • 二. 基础知识
    • 2.1 URL
  • 三. 请求报文
  • 四. 响应报文
  • 五. 实现HTTP服务器
  • 六. HTTP的细节字段
    • 6.1 请求方法
    • 6.2 状态码
    • 6.3 常见的报头
    • 6.4 Cookie和Session ID

一. 前言

在如今的数字时代,我们每天打开浏览器浏览新闻、刷社交媒体、在线购物、观看视频时,背后都有一套默默运转的 “通信规则” 在支撑 —— 这就是超文本传输协议(HyperText Transfer Protocol,简称 HTTP)。作为 TCP/IP 协议栈中应用层的核心协议之一,HTTP 如同互联网世界的 “信使”,负责在客户端(如电脑、手机浏览器)与服务器之间传递数据,让文本、图片、音频、视频等各类资源得以跨越网络顺畅流转。

然而,对于多数使用者而言,HTTP 协议更像是 “隐形的基础设施”,其运作原理、核心特性与演进逻辑往往被忽略。本文将从 HTTP 协议的基本概念入手,深入剖析其工作机制。

本文将分为4大板块:

  1. 基础知识;
  2. http请求和响应的报文结构;
  3. 实现http服务器;
  4. http的细节字段。

二. 基础知识

2.1 URL

在构建http服务器之前我们需要学习一些关于http的基础知识。

URL:在网络上的所有资源都可以使用一个唯一的“字符串”标识,该字符串被称为URL。

以下是完全符合标准格式的 URL,以及对应的各个字段标识的含义:

请添加图片描述

组成部分示例内容作用
协议方案http指定通信协议(如 http、https、ftp 等,决定客户端如何与服务器交互)
认证信息user:pass@(可选)早期用于 HTTP Basic 认证,现在少用(安全风险高)
主机名www.example.jp服务器的域名(或 IP 地址),定位网络中的服务器
端口号:80(可选)指定服务器监听的端口,http 默认 80、https 默认 443,可省略
路径/dir/index.htm服务器上资源的具体位置(类似文件系统路径)
查询参数?uid=1(可选)键值对形式,向服务器传递额外参数(如筛选、分页)
片段(锚点)#ch1(可选)浏览器内部使用,定位页面内的锚点(不传给服务器)

根据上面的表格,一个最简单的URL至少包含三个部分:协议方案 + 主机名(IP号) + 路径。

因为IP地址是一串数字难以记忆,所以一般采用域名来替代IP号,像bandu.com,bilibili.com都是域名,通过域名能够解析出IP地址,让用户更容易记忆。

http://hcl.baidu.com/:这就是一个最简单的URL,其中http是协议方案,hcl.bandu.com是域名标识IP地址,而最后的/类似于根目录标识路径。

可以看到上面的URL中各个部分要进行划分,采用了不同的分隔符,像\?等;

我们可以在URL上带上自己要查询的参数,其中内容可以自定义,那当内容中含有URL的分隔符的时候,URL会不会解析失败???

此时就需要服务端和客户端对信息进行编码和解码

  • 如果用户或客户端输入的内容中包含了URL的特殊分割符,就需要对内容进行编码和解码,也就是对特殊字符进行转义

转义原则:将需要转码的字符转为 16 进制,然后从右到左,取 4 位(不足 4 位直接处理),每2 位做一位,前面加上%,编码成%XY格式。比如对于?会被转义为%3F.

三. 请求报文

Http协议已经定制好了,学习Http协议是非常有必要的,对于我们后续使用http协议,以及进行抓包都有很大帮助。

此处我们先学习一下http的请求报文的组成。

一下是请求的报文结构:
请添加图片描述

首先请求行分为3个部分:

  1. 请求方法:参加的就是Get获取资源,POST上传资源,关于其他请求在文章结尾会详细介绍;
  2. URL:用来标识请求的资源位置;
  3. HTTP版本:保证双方使用统一协议进行交互。

其中每一行通过\n\r作为换行符进行分割。

  • 请求报头:请求报头中存放的是key-value形式的数据,内部有各种不同的字段来标识请求者,报文的各种信息,我们也会在结尾处详细介绍;
  • 请求正文:请求正文就是我们先服务区发送的请求字段信息。

在报头和正文中间,用一个空行的进行分割,来报文能够区分一个http报头的结尾位置。
在报头中存放了请求正文的行数或正文的大小,像Content-Length字段就表示请求正文的字节数,来保证服务端也能知道请求正文的结束位置。请添加图片描述

请添加图片描述

上面这是一个普通的请求报文的内容。

  • 其中的请求方法是Get,标识该请求是用来获取资源的,在HTTP中Get方法不允许携带请求正文,因此我们并没有在报头中看到关于描述正文大小的字段;另一个POST的方法中确实有Content-Length字段。
  • 在请求报文的第二个参数URL有一点问题,为什么只有一个路径,既没有协议也没有IP地址。

第二个URL这么写,有两个原因:

  1. HTTP 请求的 传输层协议(HTTP 还是 HTTPS),是在 建立 TCP 连接时就确定的: 如果客户端用 80 端口 发起 TCP 连接,默认是 HTTP 协议;如果用 443 端口 发起 SSL/TLS 加密连接,默认是 HTTPS 协议。依次在URL中并没有直接写协议方案。
  2. HTTP 协议客户端通过 Host请求头 明确指定目标域名,保证同一个IP下可以用多个域名,也就是说IP信息保存在请求报头的Host中,所以也并不用显示的在URL中写出来。

通过这两种方式只保留路径和查询参数,提升传输效率。

四. 响应报文

响应报文与请求报文结构一致,只不过其中的字段信心不一样,下面是响应报文的结构:

请添加图片描述

请求行的结构:

  1. HTTP版本:与请求行的作用一样,保证使用统一协议进行通信;
  2. 状态码服务器对客户端 HTTP 请求的 “结果反馈码”,核心作用是 用标准化的数字,清晰告知请求的处理状态,关于请求码有很多,在最后一部分来统一讲解;
  3. 状态码描述:用来描述状态码标识的含义。

报头和响应正文与请求报头一样,就不再赘述了。

下一步先实现一下HTTP服务器,再实现的过程中进行更多的介绍。

五. 实现HTTP服务器

HTTP服务器的实现与TCP是完全一样的,只不过我们使用HTTP现成的协议,而不需要自己再定制了。所以在之前编写的TCP服务器上对Service的行为进行修改即可。

因此我们只需要对接收到的HTTP请求进行响应就行了,此处先进行简单演示,我们对于所有请求都返回HELLO WORLD

1    void Response(int fd_)
2    {   
3        // 构建响应报文
4        std::string response_line = "HTTP/1.1 200 OK\r\n";   // 请求行
5        std::string text = "HELLO WORLD";                    // 请求正文
6        std::string repsonse_heaeder = "Content-Length:" + std::to_string(text.size()) + "\r\n" + "\r\n";  // 请求报头
7        
8        std::string response = response_line + repsonse_heaeder + text;
9        std::cout << response << std::endl;
10        write(fd_ , response.c_str() , response.size());
11    }
12
13    void Service(int fd_)
14    {
15        char buffer[1024*10];
16        while (1)
17        {
18            memset(buffer, 0, sizeof(buffer));
19            int n = read(fd_, buffer, sizeof(buffer) - 1);
20            if(n > 0)
21            {
22                buffer[n] = 0;
23                std::cout << buffer << std::endl;
24                Response(fd_);
25            }   
26        }
27    }
28

此时用浏览器再进行访问就会打印出HELLO WORLD的字段:
请添加图片描述

以上仅仅是一个简单的实现,我们也可以将用户要进行访问的所有数据都进行整理,到一个目录中,该目录被称为\web根目录

我们可以根据用户的请求,解析出用户要进行访问的路径,通过该路径找到文件,将文件的内容传递给用户,即使用write接口将文件内容发送给用户。此处就不再进行拓展了,有兴趣的可以自己试一试。

六. HTTP的细节字段

下面将详细介绍HTTP报头中的各个字段。

6.1 请求方法

方法核心语义主要用途
GET读取资源查询数据、获取静态资源
POST提交 / 创建资源表单提交、创建数据
PUT全量更新资源替换完整资源
PATCH部分更新资源修改资源部分字段
DELETE删除资源删除指定资源
HEAD读取资源头检查资源存在性、获取元信息
OPTIONS查询支持的方法跨域预检、调试

数据是通过表单进行提交的,即我们通常所使用的搜索框。如果我们想要提交信息一般使用POST,但是也可以通过Get来进行提交:

  • 使用POST提交的数据将被放到请求的正文部分;
  • 实际上使用Get也可以进行数据的提交,只不过Get默认请求正文为空,因此通过Get方法提交的参数信息将被放到URL中。

6.2 状态码

在HTTP中在状态码有很多,一下通过一张表格进行展示:

分类(首位数字)含义典型场景 & 状态码示例
1xx(信息类)服务器已接收请求,需客户端配合100 Continue:客户端要发大文件,服务器先回应 “可以继续发”(避免浪费流量); 101 Switching Protocols:升级协议(如 HTTP→WebSocket)。
2xx(成功类)请求被成功处理200 OK:最常见,如浏览器访问网页成功; 201 Created:新建资源成功(如注册用户、上传文件); 204 No Content:请求成功,但无内容返回(如删除资源后)。
3xx(重定向类)需客户端跳转到新地址301 永久重定向:旧网址失效,永久跳新地址(如域名更换); 302 临时重定向:临时跳转到其他页面(如商品缺货引导登记); 304 未修改:资源没变化,客户端直接用缓存(省流量)。
4xx(客户端错误类)客户端请求有问题,服务器无法处理400 错误请求:参数格式错(如 API 传了无效 JSON); 401 未授权:需要登录但没登录; 403 禁止访问:权限不够(如普通用户访问管理员页面); 404 资源不存在:网址打错,或资源被删除。
5xx(服务器错误类)服务器处理请求时出错500 内部服务器错误:服务器代码崩溃(如程序 bug); 503 服务不可用:服务器过载或维护(如双 11 期间服务器忙); 504 网关超时:服务器调用其他服务超时(如调用支付接口卡住)。

以上几种状态码都比较容易理解,此处重点解释一下3xx的状态码:

  • 因为某些原因,你要访问的服务器无法再向你提供访问,所以服务器响应时,以3开头,并在报头中会添加一个Location字段,通过这一字段来告诉浏览器哪一个位置可以访问到该资源,浏览器会根据该地址,进行二次跳转

6.3 常见的报头

因为报头中的字段有很多,此处我们只介绍一些最常见的。

  • Content_Length:正文的大小;
  • Host:请求客户端的端口号和IP;
  • User-agent:客户端的软件信息,比如用户的操作系统,浏览器版本信息等;
  • referer:用户要访问的网页是从哪一个网页跳转过来的;
  • Connectkeep-alive通信双方,采用长连接进行通信;

此处不同一下什么是长连接,以及对应的短链接又是什么意思:

在访问一个网址的时候,该网址内可能有很多元素,像视频,图片,文本等等;每一个元素都是资源,访问这些资源的时候都需要发送请求

短链接:一次请求响应一个资源,相应完成后就关闭连接;
长连接:建立TCP连接,发送并返回多个HTTP请求和响应。

长连接可以满足只建立一次连接就能拿到多个资源。

  • Connect-Type:正文部分数据类型,如html,png,image…

以上就是一些常见的报头字段的含义。

6.4 Cookie和Session ID

HTTP请求是独立的,服务器会认为每一次请求都不一样,因此服务器并不能通过HTTP请求来看是不是同一个用户在进行访问,我们在浏览器上登录一个网站后,当关闭浏览器后,为什么不需要在此重复的登录???

此时就需要使用到Cookie了,Cookie相当于一张身份标识,当浏览器向服务端发送请求的时候,如果有一个身份标识,服务端就能认出来要访问的用户是谁了,通过这种方式就很好的解决了HTTP相互独立的问题。

浏览器获取Cookie的具体流程如下:

  1. 首次登录:服务器验证账号密码后,在响应报头中通过 Set-Cookie 响应头 下发 Cookie给浏览器(如 user_id=123; password=abcdef)。
  2. 存储 Cookie:浏览器将 Cookie 存在内存(临时,浏览器关闭则删)或硬盘(长期,按过期时间留存)。
  3. 后续请求:浏览器自动在 请求头的Cookie字段 中携带 Cookie,服务器读取后识别用户。

当服务端向客户端发送响应的时候,如果对应的响应被中间人获取,此时他就可以通过对应的Cookies字段,拿到用户的账号和密码了。

此时为了防止这一种情况,需要使用Session ID.

  • 服务器会为每个用户生成唯一 Session ID,并将用户数据(如登录信息、权限)与 ID 关联,保存在服务器中。

这样服务器就不需要将用户的账号和密码放到Cookie中,而是将Session ID放到Cookies中,当浏览器再次访问时,就只需要携带该Session ID即可。

通过这种方式,如果中间人拿到了报文,他也只能获取Session ID,而用户的账号和密码并不会被泄露,个人隐私得以保证。


【网络编程】深入 HTTP:从报文交互到服务构建,洞悉核心机制》 是转载文章,点击查看原文


相关推荐


榨干每一滴算力:ONNX Runtime 多维优化实战指南
Cosolar2025/10/2

在当今人工智能应用快速落地的背景下,模型部署的效率和稳定性已成为决定产品成败的关键因素之一。ONNX(Open Neural Network Exchange)作为一种开放的模型交换格式,配合 ONNX Runtime(ORT)推理引擎,已成为工业界广泛采用的模型部署方案。然而,仅仅将模型转换为 ONNX 格式并不足以获得最佳性能。真正高效的推理部署,需要从模型优化、推理引擎配置、硬件加速和系统集成等多个维度协同发力。本文将结合实际经验,深入探讨如何通过 ONNX Runtime 实现模型部署的


​​FeedMe (RSS阅读器) 信息聚合/阅读体验优化​​
2501_935689192025/10/2

获取地址:​​FeedMe (RSS阅读器) FeedMe 是一款设计简洁、体验流畅的RSS阅读器应用,支持多平台使用。该应用提供智能分类、关键词过滤、离线下载等核心功能,并具备文章朗读、稍后阅读等增强特性。其清爽的阅读界面与手势操作设计,让用户能够高效获取并管理订阅的资讯内容


分布式专题——24 Kafka功能扩展
失散1310/2/2025

Kafka 性能压测、搭建 Kafka 监控平台 EFAK、Kraft 集群、Kafka 与流式计算


机器学习-第三章 线性模型
weixin_429630269/30/2025

斜率0.994表示:月广告费每增加1万元,月销售量平均增加0.994万元。- 截距-0.220表示:当广告费为0时,销售量的基准值约为-0.220万元。线性回归方程:y = 0.994x + -0.220。决定系数R²:0.9642(越接近1,拟合效果越好)


【C语言】计算两个整数二进制表示中不同位的个数
无限进步_9/30/2025

本文分析了一种计算两个整数二进制表示中不同位数量的方法。原始代码虽然直观易懂,但在效率和可移植性方面有改进空间。通过使用异或操作和高效计算1的个数的方法,我们可以显著提高代码的性能。在实际编程中,我们应该根据具体需求选择合适的方法。如果代码可读性是首要考虑,原始方法是不错的选择;如果性能是关键因素,优化方案更为合适。理解位操作和二进制表示是计算机科学的基础,掌握这些技巧对于成为高效的程序员至关重要。希望本文能帮助你更好地理解二进制比较的概念和实现方法。


Claude 4.5 升级解析:很强,但请别跳过“Imagine”
飞哥数智谈2025/10/4

9月30号,Anthropic 发布了 Claude 4.5。 因为最近一直在尝试 Claude Code 的各种场景,所以先尝试了 Claude Code 2.0,今天才有空完整地了解下 Claude 4.5 这次升级。 尤其是预览的“Imagine with Claude”,强烈建议了解下。 升级内容 模型核心能力 先通过评测标准的得分直观了解下升级的程度。 各方面均有所提升,但其实没有这个分数,大家估计对 Claude 的能力也没什么怀疑的。 毕竟,最佳的通用 AI 可能还会有一点点争议


【Linux CentOS 7 版本更换yum源】
zhaotiannuo_19982025/10/5

Linux CentOS 7 版本更换yum源 1、备份文件 cd /etc/yum.repos.d/ mkdir backup mv /etc/yum.repos.d/Cen* backup 2、下载文件 http://mirrors.aliyun.com/repo/Centos-7.repo 3、通过xftp 文件传输工具传输到/etc/yum.repos.d/目录下 4、清理软件源,建立缓存 yum clean all yum makecache 5、检查是否更新成功 yum repo


Python 的内置函数 bool
IMPYLH2025/10/6

Python 内建函数列表 > Python 的内置函数 bool 在编程中,我们经常需要判断某个值是“真”(True)还是“假”(False),而 bool() 函数就是 Python 提供的用于进行布尔值转换的强大工具。无论是数字、字符串、列表,还是自定义对象,bool() 都能帮助我们快速评估它们的真假状态。 bool 是一个类,它的参数如下: class bool(x=False): ''' 类型转换为 bool :param x: 一个变量 :r


Redis Zset 类型全解析
gsfl2025/10/8

文章目录 1.引言2.Zset 类型的核心特性与 Set、List 的关键差异 3.Zset 类型核心命令3.1 元素添加与基础查询:zadd、zrangezaddzrange 3.2 元素计数:zcard、zcountzcardzcount 3.3 排序与排名查询:zrevrange、zrangebyscore、zrank、zrevrank、zscorezrevrangezrangebyscorezrankzrevrankzscore 3.4 元素删除:zpopmax、


1688 店铺商品全量采集与智能分析:从接口调用到供应链数据挖掘
一人の梅雨2025/10/9

一、技术定位与商业价值重构 1688 店铺商品接口(alibaba.item.list.get)作为获取店铺全量商品数据的核心入口,其价值远不止于简单的数据采集。与常规实现不同,本文方案聚焦B 端供应链数据的深度挖掘,通过商品结构化解析、价格策略分析、供应链能力评估三大维度,解决批发商关注的 "店铺品类布局"" 批量采购议价空间 ""供应商履约能力" 等核心问题,构建从数据采集到商业决策的完整技术链路。 区别于网络上常见的基础调用示例,本方案实现三大突破: 支持全量商品智能分页(突破单页限

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0