当前位置: 首页 > news >正文

深入理解HTTP/2:nghttp2库源码解析及客户端实现示例

文章目录

    • 一、HTTP/2 特性实现:nghttp2 源码剖析
      • 1.1 二进制帧
      • 1.2 多路复用
      • 1.3 头部压缩
      • 1.4 服务器推送
      • 1.5 总结
    • 二、使用 nghttp2 库创建一个 HTTP/2 客户端
    • 三、结语

在互联网时代,网络传输协议的作用至关重要。在本文中,我们将对 HTTP/2 的一些核心特性进行深入的剖析,并通过实例代码,展示如何使用 nghttp2 库来实现 HTTP/2 的高效特性。

一、HTTP/2 特性实现:nghttp2 源码剖析

在我们开始探索如何使用 nghttp2 库创建一个 HTTP/2 客户端之前,首先让我们结合 nghttp2 库的源码,了解 HTTP/2 的主要特性是如何实现的。

1.1 二进制帧

作为 HTTP/2 的基础,二进制帧的处理方式对于整个协议的性能和可靠性有着至关重要的影响。HTTP/2 使用二进制帧来传输数据,这使得数据传输更加高效和可靠。

在 nghttp2 中,二进制帧的实现可以在 nghttp2_frame.c 文件中找到。每个帧由一个固定长度的帧头(9 字节)和一个可选的帧负载组成。帧头包括以下字段:长度(24位)、类型(8位)、标志(8位)、保留位(1位)和流标识符(31位)。

nghttp2 提供了一系列 API 来处理二进制帧,如 nghttp2_frame_pack() 用于将帧结构体编码为二进制数据,nghttp2_frame_unpack() 用于将二进制数据解码为帧结构体。

1.2 多路复用

接下来,我们来看看 nghttp2 是如何实现 HTTP/2 的另一个重要特性:多路复用。通过多路复用,HTTP/2 可以在一个连接上并行处理多个请求和响应,这大大提高了网络利用率。

在 nghttp2 中,多路复用的实现可以在 nghttp2_stream.c 文件中找到。nghttp2 使用优先级队列来管理多个流,以实现多路复用。

当新的帧到达时,nghttp2 会根据帧头中的流标识符找到对应的流。然后,根据帧类型和优先级,对流进行处理。例如,数据帧会被传递给应用程序进行处理,而控制帧(如 WINDOW_UPDATE)会被用来更新流的状态。

1.3 头部压缩

头部压缩是 HTTP/2 的另一个重要特性,它可以有效地减少网络传输的开销。HTTP/2 使用 HPACK 算法压缩头部,减少了网络传输的开销。

在 nghttp2 中,头部压缩的实现可以在 nghttp2_hd.c 文件中找到。nghttp2 提供了一系列 API 来处理头部压缩,如 nghttp2_hd_deflate() 用于压缩头部,nghttp2_hd_inflate() 用于解压缩头部。

HPACK 算法使用了两种技术来压缩头部:静态表和动态表。静态表包含了常见的头部字段,动态表则在连接过程中逐渐学习头部字段。通过这两个表,HPACK 可以有效地压缩头部数据。

1.4 服务器推送

最后,我们来看看 nghttp2 是如何实现 HTTP/2 的服务器推送特性的。HTTP/2 允许服务器主动向客户端推送资源,提高了页面加载速度。

在 nghttp2 中,服务器推送的实现可以在 nghttp2_push.c 文件中找到。服务器可以通过 nghttp2_submit_push_promise() 函数提交一个 PUSH_PROMISE 帧,告知客户端即将推送的资源。然后,服务器可以使用 nghttp2_submit_response() 函数发送推送资源的响应。

客户端可以通过设置回调函数来接收服务器推送的资源。例如,可以使用 nghttp2_session_callbacks_set_on_push_promise() 函数设置 PUSH_PROMISE 帧的回调,以及 nghttp2_session_callbacks_set_on_data_chunk_recv() 函数设置接收到推送数据的回调。

1.5 总结

通过以上的源码分析,我们可以看到,HTTP/2 的主要特性在 nghttp2 中得到了很好的实现。这些特性共同让 HTTP/2 成为了一个高效、可靠的网络传输协议。

二、使用 nghttp2 库创建一个 HTTP/2 客户端

理论知识了解了之后,接下来我们通过一个实例来看看如何使用 nghttp2 库创建一个 HTTP/2 客户端。

