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

应用层协议 --- HTTP

序言

 在上一篇文章中,我们在应用层实现了一个非常简单的自定义协议,我们在我们报文的首部添加了报文的长度并且使用特定的符号分割。但是想做一个成熟,完善的协议是不简单的,今天我们就一起看看我们每天都会用到的 HTTP协议


URL

1.再识 URL

 刚接触电脑时我们肯定就会了解到 URL,我们一般称他为 网址,我们利用网址来定位网络上的特定资源。但是现在我们开始接触计网了,还应该认识他的本质了。
 在之前的内容中我们知道,所谓网络通信不过就是 物理距离增加后的 进程间通信。想要和网络上的某个设备的进程进行通信,我们需要知道该设备的 IP 地址和端口号IP地址 用于将数据发送到该设备上,端口号 用于将数据发送到指定进程。
 所以我们可以合理的推测 URL 的背后运行逻辑也是这样,但是我们一般的网址都是:

https://www.baidu.com/

没有你说的 IP,端口 呀?这是因为我们还需要 DNS,网址中的域名部分通过 DNS 系统被解析成 IP 地址。DNS 是一个分布式数据库,负责将人类可读的域名转换成机器可读的 IP 地址。
 这样做的原因是 网址提供了一种更加用户友好的方式来定位和访问这些资源。通过域名系统和默认的端口号约定,用户可以在不知道具体IP地址和端口号的情况下,轻松地访问他们想要的网络资源。

2. URL 的组成

 现在我使用百度的搜索引擎输出一个 你好URL 生成如下:
在这里插入图片描述
输出一个简单的 你好 竟然这么长,我们来层次的划分理解一下:

  • 协议部分指定了用于访问资源的应用层协议,如 HTTP、HTTPS、FTP
  • 域名部分代表了资源所在的主机或服务器的名称
  • 路径部分指定了服务器上资源的具体位置,帮助服务器定位到用户请求的具体资源
  • 参数部分用于向服务器传递额外的信息,以便服务器根据信息返回更精确的结果

在这里重点介绍一下 路径部分,当我们只是输入一个域名时我们可以在后面加上具体要访问的资源的路径,但是如果我们没有加上的话,他会自动跟一个 \。咦?这不是根目录吗?不是的,服务端接收后会处理你的路径,方法各有不同,但是常见的是将 \ 替换为他首页的页面路径,就拿百度举例:
在这里插入图片描述


HTTP 协议

1. 什么是 HTTP

 在互联网世界中,HTTP(HyperTextTransferProtocol,超文本传输协议)是一个至关重要的协议。它定义了 客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如 HTML 文档)。经过前面的的铺垫学习,我们知道了只要是协议,双方就达成了一定的约定!
 在这里还需要注意重要的一点是,HTTP 的底层是基于 TCP 进行数据传输的!

2. HTTP 的请求与响应

2.1 请求报头

 现在我们做一个简单的试验,我们在云服务器上运行我们的服务端程序,并在本地的浏览器上使用 IP + 端口号 的形式访问,可以看到服务端输出:
在这里插入图片描述
很奇怪,我们并没有发送任何的内容,这是哪里来的呀?大家别忘了,浏览器在应用层,发送信息是会对报文增添报头,即使报文为空!这里输出的内容,就是 HTTP 协议约定好的报头!!!

 现在我们来看一下标准格式的请求头:
在这里插入图片描述

我们来了解一下请求行的作用:

  • 请求方法
    GET: 请求指定的资源,我们用的最多的方式
    POST:向服务器提交数据,比如登陆账号信息,会将具体的信息放在正文中
  • URL:这指定了请求的资源的路径,前面介绍过
  • HTTP版本:指定了客户端使用的 HTTP 协议版本,告诉服务器客户端能够理解的协议特性

请求报头数量是非常多的,并且很多都是需要特定的使用场景,我们只是介绍常用的报头:

  • Host: 指定请求的服务器的域名和端口号
  • User-Agent: 包含了发出请求的用户代理软件信息,通常是浏览器
  • Accept: 告诉服务器可以发送哪些媒体类型的内容
  • Content-Length: 请求体的长度
  • Cookie:缓存部分用户信息

 在这里可能需要阐明一点,如果你的请求方式为 GET,也是可以传递数据的。咦?不应该使用 POST吗?两个都行!比如搜索引擎,我们需要向输入框内输入搜索查询内容吧,我们的输入的内容就会以参数的格式加在 URL 后面。所以只是两者的方式不同,前者是将传递数据放在正文中,后者是放在 URL 中。
Cookie 非常重要,在后面会专门写一篇文章来进行说明,今天重要的是大体介绍 HTTP 的内容。

2.2 响应报头

 有了请求报文,现在我们再获取一下响应报文(在这里并没有找到一个很好抓包的方法,使用 AI 生成了一个效果一样的,嘻嘻):
在这里插入图片描述

响应报文的标准格式如下:
在这里插入图片描述
HTTP版本 就不多阐述了,我们重点其他内容,首先是状态码和状态码阐述:
在这里插入图片描述
一共包含 5 类,每一类的作用都大不相同,我们介绍最常见的状态码:

  • 200 OK:请求已成功,请求所希望的响应头或数据体将随此响应返回
  • 404 Not Found:服务器无法找到请求的资源。在浏览器中,这意味着 URL 输入错误或请求的页面已经被移除或不存在
  • 403 Forbidden:服务器理解请求,但拒绝执行它,通常都是权限不够
  • 302 Found (Redirect):请求的资源临时移动到了另一个 URI。客户端应该使用 GET 方法访问新的 URI ,原始请求的方法可能不再适用
  • 504 Gateway Timeout:作为网关或代理的服务器在等待上游服务器的响应时超时

