【问题标题】:Transmission of data in HTTPHTTP 中的数据传输
【发布时间】:2017-11-10 18:37:44
【问题描述】:

我正在尝试了解 HTTP 服务器和客户端内部的基础知识,了解它们如何传输数据。我已经阅读了很多关于 HTTP 如何工作的文章,但我没有找到任何可以回答我的一些问题的文章。 我想按照我的理解来完成加载网页的过程,如果你能告诉我哪里出错了,我将不胜感激。

  1. 当我访问一个站点时,我的浏览器会向服务器请求一个 HTML 文件,因为我的浏览器会创建一个套接字,将其绑定到我的 IP 地址,并将其连接到该站点服务器的侦听套接字我正在参观。为了将浏览器的套接字连接到服务器,我需要一个端口号和一个主机名,端口号是 80,因为这是 HTTP,并且主机名是通过 DNS 解析获得的。现在套接字之间存在连接,我的浏览器发送了一个 GET 请求。该请求是一个 ASCII 文件,其内容对应于 HTTP 请求。我的浏览器将 ASCII 原始字节写入套接字,然后写入服务器的套接字。

  2. 服务器将我请求的 HTML 文件写回到套接字。服务器发送的 HTML 只是一个 ASCII 文件,服务器将逐字节写入套接字。

  3. 我的浏览器接收到 ASCII 文件并对其进行解析。让我们假设它找到了一个图像标签。浏览器发送对该图像文件的 HTTP 请求。我不明白的东西来了。服务器如何响应?据我所知,服务器必须发回由一组标头组成的 ASCII 文件,然后是 CRLF,然后是消息正文。在这种情况下,假设我的浏览器请求 .jpeg,服务器是否将标头作为 ASCII 明文写入套接字,然后将图像的原始字节写入套接字?

  4. 如果 HTML 文件有多个图像,我们是否会为每个图像(每个请求)打开一个套接字?

  5. 假设我的浏览器现在找到了一个 javascript 标签。当服务器响应我对该脚本的请求时,服务器是否将脚本源的 ASCII 字节写入套接字? js库会发生什么?服务器是否必须发送每个源代码?

  6. 关于向套接字写入数据:write(2) 是在套接字之间进行所有这些写入的正确方法吗?

  7. 关于大文件的传输:如果我点击网站上的一个按钮让我下载一个大的 PDF,服务器是如何完成的?我假设服务器尝试分段传输。据我所知,有一个分块编码的选项。是这样吗?如果是,文件是否被分成块,这些块被附加到 ASCII 响应中并逐字节写入套接字?

最后,视频是如何传输的?我知道视频编码和传输需要整本书才能详细解释,但如果您能谈谈视频传输的一般性(例如在 youtube 中),我将不胜感激。

您可以就套接字级别的 HTTP 说任何话,我们将不胜感激。谢谢。