下面的 C 语言示例代码演示了如何使用 nghttp2 库创建一个 HTTP/2 客户端。这个客户端会向服务器发送一个 GET 请求,打印出响应,并加入错误处理、超时、取消请求、流量控制等特性。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <nghttp2/nghttp2.h>#define PORT 8080
#define BUFFER_SIZE 4096// 发送回调函数
static ssize_t send_callback(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) {int fd = *(int *)user_data;ssize_t sent = write(fd, data, length);if (sent < 0) {perror("Failed to send data");exit(EXIT_FAILURE);}return sent;
}// on_frame_send_callback 函数
static int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) {if (frame->hd.type == NGHTTP2_DATA) {printf("Sent data frame\n");}return 0;
}// on_data_chunk_recv_callback 函数
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) {printf("Received data chunk: %.*s\n", (int)len, data);// 检查是否需要流量控制ssize_t window_size = nghttp2_session_get_stream_remote_window_size(session, stream_id);if (window_size < len) {printf("Stream window size is too small, need to increase\n");nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, stream_id, len - window_size);}return 0;
}// on_stream_close_callback 函数
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) {printf("Stream %d closed with error code %d\n", stream_id, error_code);return 0;
}int main(int argc, char **argv) {if (argc != 3) {fprintf(stderr, "Usage: %s <host> <path>\n", argv[0]);return 1;}const char *host = argv[1];const char *path = argv[2];struct addrinfo hints = {0}, *res;hints.ai_family = AF_INET;hints.ai_socktype = SOCK_STREAM;int error = getaddrinfo(host, "80", &hints, &res);if (error) {fprintf(stderr, "Failed to resolve host: %s\n", gai_strerror(error));return 1;}int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);if (fd < 0) {perror("Failed to create socket");return 1;}if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {perror("Failed to connect");return 1;}// 设置套接字超时struct timeval timeout = {5, 0};  // 5 secondssetsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));nghttp2_session_callbacks *callbacks;nghttp2_session_callbacks_new(&callbacks);nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback);nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, on_data_chunk_recv_callback);nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);nghttp2_session *session;nghttp2_session_client_new(&session, callbacks, &fd);nghttp2_session_callbacks_del(callbacks);nghttp2_nv headers[] = {MAKE_NV(":method", "GET"),MAKE_NV(":path", path),MAKE_NV(":scheme", "http"),MAKE_NV(":authority", host),};int32_t stream_id = nghttp2_submit_request(session, NULL, headers, sizeof(headers) / sizeof(headers[0]), NULL, NULL);char buffer[BUFFER_SIZE];ssize_t bytes_read;while ((bytes_read = read(fd, buffer, BUFFER_SIZE))```c
> 0) {if (nghttp2_session_mem_recv(session, (const uint8_t *)buffer, bytes_read) < 0) {fprintf(stderr, "Failed to process data\n");break;}if (nghttp2_session_send(session) < 0) {fprintf(stderr, "Failed to send data\n");break;}}// 取消请求nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_CANCEL);nghttp2_session_del(session);close(fd);return 0;
}

在上述代码中,我们创建了一个 HTTP/2 客户端,这个客户端连接到服务器,发送一个 GET 请求,并打印出响应。

on_frame_send_callback 函数在每次发送帧时被调用。在这个函数中,我们打印了一条消息,表明我们发送了一个数据帧。

on_data_chunk_recv_callback 函数在每次接收数据块时被调用。在这个函数中,我们打印了接收到的数据块,并检查了流的窗口大小。如果窗口大小小于数据块的长度,我们就提交一个窗口更新帧,以增加窗口大小。这是流量控制的一种形式。

on_stream_close_callback 函数在流关闭时被调用。在这个函数中,我们打印了流的 ID 和错误代码。

在主函数中,我们增加了错误检查,以处理获取地址信息、创建套接字和连接套接字时可能出现的错误。我们还设置了套接字的接收超时。如果在指定的时间内没有接收到数据,read 函数将返回一个错误。

在读取和处理数据的循环中,我们增加了错误检查,以处理接收和发送数据时可能出现的错误。

最后,我们使用 nghttp2_submit_rst_stream 函数提交了一个 RST_STREAM 帧,以取消请求。这个帧将导致流立即关闭,任何未发送或未接收的数据都将被丢弃。

三、结语