原来这就是当我们搜索出错时弹出来的状态码的意义呀!最后便是我们的报头数据了:

  • Content-Type:指示响应主体的数据类型
  • Content-Length:指示响应主体的长度,以字节为单位
  • Date:指示响应生成的日期和时间

实现 HTTP 响应

 首先我们来分析一下在应用层数据需要怎么处理:
在这里插入图片描述

  1. 首先,本地将请求序列化发送给服务端特定端口
  2. 服务端接收请求数据并反序列化,处理后得到相应的响应数据
  3. 服务端将响应数据序列化发送给客户端特定端口
  4. 客户端接受响应数据并反序列化,得到结果

结果分析,看起来我们需要完成请求数据的序列化和反序列化,响应数据的序列化和反序列化。但是其实 请求数据的序列化和响应数据反序列化 浏览器已经帮我们做了!当你访问一个 URL 时其实已经将请求数据序列化并发送了!根据你的搜索呈现相应的结果不就是响应数据的反序列化吗?

 明白了这些,咋们直接开敲,首先肯定是构建 TCP注意:HTTP协议的底层使用的是 TCP 协议,如果你使用 UDP 是不能正常沟通的!!!) 的创建套接字文件:

// 创建套接字文件
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
    perror("socket:");
    exit(1);
}
std::cout << "Successful create sockfd..." << std::endl;

 之后便是绑定:

 // 绑定
 struct sockaddr_in Address;
 Address.sin_family = AF_INET;
 Address.sin_addr.s_addr = INADDR_ANY;
 Address.sin_port = htons(PORT);
 int n = bind(sockfd, (struct sockaddr*)&Address, sizeof(Address));
 if(n < 0)
 {
     perror("bind::");
     exit(1);
 }
 std::cout << "Successful bind..." << std::endl;

再者就是监听和接受连接:

// 监听
n = listen(sockfd, 3);
if(n < 0)
{
    perror("listen:");
    exit(1);
}
std::cout << "Successful listening..." << std::endl;

// 等待接收连接
struct sockaddr_in ClientAddress;
socklen_t len = sizeof(ClientAddress);
int newfd = accept(sockfd,  (struct sockaddr*)&ClientAddress, &len);
if(newfd < 0)
{
    perror("accept:");
    exit(1);
}
std::cout << "Successful accept..." << std::endl;

这都是几乎比较固定的步骤!咋们对特定的业务只需要对数据处理模块进行特定的设计,现在我想要返回一个类似于网页的界面,就简单一下让人工智能帮咋们设计一下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>你好</title>
<style>
  @keyframes bounce {
    0%, 20%, 50%, 80%, 100% {
      transform: translateY(0);
    }
    40% {
      transform: translateY(-30px);
    }
    60% {
      transform: translateY(-15px);
    }
  }

  .bounce-text {
    font-size: 24px;
    animation: bounce 2s infinite;
  }
</style>
</head>
<body>

<div class="bounce-text">你好</div>

</body>
</html>

 现在当我们和客户端顺利连接时,咋们就按照协议的要求发送该内容:

std::string Content = GetIndex();
while(true)
{
    std::string response;
    response += "HTTP/1.0 200 OK\n";
    response += "Content-Length:" + std::to_string(Content.size()) + '\n';
    response += '\n'+ Content;

    send(newfd, response.c_str(), response.size(), 0);
}

我们简单的添加了状态行和响应报头最后是我们的上述 html 的内容,最后看一下效果:
在这里插入图片描述
成功啦,也不难是吧!


总结

 在这篇文章中我们简单认识了 HTTP 协议的组成,并且已经简单的实现了浏览器访问云服务器,返回相应数据的功能,希望大家有所收获!


http://www.kler.cn/a/326751.html

相关文章:

  • B树的实现
  • 细说STM32F407单片机轮询方式读写SPI FLASH W25Q16BV
  • 强力巨彩租赁屏技术更新,适用多种户外应用场景
  • Kafka可视化工具 Offset Explorer (以前叫Kafka Tool)
  • 查询 MySQL 默认的存储引擎(SELECT @@default_storage_engine;)
  • flask基础
  • 将Docker镜像推送到阿里云仓库,使用Docker-compose将mysql、redis、jar包整合在一起
  • spring如何解决循环依赖
  • NAND Flash虚拟层垃圾回收机制
  • 【微信小程序前端开发】入门Day01 —— 小程序页面组成、组件使用及协同开发发布指南
  • 深度探索与实战编码:利用Python与AWS签名机制高效接入亚马逊Product Advertising API获取商品详情
  • Web端云剪辑解决方案,提供前端产品源码
  • 使用 MongoDB 在 Spring Boot 中构建安全的 RBAC 系统
  • 【GESP】C++一级练习BCQM3016,初识输入
  • 实用工具推荐---- PDF 转换
  • 利用 Local Data 导入文件到 OceanBase 的方法
  • elasticsearch实战应用JAVA案例
  • Codeforces Round 975 (Div. 2)(A,B,C,D线段树解法,E)
  • 浅谈音频采集方案
  • Elasticsearch深度攻略:核心概念与实践应用
  • 前端使用 Konva 实现可视化设计器(23)- 绘制曲线、属性面板
  • vue3.0 + element plus 全局自定义指令:select滚动分页
  • 现代前端框架实战指南:React、Vue.js、Angular核心概念与应用
  • elasticsearch 向量检索 ann
  • 【架构】前台、中台、后台
  • QT 界面编程中使用协程