我试图了解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发表任何看法,我们将不胜感激。谢了。
我下面所有的答案都与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还为交换添加了一些额外的结构,称为三次握手:
三次握手确保每个人都准备好并愿意倾听。然而,乐趣还不止于此:
UDP跳过三次握手。它只进行分块和发送。它不能保证您的所有信息都能到达那里。它不能保证它将按顺序发送(与按顺序接收相反)。对于高网络可靠性意味着您的大部分消息可能会到达,但如果所有消息都到达也无关紧要的情况(例如,如果视频中的某些帧没有到达也没关系),它是完美的。
视频从根本上说与其他任何内容格式没有区别。将HTTP用于视频是完全可能的。使用TCP是否可取则另当别论,但这并不是坏事--Skype同时使用UDP和TCP。
所有视频都由一系列字节组成。如何解释这些字节是编码的工作。视频可以有多种编码方式:AVI
和MP4
都很容易想到。使用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对其进行进一步的块化,这样您就可以凑合着使用一次到达的小段。
最后,视频是如何传输的?
请看上面我写的视频部分。