提问者:小点点

HTTP中的数据传输


我试图了解HTTP服务器和客户端的基本内部结构,以及它们如何传输数据。我读了许多关于HTTP如何工作的文章,但我还没有找到任何能回答我的一些问题的文章。我想去一个过程加载一个网页,因为我理解它,如果你让我注意到哪里我得到了它的错误,我会很感激。

>

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

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

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

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

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

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

    关于大文件的传输:如果我点击站点上的一个按钮,让我下载一个大的PDF,服务器是如何完成的?我假设服务器试图将其分段传输。据我所知,有一种分块编码的选择。是这条路吗?如果是的话,文件是不是被划分成块,并且这些块被附加到ASCII响应中,然后一个字节一个字节地写入套接字中?

    最后,视频是如何传输的?我知道视频编码和传输需要整本书来详细解释,但是如果你能说一些关于视频传输的一般性的东西(例如在youtube中),我会很感激的。

    如果您能在套接字级别上对HTTP发表任何看法,我们将不胜感激。谢了。


  • 共3个答案

    匿名用户

    我下面所有的答案都与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响应中,然后一个字节一个字节地写入套接字中?

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

    最后,视频是如何传输的?我知道视频编码和传输需要整本书来详细解释,但是如果你能说一些关于视频传输的一般性的东西(例如在youtube),我会很感激的。

    太宽泛了,我回答不了。

    匿名用户

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

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

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

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

    匿名用户

    强烈推荐阅读《高性能浏览器联网》。

    HTTP是一种消息结构化协议。它可以构建在TCP/IP,UDP或任何其他通信协议之上。

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

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

    例如,您可以将您的身体的字符编码与HTTP通信,您的内容有多长,您是否可以以压缩格式接收它,等等。所以,不,HTTP并不局限于ASCII文本--您可以发送带有BOM标记的UTF-8编码字符,或者甚至根本不指定编码。HTTP所做的一切就是让你以你想要的方式要求东西,并告知收件人你是如何打包一条消息的。

    负责处理消息发送方式而不是结构的实际事情是TCP/IP和UDP。HTTP与此无关。TCP/IP和UDP都增加了开销,但值得这样做,以便通信能够畅通无阻地通过。

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

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

    TCP/IP和UDP只是蓝图--操作系统的责任是实际按照它们的布局去做,大多数OSs都有一些程序设计来实现这些标准。在软件级别,如何读写信息比仅仅传递字节稍微复杂一些,因为计算机还必须能够在硬件事件发生时中断正在运行的程序,包括从套接字进行通信--这里是Linux内核如何实现TCP/IP的参考。

    所有操作系统都公开一组调用,以开始侦听(绑定)一个套接字,读取一个套接字和写入一个套接字。但是,您可以通过多种方式读取套接字。从大多数Linux发行版中的基本select()和[poll()]到Linux中的epoll(),前者强制程序等待,直到接收到所请求的所有数据,然后再读取数据,后者允许程序在读取数据之前请求收到通知。

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

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

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

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

    TCP和UDP旨在解决另一个令人痛苦的问题:如何确保所有消息都能通过。在共享通信信道(如无线或有线信道,组织如总线拓扑结构)下发送任何消息,本质上都是杂乱无章的--不同的消息可能重叠,消息可能意外丢失,消息可能被破坏等等。TCP通过保证所有的消息都能通过来解决这些问题。另一方面,UDP没有这样的保证,因此通过跳过TCP所做的许多步骤来节省时间。

    TCP和UDP将报文分成一定大小的数据包,这样报文就可以尽可能快地发送出去。TCP还为交换添加了一些额外的结构,称为三次握手:

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

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

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

    UDP跳过三次握手。它只进行分块和发送。它不能保证您的所有信息都能到达那里。它不能保证它将按顺序发送(与按顺序接收相反)。对于高网络可靠性意味着您的大部分消息可能会到达,但如果所有消息都到达也无关紧要的情况(例如,如果视频中的某些帧没有到达也没关系),它是完美的。

    视频从根本上说与其他任何内容格式没有区别。将HTTP用于视频是完全可能的。使用TCP是否可取则另当别论,但这并不是坏事--Skype同时使用UDP和TCP。

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

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

    当然,有时你想要更多关于视频的保证,有很多很多的技巧可以用来在低速互联网环境中支持高保真视频,或者让多人订阅一个直播等等。这时你必须要有创意。但除此之外,视频内容与任何其他内容类型并无本质区别。

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

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

    您不会从DNS收到主机名。实际上正好相反--你提供一个主机名,然后从DNS中检索IP地址。它是用来标识另一个网络中的位置的IP地址。

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

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

    是的,只是它不需要是ASCII。

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

    是的。

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

    看这个答案。HTML总是在发出图像请求之前首先下载,并且图像总是按照在DOM中遇到的顺序被请求。如果你在Chrome上有24张图片,那么一次会并行加载其中的6张,意味着四个并行连接。

    另外,你还可以通过打开Chrome控制台中的Network选项卡来回答这个问题,并检查图像请求是否被并行触发。

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

    HTML规范允许您选择Javascript文件的下载顺序。

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

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

    它当然是在Linux内核上写入打开文件描述符的最基本方法。Linux中的所有东西都像一个文件一样对待,包括套接字,所以是的,套接字有文件描述符,可以这样写。

    有更复杂的方法来实现这一点,在write手册页中引用了所有这些方法。但是,大多数语言都支持对套接字的写入,通过使用更友好的接口使用胶合代码手动调用write()。也许只有在编写内核级程序或在嵌入式硬件上时,才需要在C中显式调用write()

    关于大文件的传输:如果我点击站点上的一个按钮,让我下载一个大的PDF,服务器是如何完成的?我假设服务器试图将其分段传输。据我所知,有一种分块编码的选择。是这条路吗?如果是的话,文件是不是被划分成块,并且这些块被附加到ASCII响应中,然后一个字节一个字节地写入套接字中?

    请参阅上面我写的TCP/IP部分。HTTP标准确实允许您先将消息分成更高阶的块,然后再让TCP对其进行进一步的块化,这样您就可以凑合着使用一次到达的小段。

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

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