通过对 nghttp2 库的源码剖析,我们对 HTTP/2 的主要特性有了深入的理解。同时,我们也演示了如何使用 nghttp2 库创建一个 HTTP/2 客户端。希望通过本文的分析,能够帮助读者更好地理解 HTTP/2 协议,为客户端开发提供更高效、可靠的网络传输支持。

相关文章:

深入理解HTTP/2:nghttp2库源码解析及客户端实现示例

文章目录 一、HTTP/2 特性实现&#xff1a;nghttp2 源码剖析1.1 二进制帧1.2 多路复用1.3 头部压缩1.4 服务器推送1.5 总结 二、使用 nghttp2 库创建一个 HTTP/2 客户端三、结语 在互联网时代&#xff0c;网络传输协议的作用至关重要。在本文中&#xff0c;我们将对 HTTP/2 的一…...

如何在印尼新闻媒体发布新闻稿件:通稿宣发的好处

如何在印尼新闻媒体发布新闻稿件&#xff1a;通稿宣发的好处 在全球化的时代&#xff0c;新闻稿件的发布和传播对于企业、组织或个人来说至关重要。尤其在印尼这样的东南亚国家&#xff0c;新闻媒体的力量不容忽视。本文将探讨在印尼新闻媒体发布新闻稿件的优势&#xff0c;以…...

如何在 Linux 系统上更改 SSH 服务端口以增强服务器安全性

SSH 服务器默认使用 22 端口&#xff0c;这使得攻击者可以轻松识别并尝试攻击 SSH 服务。为了增加服务器的安全性&#xff0c;建议更改默认的 SSH 端口。本文将详细介绍如何在 Linux 系统上更改 SSH 服务端口&#xff0c;并包含一些最佳实践和注意事项。 1. 检查新的 SSH 端口…...

c++11新特性 -nullptr

今天遇到一道不错的题目&#xff0c;主要讲了c11对nullptr的特性。 首先&#xff1a;nullptr 是C11引入的一个关键字&#xff0c;用于表示“空指针常量”&#xff0c;它是类型std::nullptr_t的唯一值。nullptr 的引入解决了C语言风格的空指针字面量&#xff08;如NULL或0&#…...

kubernets学习笔记——Kubernets 命令行工具 kubectl

目录 kubectl 的使用方法1、语法2、操作3、资源类型4、输出格式5、示例&#xff1a;常用操作6、示例&#xff1a;创建和使用插件 kubectl 的使用方法 Kubernetes 提供 kubectl 是使用 Kubernetes API 与 Kubernetes 集群的控制面进行通信的命令行工具。这个工具叫做 kubectl。通…...

Hadoop单机及集群部署

目录 一、Hadoop 单机模式部署1. 环境准备2. 安装 Java3. 下载并安装 Hadoop4. 配置环境变量5. 配置 Hadoop编辑 core-site.xml编辑 hdfs-site.xml编辑 mapred-site.xml编辑 yarn-site.xml 6. 格式化 Namenode7. 启动 Hadoop 服务8. 验证 Hadoop注意事项 二、Hadoop 集群模式部…...

使用es-hadoop同步hive和es之间数据

&#x1f4bb;近期在华为云连接es时的时候发现不能输入账号密码&#xff0c;后面联系华为工程师了解到&#xff0c;华为云默认是非安全模式&#xff0c;即不需要输入账号密码。 如果对你有所帮助&#xff0c;欢迎点赞收藏关注不迷路哦&#x1f493; 目录 使用es-hadoop同步h…...

Java - 泛型 + JUnit

一、泛型(参数化类型,在编译时确定) 泛型是一种可以接收数据类型的数据类型(可以这么理解) 作用&#xff1a; 1.能对加入到集合中的数据类型进行约束 2.遍历的时候&#xff0c;不需要进行类型转换&#xff0c;提高效率(因为遍历时&#xff0c;默认的是Object,需要进行类型转换的…...

vue3实现包含表格的Word文件导出

vue3实现包含表格的Word文件导出 近期遇到一个要求&#xff0c;需要在网页上导出Word文档&#xff0c;文档中有表格&#xff0c;也有普通的数据&#xff0c;查阅了很多资料&#xff0c;总算比较完美的解决了&#xff0c;记录一下 先上一下最终效果 演示视频 vue3项目根据Wor…...

【深度学习】TTS,CosyVoice,推理部署的代码原理讲解分享

