HTTP 连接详解
概述
世界上几乎所有的 HTTP 通信都是由 TCP/IP 承载的,客户端可以打开一条TCP/IP连接,连接到任何地方的服务器。一旦连接建立,客户端和服务器之间交换的报文就永远不会丢失、受损或失序
TCP(Transmission Control Protocol)传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。通俗来讲,TCP就是双方通信的一个规范标准,负责对数据的传输进行一定的控制
HTTP 要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的 TCP 连接按序传输。TCP 收到数据流之后,会将数据流分成被称作段的小数据块,并将段封装在 IP 分组中,通过因特网进行传输。所有这些工作都是由 TCP/IP 软件来处理的、HTTP 程序员什么都看不到
每个 TCP 段都是由 IP 分组承载,从一个 IP 地址发送到另一个 IP 地址,每个 IP 分组包括:
- IP 分组首部(通常为 20 字节)
- TCP 段首部(通常为 20 字节)
- TCP 数据块(0 个或多个字节)
IP 首部包含了源和目的 IP 地址、长度和其他一些标记。TCP 段的首部包含了 TCP 端口号、TCP 控制标记,以及用于数据排序和完整性检查的一些数字值
TCP 段首部格式如下:
-
源端口号就是指本地端口,目的端口号就是远程端口
-
序号,也称序列号(Sequence Number),用于 TCP 通信过程中某一传输方向上字节流的每个字节的编号,以防止乱序问题。简单来说,就是在传输过程中用序列号来标记自己的位置,保证数据能按序传输
-
确认序号,也称确认序列号(Acknowledgment Numbe),是接收确认端所期望收到的下一序列号。确认序号应当是上次已成功收到数据字节序号加 1,只有当标志位中的 ACK 标志为 1 时该确认序列号的字段才有效。主要用来解决不丢包的问题
-
标志位,TCP Flag,TCP 首部中有 6 个标志比特,它们中的多个可同时被设置为 1,主要是用于操控 TCP 的状态机,依次为 URG,ACK,PSH,RST,SYN,FIN
-
ACK
表示应答域有效,这个标识可以理解为发送端发送数据到接收端,发送的时候 ACK 为 0,标识接收端还未应答,一旦接收端接收数据之后,就将 ACK 置为 1,发送端接收到之后,就知道了接收端已经接收了数据
-
SYN
表示同步序列号,用来建立 TCP 连接。SYN 标志位和 ACK 标志位搭配使用,当连接请求的时候,SYN = 1,ACK = 0;连接被响应的时候,SYN = 1,ACK = 1;这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有 SYN 的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口
-
FIN
表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送 FIN 标志位的 TCP 数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描
-
-
窗口大小(Window Size),也称为滑动窗口大小,用来进行流量控制
TCP 三次握手
TCP 三次握手,即建立 TCP 连接,需要客户端和服务端总共发送 3 个包以确认连接的建立。在 Socket 编程中,这一过程由客户端执行 connect 来触发
TCP 握手的目的有三个:
- 确认双方的接收与发送能力是否正常
- 初始化序列号,为后面的可靠传送做准备
- 进行数字证书的验证以及加密密钥的生成
- 第一次握手:客户端发送请求报文将 SYN = 1 同步序列号和初始化序列号 seq = J 发送给服务端,发送完之后客户端处于发送等待状态
- 第二次握手:服务端受到 SYN 请求报文之后,如果同意连接,会以自己的同步序列号 SYN = 1、初始化序列号 seq = K 和确认序列号(期望下次收到的数据包)ack = J + 1 以及确认号 ACK = 1 报文作为应答,此时服务器为确认接收状态
- 第三次握手:客户端接收到服务端的 SYN + ACK 之后,知道可以发送下一序列的数据包了,然后发送同步序列号 ack = K + 1 和数据包的序列号 seq = J + 1 以及确认号 ACK = 1 确认包作为应答,客户端转为确认连接状态
为什么是三次握手,而不是一次、二次呢?因为有可能出现这种情况:客户端发送了一个连接请求,但出现网络延迟,导致客户端没有及时收到服务端的响应,就会认为本次请求失效。而这时原本延迟的请求又来到服务端,服务端确认并保持等待状态,但实际上此时客户端并没有与服务端连接的意思,这就会导致服务器一直处于等待状态,造成资源浪费
TCP 四次挥手
TCP 四次挥手,即终止 TCP 连接,需要客户端和服务端总共发送 4 个包以确认连接的断开。在 Socket 编程中,这一过程由客户端或服务端任一方执行 close 来触发
- 第一次分手:第一次分手无论是客户端还是服务端都可以发起,因为 TCP 是全双工的。假如客户端发送的数据已经发送完毕,发送 FIN = 1 告诉服务端,客户端所有数据已经全发完了,服务端可以关闭接收了,但如果服务端还有数据要发给客户端,客户端照样可以接收的。此时客户端处于 FIN_WAIT_1 等待关闭状态
- 第二次分手:服务端接收到客户端的释放请求连接之后,知道客户端没有数据要发给自己了,然后服务端发送 ACK = 1 告诉客户端已经收到发给自己的信息,此时服务端处于 CLOSE_WAIT 等待关闭状态
- 第三次分手:此时服务端已经没有数据向客户端发送了,然后发送一个 FIN = 1,用于告诉客户端,服务端的所有数据发送完毕,客户端可以关闭接收数据连接了。此时服务端状态处于 LAST_ACK 确认关闭状态
- 第四次分手:此时如果客户端收到了服务端发送完的信息之后,就发送 ACK = 1,告诉服务端,客户端已经收到了服务端的信息,服务端处于 CLOSED 状态,四次挥手全部完成
为什么是四次挥手呢?因为关闭连接时,己方收到对方的 FIN 报文,仅仅表示对方不再向自己发送数据,但还能接受数据。己方可能还有数据要发送给对方,所以不能向三次握手一样直接把 ACK 和 SYN 放一起发送,而是先发送 ACK,直到没有数据要发送了,才是 FIN 确认关闭连接
TCP 性能优化
HTTP 位于 TCP 上层,所以 HTTP 事务的性能在很大程度上取决于底层 TCP 通道的性能
1. 延迟确认
为了避免网络延迟导致的数据丢失,TCP 实现了自己的确认机制来确保数据的成功传输
每个 TCP 段都有一个序列号和数据完整性校验和。每个段的接收者收到完好的段时,都会向发送者回送小的确认分组。如果发送者没有在指定的窗口时间内收到确认信息,发送者就认为分组已被破坏或损毁,并重发数据
由于确认报文很小,所以 TCP 允许将返回的确认信息与输出的数据分组结合在一起,更有效地利用网络。为了增加确认报文找到同向传输数据分组的可能性,TCP 实现了一种【延迟确认】算法,延迟确认算法会在一个特定的窗口时间(通常是 100-200 毫秒)内将输出确认存放在缓冲区中,以寻找能够捎带它的输出数据分组。如果在那个时间段内没有输出数据分组,就将确认信息放在单独的分组中传送
2. TCP 慢启动
TCP 数据传输的性能还取决于 TCP 连接的使用期,TCP 连接会随着时间进行自我调节,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度,这种调节被称为 TCP 慢启动,用于防止因特网的突然过载和拥塞
TCP 慢启动限制了一个 TCP 端点在任意时刻可以传输的分组数。简单来说,每成功接收一个分组,发送端就有了发送另外两个分组的权限。如果某个 HTTP 事务有大量数据要发送,是不能一次将所有分组都发送出去的,必须发送一个分组,等待确认,然后发送两个分组,每个分组都必须被确认,然后发送四个分组,以此类推,这种方式被称为【打开拥塞窗口】
由于存在这种拥塞控制特性,所以新连接的传输速度会比已经交换过一定量数据的连接慢一些
3. Nagle 算法与 TCP_NODELAY
TCP 有一个数据流接口,应用程序可以通过它将任意尺寸的数据放入 TCP 栈中,即使一次只放一个字节。但是,每个 TCP 段都至少装载了40个字节的标记和首部如果 TCP 发送了大量包含少量数据的分组,网络的性能就会严重下降
Nagle 算法试图在发送一个分组之前,将大量 TCP 数据绑定在一起,以提高网络效率。Nagle 算法鼓励发送全尺寸的分组,只有当所有其他分组都被确认之后,Nagle 算法才允许发送非全尺寸的分组。如果其他分组仍然在传输过程中,就将那部分数据缓存起来。只有当挂起分组被确认,或者缓存中积累了足够发送一个全尺寸分组的数据时,才会将缓存的数据发送出去
Nagle 算法会引发几种 HTTP 性能问题,首先,小的 HTTP 报文可能无法填满一个分组,可能会因为等待那些永远不会到来的额外数据而产生时延。其次,Nagle 算法与延迟确认之间的交互存在问题,Nagle 算法会阻止数据的发送,直到有确认分组抵达为止,但确认分组自身会被延迟确认算法延迟 100-200 毫秒
HTTP应用程序常常会禁用 Nagle 算法,如果要使用的话,一定要确保会向TCP写入大块的数据,不会产生一堆小分组
4. TIME_WAIT 累积与端口耗尽
当某个 TCP 端点关闭 TCP 连接时,会在内存中维护一个小的控制块,用来记录最近所关闲连接的 IP 地址和端口号。这类信息只会维持一小段时间,通常是所估计的最大分段使用期的两倍(称为 2MSL,通常为两分钟左右),以确保在这段时间内不会创建具有相同地址和端口号的新连接
2MSL 的连接关闭延迟在某些情况下会出现问题,例如客户端每次连接到服务器时,都会获得一个新的端口,以实现连接的唯一性,但由于可用端口的数量有限(比如 60000 个),而且在 2MSL 秒(比如 120 秒)内连接是无法重用的,连接率就被限制在 60000/120=500 次/秒,如果服务器的连接率高于 500 次/秒,就会遇到端口耗尽问题
HTTP 连接处理
1. 串行事务处理延时
如果只对连接进行简单的管理,TCP 的性能时延可能会叠加起来。比如,假设有一个包含了三个嵌入图片的 Web 页面,测览器需要发起四个 HTTP 事务来显示此页面:一个用于顶层的 HTML 页面,三个用于嵌入的图片。如果每个事务都需要建立一条新的连接,那么连接时延和慢启动时证就会叠加起来
除了串行加载引入的实际时延之外,加载一幅图片时,页面上其他地方都没有动静也会让人觉得速度很慢,用户更希望能够同时加载多幅图片。并行加载的另一个缺点是,有些沟览器在对象加载完毕之前无法获知对象的尺寸,而它们可能需要尺寸信息来决定将对象放在屏幕的什么位置,所以在加载了足够多的对象之前,无法在屏靠上显示任何内容
2. 并行连接
HTTP 允许客户端打开多条连接,并行地执行多个 HTTP 事务,提高加载速度,但并不是绝对的,在带宽较小的情况下,并行执行多个 HTTP 事务带来的性能提升就很小,甚至没什么提升。而且,打开大量连接会消耗大量的资源
3. 持久连接
Web 客户端经常会打开到同一个站点的连接,比如,一个 Web 页面上的大部分内嵌图片通常都来自同一个 Web 站点。因此,对某服务器 HTTP 请求的应用程序很可能会在不久的将来发起更多的请求。因此,HTTP/1.1 允许 HTTP 设备在事务处理结束之后会将 TCP 连接保持在打开状态,以便在未来重用现存的连接发起 HTTP 请求,称为持久连接,直到客户端或服务器决定将其关闭为止。持久连接可以避免缓慢的连接建立阶段,以及慢启动的拥塞适应阶段,以便更快速地进行数据传输。通常情况下,持久连接和并行连接配合使用
实现 HTTP/1.0 的客户端可以通过包含 Connection:Keep-Alive 首部请求将一条连接保持在打开状态,如果服务器愿意为下一条请求将连接保持在打开状态,就在响应中包含相同的首部。如果响应中没有 Connection:Keep-Alive 首部,客户端就认为服务器不支特 Keep-alive,会在收到响应报文之后关闭连接
可以用 Keep-A1ive 通用首部中指定的,由逗号分隔的选项来调节 keep-alive 的行为
Connection: Keep-Alive
Keep-Alive: max=5, timeout=120
- timeout:估计服务器希望将连接保持在活跃状态的时间,这并不是一个承诺值
- max:估计服务器还希望为多少个事务保持此连接的活跃状态,这并不是一个承诺值
HTTP/1.1 逐渐停止对 keep-alive 连接的支持,用一种名为持久连接(persistentconnection)的改进型设计取代了它。,HTTP/1.1 默认所有连接都是持久的,要在事务处理结束之后关闭连接,必须向报文中显式地添加一个 Connection: close
首部。客户端收到响应后,除非响应中包含 connection: close
首部,否则连接就维持在打开状态。但是,客户端和服务端仍然可以随时关闭空闲的连接,不发送 connection: close
并不意味着服务端承诺永远将连接保持在打开状态
4. 管道化连接
HTTP/1.1 允许在持久连接上可选的使用管道,在响应到达之前,可以将多条请求放入队列,当第一条请求到达服务器,第二条和第三条请求也可以开始发送了。在高时延网络条件下,这样可以降低网络的环回时间,提高性能
对管道化连接有几点注意事项:
- 如果 HTTP 客户端无法确认连接是持久的,就不应该使用管道
- 必须按照与请求相同的顺序回送 HTTP 响应,HTTP 报文中没有序列号标签,如果收到的响应失序了,就没办法将其与请求匹配起来了
- HTTP 客户端必须做好连接会在任意时刻关闭的准备,还要准备好重发所有未完成的管道化请求。如果客户端打开了一条持久连接,并立即发出了十条请求,服务器可能只处理了五条请求之后关闭连接,客户端必须能够应对过早关闭连接的情况,重新发送请求
- HTTP 客户端不应该用管道化的方式发送会产生副作用的请求(比如 POST),如果出错,管道化方式会阻碍客户端了解服务器执行的是一系列管道化请求中的哪一个
相关文章:

HTTP 连接详解
概述 世界上几乎所有的 HTTP 通信都是由 TCP/IP 承载的,客户端可以打开一条TCP/IP连接,连接到任何地方的服务器。一旦连接建立,客户端和服务器之间交换的报文就永远不会丢失、受损或失序 TCP(Transmission Control Protocol&…...

练习题(2024/5/12)
1二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9 输出: 4…...

Day50代码随想录动态规划part12:309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费
Day50 动态规划part12 股票问题 309.最佳买卖股票时机含冷冻期 leetcode题目链接:309. 买卖股票的最佳时机含冷冻期 - 力扣(LeetCode) 题意:给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。设计一个算…...

【软考】scrum的步骤
目录 1. 明确产品愿景和需求2. 制定计划和任务列表3. 进行迭代开发(Sprint)4. Sprint评审会议5. Sprint回顾会议6. 重复迭代 1. 明确产品愿景和需求 1.这个过程通常由项目所有者和利益相关者参与,目的是确保整个团队对项目的目标和方向有清晰…...

【C语言】编译与链接
✨✨欢迎大家来到Celia的博客✨✨ 🎉🎉创作不易,请点赞关注,多多支持哦🎉🎉 所属专栏:C语言 个人主页:Celias blog~ 目录 引言 一、翻译环境 1.1 编译 1.1.1 预处理 1.1.2 编译 …...

Consul 注册的服务地址变成了 127.0.1.1
问题 我们的服务一直用 Consul 作为注册中心,在 AWS 和 阿里云上使用的时候,没出现过问题。最近把一些服务迁到腾讯云的时候,遇到一个问题:注册的服务地址都是 127.0.1.1。 127.0.1.1 这个地址我们平时遇到的比较少,…...

数字水印 | 离散小波变换 DWT 的 Python 代码实现
🍍原文: 【图像处理】图像离散小波变换及 Python 代码实现 🍍写在前面: 本文在原文的基础上补全了代码。 1 环境准备 ① 安装 p y w t \mathsf{pywt} pywt 包: pip install PyWavelets说明: p y w t \…...

[框架] Unity 公共执行器
本篇我们通过使用单例模式来创建一个公共执行器,使得原本应该在Update()、FixedUpdate()中的指令都可以统一放在一个对象中执行,且可进行添加和移除操作。 1. 创建单例模式改造器:SingletonMono 我们先创建一个单例模式改造器,使…...

二进制转为HEX数组小工具
在使用RA8889时,JPG的解码只能从FLASH的DMA通道获取,那么如果要从远端、或者SD卡等处读取JPG图片出来显示怎么办? RA8889支持JPG图片硬解码,但数据流是从FLASH进行DMA读取的,然后再进行解码。因此这种情况下ÿ…...

数据结构-二叉树-红黑树
一、红黑树的概念 红黑树是一种二叉搜索树,但在每个节点上增加一个存储位表示节点的颜色,可以是Red或者BLACK,通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,…...

C++11 新特性 decltype 说明符
一、typeof与typeid 1.1、typeof 在C11标准之前,GCC已经提供了一个类似功能的运算符 typeof对类型进行推导,但是这毕竟是编译器的实现,不是标准。 int a 0; typeof(a) b 5;1.2、typeid C标准提供了 typeid 运算符,获取的类型…...

java线程局部变量使用方式
线程局部变量是Java中用于存储线程本地信息的变量。这种变量仅在线程的生命周期内存在,并且每个线程都有自己的一份拷贝。换句话说,线程局部变量是线程私有的,其他线程无法访问。 使用场景主要包括: 1. 存储线程状态信息ÿ…...
【隧道篇 / WAN优化】(7.4) ❀ 01. 启动WAN优化 ❀ FortiGate 防火墙
【简介】几乎所有的人都知道,防火墙自带的硬盘是用来保存日志,以方便在出现问题时能找到原因。但是很少的人知道,防火墙自带的硬盘其实还有另一个功能,那就是用于WAN优化。 防火墙自带的硬盘 在FortiGate防火墙A、B、C、D系列&…...

2024数维杯数学建模B题生物质和煤共热解问题的研究原创论文分享
大家好,从昨天肝到现在,终于完成了2024数维杯数学建模挑战赛B题的完整论文啦。 实在精力有限,具体的讲解大家可以去讲解视频: 2024数维杯数学建模B题煤共热解每一问高质量完整代码讲解!_哔哩哔哩_bilibili 2024数维杯…...

中国电子学会(CEIT)2022年12月真题C语言软件编程等级考试三级(含详细解析答案)
中国电子学会(CEIT)考评中心历届真题(含解析答案) C语言软件编程等级考试一级 2022年12月 编程题五道 总分:100分一、鸡兔同笼(20分) 一个笼子里面关了鸡和兔子(鸡有2只脚,兔子有4只脚,没有例外)。已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至…...

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 5月12日,星期日
每天一分钟,知晓天下事! 2024年5月12日 星期日 农历四月初五 1、 全国多地已推“一次挂号管三天”,部分医院专家门诊适用。 2、 在梅大高速塌方事故中拦车、救援,黄曼秋等5人拟确认为见义勇为。 3、 深圳新能源车指标申请条件调…...

微服务思想以及实现
文章目录 前言一、什么时候需要拆分微服务1. 创业型项目2. 大型项目 二、怎么拆1. 拆分目标2. 拆分方式 三、微服务之间远程调用1. 实现方式2. 手动发送Http请求(RestTemplate)3. 服务注册中心3.1 原理3.2 Nacos注册中心3.3 服务注册3.4 服务发现(Discov…...

C语法:格式符号%f和%lf引发的错误
今天编程时有如下代码: #include"stdio.h"int main(void) {double profit;double bonus;printf("请输入本月利润\n");scanf("%f",&profit);//错误:此行profit是double类型,格式符为%f,当输入8时࿰…...

Java基础入门day48
day48 JDBC调用关系 tomcat 简介 tomcat是Apache下的一个核心项目,免费开源,支持servlet和jsp。 tomcat技术先进,性能稳定,目前比较流行的web应用服务器 安装 官网: Apache Tomcat - Welcome! 下载 tomcat8.5 解压&a…...

C++笔记(体系结构与内核分析)
1.OOP面向对象编程 vs. GP泛型编程 OOP将data和method放在一起,目的是通过封装、继承、多态提高软件的可维护性和可扩展性GP将data和method分开,可以将任何容器与任何算法结合使用,只要容器满足塞饭所需的迭代器类型 2.算法与仿函数的区别 …...

c++ 唤醒指定线程
在C中,直接唤醒一个特定的线程并不像在Java的Thread类中有interrupt()方法或者某些操作系统特定的API(如POSIX的pthread_cond_signal或Windows的SetEvent)那样简单。C标准库没有提供一个直接的方法来"唤醒"一个正在等待的线程。然而…...

java报错:使用mybatis plus查询一个只返回一条数据的sql,却报错返回了1000多条
今天遇到一个问题 系统线上问题,经常出现这样的问题,刚重启系统时不报错了,可是运行一段时间又会出现。sql已经写了limit 1,mybatis的debug日志也返回total为1,可是却报错返回了1805条数据 乍一看,感觉太不…...

AI图书推荐:利用生成式AI实现业务流程超自动化
《利用生成式AI实现业务流程超自动化》(Hyperautomation with Generative AI)这本书探索了广泛的用例和示例,展示了超自动化在不同行业、领域和特定部门的多样化应用, 让您熟悉UiPath、Automation Anywhere和IBM等流行工具和平台&…...

什么事防抖和节流,有什么区别,如何实现
防抖和节流,本质上是优化高频率执行代码的一种手段,比如:resize、scroll、keypress、mousemove这些事件在触发的时候,会不断调用绑定在事件上的回调函数,这样极大浪费资源,降低前端性能。 为了优化体验&am…...

PanguSync大数据量初始化脚本
由于数据库增量同步软件PanguSync初始化最大超时时间为600s,如果初始数据量很大,第一次部署时可能会超时,可以先停止任务,使用以下Sql语句进行初始化,以下语句可以分步执行,初始化完成后,后续无需再执行耗时…...

DELL T630服务器iDRAC分辨率调整办法
对于Dell T630服务器的iDRAC分辨率调整,您需要登录到iDRAC的Web界面。以下是详细的步骤: 登录iDRAC:在浏览器中输入iDRAC的IP地址,然后使用用户名(通常是“root”)和密码登录。 导航到虚拟控制台ÿ…...

您真的会高效使用 Mac 吗?
文章目录 屏幕的保养快捷键预览修改文件名查看文件属性搜索编辑复制,粘贴,剪切,撤销删除 跳转窗口屏幕截图声音Dock强制退出查字典神奇的Option键鼠标与触控板切换桌面与应用程序打开通知中心打开Mission Control 安装与卸载Mac应用程序压缩和…...

Vue11 Vue3完结撒花
shallowRef和shallowReactive shallowRef 作用:创建一个响应式数据,但只对顶层属性进行响应式处理 用法 let myVar shallowRef(initialValue)特点:只跟踪引用值变化,不关心值内部的属性变化 案例 <template><div c…...

CodeTop 高频笔试题总结(持续更新)
🏆 频率从高到低排序 👨🏫 参考的频率数据:CodeTop 👨🏫 力扣hot100 无重复字符的最长子串 双指针 滑动窗口 哈希👨🏫 力扣hot100 反转链表 指针 递归 一题多解👨…...

类和对象一(从封装开始讲述)
目录: 一.封装 二.封装扩展之包,自定义包 三.访问限定符 四.static成员 一.封装:封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互。面向对象…...