【问题讨论】:

    标签: javascript html sockets http server-side


    【解决方案1】:

    我下面的所有答案都与 HTTP/1.1 相关,而不是 HTTP/2:

    3.-我的浏览器接收到 ASCII 文件并对其进行解析。让我们假设它找到了一个图像标签。浏览器发送对该图像文件的 HTTP 请求。我不明白的东西来了。服务器如何响应?据我所知,服务器必须发回一个由一组标头组成的 ASCII 文件,然后是 CRLF,然后是消息的正文。在这种情况下,假设我的浏览器请求 .jpeg,服务器是否将标头作为 ASCII 明文写入套接字,然后将图像的原始字节写入套接字?

    是的,通常是这样。它可能是用不同的格式(gzip、brotli)编码的,或者如果没有设置Content-Length,它可能会被分块。

    4.- 如果 HTML 文件有多个图像,我们是否为每个图像(每个请求)打开一个套接字?

    在 HTTP/1 中,现代浏览器将为每个主机打开最多 6 个套接字,但不会更多。如果有超过 6 个请求发往同一个主机,它会一直等待,直到收到其他响应。

    5.- 假设我的浏览器现在找到了一个 javascript 标签。当服务器响应我对该脚本的请求时,服务器是否将脚本源的 ASCII 字节写入套接字? js库会发生什么?服务器是否必须发送每个源代码?

    通常是的,每个 javascript 文件需要 1 个 http 请求。有一些服务器端工具将 javascript 源代码及其依赖项组合在一个 javascript“文件”中。请注意,javascript 源通常是 UTF-8,而不是 ASCII。

    6.- 向套接字写入数据:write(2) 是在套接字之间进行所有这些写入的正确方法吗?

    不知道!不是C家伙

    7.- 关于大文件的传输:如果我点击网站上的一个按钮让我下载一个大的 PDF,服务器是如何完成的?我假设服务器尝试分段传输。据我所知,有一个分块编码的选项。是这样吗?如果是,文件是否被分成块,这些块被附加到 ASCII 响应中并逐字节写入套接字?

    不,chunked 用于事先不知道内容长度的 HTTP 响应。您所说的“拆分”是在 IP/TCP 级别上完成的,而不是在 HTTP 协议级别上。从 HTTP 的角度来看,它只是一个连续的流。

    最后,视频是如何传输的?我知道视频编码和传输需要整本书才能详细解释,但如果您能谈谈视频传输的一般性(例如在 youtube 中),我将不胜感激。

    太宽泛了,我无法回答。

    【讨论】:

      【解决方案2】:

      强烈推荐阅读High-Performance Browser Networking

      关于 HTTP

      HTTP 是一个消息结构协议。它可以构建在TCP/IPUDP 或任何其他通信协议之上。

      IP 解决了确定消息要到达网络中哪台计算机的问题,而 TCP 解决了确保尽管有噪声干扰但仍能接收到消息的问题。 UDP 做 TCP 做的事,但没有一些重要的保证使其在某些情况下变得更好,例如视频流。

      HTTP 只解决了消息应该是什么样子的问题,所以每个人都能理解你的意思。 HTTP 消息由 headerbody 组成。正文是您要发送的消息;标头包含有关消息本身状态的元信息。 HTTP 允许您通过一组标准术语以有意义的、面向上下文的方式构建您的应用程序。

      例如,您可以通过 HTTP 通信字符编码,您的内容有多长,您是否可以接收压缩格式的内容,等等。所以,不,HTTP 不限于 ASCII 文本 - 您可以发送带有 BOM 标记的 UTF-8 编码字符,甚至根本不指定编码。 HTTP 所做的只是让您以您想要的方式请求事物,并告知收件人您是如何打包消息的。

      负责处理您的消息如何发送而不是结构化的实际事情是 TCP/IP 和 UDP。 HTTP 与它无关。 TCP/IP 和 UDP 都增加了开销,但非常值得这样做,以便通信可以畅通无阻。

      关于套接字

      计算机侦听“套接字”,这只是一个用来指代通信通道的花哨名称。套接字是什么并不重要 - 它只是用于指代通信通道的通用名称,无论是有线还是无线电。重要的是套接字可以做什么。计算机可以通过套接字发送字节(称为刷新),并且可以读取通过套接字发送的字节。套接字总是携带一定数量的内存,用于为传入的消息(如收件箱)保留称为 缓冲区,甚至可以将许多消息捆绑在一起并一次性发送以节省时间。

      硬件级别的套接字通常转移到网卡,它可以让您与无线网络或以太网电缆通信。请注意,计算机的插座可能比电缆多得多 - 这是因为插座是单个通信通道的通用名称,而单个网络/以太网卡可以处理多个通信通道。能够同时处理多个通道称为多路复用

      TCP/IP 和 UDP 只是蓝图 - 操作系统有责任按照它们的布局实际执行,并且大多数操作系统都有一些旨在实现这些标准的程序。在软件级别,信息的读取和写入方式变得比仅传递字节稍微复杂一些,因为计算机还必须能够在硬件事件发生时中断其正在运行的程序,包括从套接字通信时 - 这是@的参考987654324@。

      所有操作系统都会公开一组调用来开始监听(绑定)套接字、读取套接字和写入套接字。但是,您可以通过多种方式从套接字读取。这些范围从大多数 Linux 发行版中的基本 select() 和 [poll()] 到 Linux 中的 epoll()一个程序,在必须读取数据之前要求在收到数据时得到通知。

      Windows 导出一组完全不同的系统调用,因此如果您计划为 Windows 构建应用程序,建议您参考参考手册。

      关于 TCP/IP

      TCP/IP 是两种协议的组合,大部分已成为确保可靠通信的标准。

      IP 负责术语IP 地址。每台计算机都有一个与之关联的唯一地址,指定为 32 位数字 (IPv4) 或 128 位数字(IPv6 或 IP 版本 6)。请注意,这些地址存在于网络之外:网络只是计算机的集合,而计算机的地址仅在该集合中才有意义。计算机来自的网络是计算机IP地址的一部分;网络本身被赋予一个唯一的地址;一个网络可以由多个网络组成。 IP 协议引入了端口的概念,它本质上与套接字的概念同义

      我只是随意地把“网络”这个词当作一个抽象概念来讨论,但实际上它归结为一个路由器。路由器是一种特殊的计算机,负责使用附加到消息的 IP 地址来确定消息中引用的对象,并将 IP 地址分配给它知道的计算机(网络实际上是路由器知道的计算机集about),以及将消息转发到其他计算机或路由器。 internetwork(或只是 Internet)只是一堆路由器,每个路由器都有自己的网络,能够相互通信以形成一个巨大的连接网络网络。实际上,路由器实现了 IP 标准。

      TCP 和 UDP 旨在解决另一个令人痛心的问题:如何确保您的所有消息都能通过。将任何消息发送到共享通信通道(如无线或什至像bus topology 组织的有线通道)本质上是混乱的 - 不同的消息可能重叠,消息可能意外丢失,消息可能损坏等等。 TCP 旨在通过保证消息的所有通过来解决这些问题。另一方面,UDP 不做这样的保证,因此通过跳过 TCP 所做的许多步骤来节省时间。

      TCP 和 UDP chunk 将消息分成一定大小的数据包,以便尽可能快地发出消息。 TCP 进一步为交换添加了一些额外的结构,称为三向握手:

      • 它向要向其发送消息的计算机发送一个称为 SYN 数据包的特定于 TCP 的消息,然后等待响应。
      • 如果目标计算机收到它,它会以 SYN ACK 数据包进行响应。收到此消息后,源计算机会以 ACK 数据包进行响应。这让两台计算机都知道对方正在侦听,并且它们可以开始发送数据包。
      • 另一方面,如果源计算机或目标计算机在一段时间后没有听到任何声音,它们会等待一段时间并再次发送,然后再等待一段时间。每次他们必须等待时,他们等待的时间是上次的两倍,直到达到最大等待时间并中止连接。这称为exponential backoff,是 TCP 的关键。

      三次握手可确保每个人都准备好并愿意倾听。然而,乐趣并不止于此:

      • 作为握手的一部分,源计算机指定它将触发初始特定数量的数据包,每个数据包具有特定大小。
      • 握手后,源计算机触发指定的数据包,并等待每个发送数据包的 ACK。如果它没有收到任何数据包的 ACK,它会在重新发送该数据包之前进入指数退避
      • 同时,目标计算机已被告知等待一定数量的数据包,因此它会等到所有数据包都进入。数据包可能会乱序到达,具体取决于中间网络路由器选择如何优化每个数据包的路径数据包,因此每个数据包前面都有一条特定的消息,指示它们的顺序,目标计算机将它们组合成一条简洁的消息。
      • 一旦源接收到 ACK,它会使用总时间来查看接下来可以发送多少。响应时间越好,TCP 愿意发送的数据包就越多。

      UDP 跳过三次握手。它只分块和发送。不能保证您的所有消息都会到达那里。不能保证它会按顺序发送(而不是按顺序接收)。它非常适用于高网络可靠性意味着您的大部分消息可能会到达的情况,但如果所有消息都到达并不重要(例如,如果视频中的某些帧没有到达也没关系)。

      关于视频

      视频与任何其他内容格式基本上没有区别。完全可以将 HTTP 用于视频。是否建议使用 TCP 是另一回事,但也不错 - Skype uses both UDP and TCP

      所有视频都由一系列字节组成。如何解释这些字节是编码的工作。视频可以有多种编码:avimp4 很容易想到。使用 HTTP,您可以将内容编码指定为消息头的一部分。

      HTTP 启用压缩内容,包括视频。 HTTP 还允许您请求连接保持保持活动状态,即在发送完整消息后无需再次执行三次握手。开发了一个名为websockets 的HTTP 扩展,它有效地使用这两个功能来为实时视频传递提供支持。这些仅优化视频到达,使其看起来不会延迟,但不会改变视频到达的方式。

      当然,有时候你想要对视频有更多的保证,在低速互联网环境下支持高保真视频,或者让多人订阅一个直播等等,有很多很多的技巧。那是你必须发挥创造力的时候。但除此之外,视频内容与任何其他内容类型没有根本区别。

      回答您的问题

      当我访问一个站点时,我的浏览器会向服务器请求一个 HTML 文件,因为 我的浏览器创建一个套接字,将它绑定到我的 IP 地址,并且 将其连接到我所在站点的服务器的侦听套接字 参观。为了将浏览器的套接字连接到我需要的服务器 端口号和主机名,端口号是 80,因为这是 HTTP 和主机名是通过 DNS 解析获得的。现在有了 是套接字之间的连接,我的浏览器发送 GET 请求。那 request 是一个 ASCII 文件,其内容对应于 HTTP 要求。我的浏览器将 ASCII 原始字节写入套接字,然后 写入服务器的套接字。

      HTTP 不需要 80 端口。按照惯例,使用 HTTP 的服务器默认端口为 80,HTTPS 的默认端口为 443,但任何端口都可以使用,只要不占用其他端口即可。

      您没有收到来自 DNS 的主机名。实际上,恰恰相反——您提供一个主机名,然后从 DNS 中检索一个 IP 地址。它是用于标识另一个网络上的位置的 IP 地址。

      响应不一定是 ASCII。是的,标头将被解释为 ASCII,因为它们是在 UTF-8 获得突出地位之前开发的国际标准的一部分,但正文不需要此类限制。事实上,内容编码传统上是作为标头本身传递的,浏览器或客户端可以使用它来自动解码正文内容。

      服务器将我请求的 HTML 文件写回套接字。这 服务器发送的 HTML 只是服务器将写入的 ASCII 文件 逐字节发送到套接字。

      是的,除非它不需要是 ASCII。

      我的浏览器接收到 ASCII 文件并对其进行解析。让我们在这里假设 它找到一个图像标签。浏览器为此发送一个 HTTP 请求 图像文件。我不明白的东西来了。如何 服务器响应?据我所知,服务器必须发回 由一组标头组成的 ASCII 文件,后跟 CRLF,然后是 消息的正文。在这种情况下,假设我的浏览器要求 .jpeg,服务器是否将标头作为 ASCII 明文写入 socket,然后将图像的原始字节写入套接字?

      是的。

      如果 HTML 文件有多个图像,我们是否为每个图像打开一个套接字(每个 请求)?

      this answer。 HTML 总是在图像请求被触发之前首先被下载,并且图像总是按照它们在 DOM 中遇到的顺序被请求。如果您在 Chrome 上有 24 张图片,其中 6 张将一次并行加载,这意味着四个并行连接。

      您还可以自己回答这个问题,方法是在 Chrome 控制台中打开“网络”选项卡,并检查图像请求是否被并行触发。

      假设我的浏览器现在找到了一个 javascript 标签。当。。。的时候 服务器回答我对该脚本的请求服务器是否写入 脚本源的 ASCII 字节到套接字?什么 js库会发生什么?服务器是否必须发送所有源 每个都有代码?

      HTML 规范允许您选择what order you want your Javascript files to be downloaded

      是的,服务器写入字节。字节不需要进行 ASCII 编码。标头将采用 ASCII 格式。是的,服务器必须发送每个库的源代码。这就是为什么 Web 优化的一个重要部分是最小化您的 Javascript 文件大小并将所有库捆绑到一个文件中,以减少请求的数量和大小。

      关于向套接字写入数据:write(2) 是正确的方法吗? 这是套接字之间的写法?

      这无疑是在 Linux 内核上写入打开文件描述符的最基本方法。 Linux 中的所有内容都被视为文件,包括套接字,所以是的,套接字具有文件描述符并且可以以这种方式写入。

      有更复杂的方法可以实现这一点,所有这些方法都在manual page 中为write 引用。大多数语言都支持写入套接字,但是,通过胶合代码使用更友好的界面手动调用write()。也许您需要在 C 中显式调用 write() 的唯一情况是您正在编写内核级程序或在嵌入式硬件上。

      关于大文件的传输:如果我点击网站上的按钮 让我下载一个大的PDF,这是如何完成的 服务器?我假设服务器尝试分段传输。作为 据我所知,有一个分块编码的选项。这是 大大地?如果是,是否将文件划分为块,并附加这些块 到 ASCII 响应并逐字节写入套接字?

      请参阅我上面写的 TCP/IP 部分。 HTTP 标准确实允许您在让 TCP 进一步分块之前将消息分解为更高阶的块,因此您可以处理一次到达的小段。

      最后,视频是如何传输的?

      请看我上面写的视频部分。

      【讨论】:

        【解决方案3】:

        HTTP、套接字、流和包传输是不同的主题。

        HTTP 是一种用于请求或发送数据的通信协议。 Web 开发人员不经常使用套接字,因为它们对网络不是很友好,因为需要持久连接。您的浏览器如何管理 HTTP 请求通常不是您真正关心的问题。

        对于像视频这样的大块数据,流式传输可能是最好的技术,因为您不需要客户端和服务器之间的同步,也不需要像套接字这样的始终处于活动状态的连接。 流式传输的方式仅取决于您以及您在服务器上用于共享内容的语言。

        如果您想了解有关 HTTP 的更多信息,我建议您阅读一些有关 RFC 的内容,例如 RFC 7230RFC 7231。 要了解数据是如何传输的,您应该真正了解Abstraction Layers 的基础,对于视频流,您可能会学习如何使用 NodeJs 制作一个视频流服务器(您可能会选择您喜欢的另一种语言),或者只是搜索并安装一个已经为你完成这项工作的 NPM 包。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-03-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-06
          • 2021-10-29
          • 2018-05-04
          • 1970-01-01
          相关资源
          最近更新 更多