文章目录 demo代码加载配置文件speech_tokenizer_v1.onnx(只在zero_shot的时候使用)campplus.onnx(只为了提取说话人音色embedding)`campplus_model` 的作用代码解析具体过程解析总结示意图CosyVoiceFrontEndCosyVoiceModel推理过程总体推理过程推理速度很慢: https://git…...

flask高频面试题

目录 高频面试题及答案1. 如何在Flask中处理数据库迁移&#xff1f;2. Flask如何处理文件上传&#xff1f;3. 如何在Flask中处理跨域请求&#xff08;CORS&#xff09;&#xff1f;4. 如何在Flask中实现用户认证&#xff1f;5. Flask如何处理会话&#xff1f;6. Flask如何处理表…...

尚硅谷谷粒商城项目笔记——五、使用docker安装mysql

五、使用docker安装mysql 注意&#xff1a; 因为电脑是AMD芯片&#xff0c;自己知识储备不够&#xff0c;无法保证和课程中用到的环境一样&#xff0c;所以环境都是自己根据适应硬件软件环境重新配置的&#xff0c;这里的虚拟机使用的是VMware。 使用 Docker 安装 MySQL 与安…...

filebeat + logstash使用笔记

背景 本文中有2台主机&#xff1a; &#xff08;1&#xff09;1.1.1.1是OpenStack的nova节点&#xff0c;安装filebeat &#xff08;2&#xff09;1.1.1.2是logstash节点 在1.1.1.1上通过filebeat读取OpenStack的nova-compute组件日志&#xff08;/var/log/nova/nova-compute.…...

学校考场电子钟设置自动开关机,节能环保

在标准化考试中&#xff0c;准确的时间显示对于确保考试的公正性和秩序至关重要。然而&#xff0c;传统的电子钟系统往往存在一些问题&#xff0c;影响了考试管理的效率。 一、学校普通电子钟使用问题 二、学校考场电子钟优点 学校同步时钟系统通过自动同步网络或卫星时间的方式…...

短剧APP系统开发带来了哪些发展空间?

在影视行业快速发展的时期&#xff0c;短剧作为一种新兴的影视模式&#xff0c;获得了大众的欢迎。目前&#xff0c;短剧行业巨大的发展空间&#xff0c;再次成为大众关注的焦点。 随着移动互联网的发展&#xff0c;信息技术不断升级进步&#xff0c;短剧APP系统的开发&#x…...

PaddlePaddle / PaddleOCR踩坑记,动手实现一个OCR服务器

文章目录 一、环境搭建1、官网2、准备环境 二、编码实现一个web程序 一、环境搭建 1、官网 https://gitee.com/paddlepaddle/PaddleOCR#/paddlepaddle/PaddleOCR/blob/main/doc/doc_ch/quickstart.md 2、准备环境 本地环境坑太多了&#xff0c;好在官网还有一种基于docker搭…...

JeecgBoot低代码平台简单记录

BasicModal弹窗 Usage 由于弹窗内代码一般作为单文件组件存在&#xff0c;也推荐这样做&#xff0c;所以示例都为单文件组件形式 注意v-bind"$attrs"记得写&#xff0c;用于将弹窗组件的attribute传入BasicModal组件 attribute&#xff1a;是属性的意思&#xff0c;…...

零基础入门转录组数据分析——机器学习算法之xgboost(筛选特征基因)

零基础入门转录组数据分析——机器学习算法之xgboost&#xff08;筛选特征基因&#xff09; 目录 零基础入门转录组数据分析——机器学习算法之xgboost&#xff08;筛选特征基因&#xff09;1. xgboost基础知识2. xgboost&#xff08;Rstudio&#xff09;——代码实操2. 1 数据…...

C#开发常见面试题三(浅复制和深复制的区别)

C#开发常见面试题三(浅复制和深复制的区别) 一.浅复制和深复制定义 &#xff08;1&#xff09;浅复制&#xff1a;复制一个对象的时候&#xff0c;仅仅复制原始对象中所有的非静态类型成员和所有的引用类型成员的引用。&#xff08;新对象和原对象将共享所有引用类型成员的实…...

Linux/C 高级——Linux命令

从这里开始&#xff0c;我们展开对Linux/c 高级的学习&#xff0c;首先介绍的是在Linux/c高级中&#xff0c;Linux的部分 目录 1.Linux简介 1.1Linux起源 1.2查看系统版本命令 1.3分层结构 1.4系统关机重启命令 2.Linux安装工具 2.1软件包安装 2.1.1软件包的管理机制 …...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...