Web(5)
HTTP底层协议
1. Internet协议族由4个主要层组成
A.链路层:处理物理通信介质的硬件接口细节,如以太网(Ethernet)、异步传输模式(ATM)
B.网络层:处理在网络上传递的各个数据包(IP数据包),网络层协议在路由器和终端主机中实现
C.传输层:传输层为应用层协调主机间通信,在实际应用中传输层协议一般在终端主机操作系统中实现
D.应用层:应用层处理特定应用程序的细节,在实际应用中,应用层协议一般作为应用软件的一部分实现(如WEB浏览器或WEB服务器)
2. HTTP消息传送涉及的3个主要协议,它们起始于网络层:
A.网际协议(IP):IP是一个网络层协议,它根据目标主机的IP地址协调一台主机到另一台主机间各个数据包(信息单元)的传递。IP协议是在链路层上构建的,它可以不考虑链路层的硬件技术问题
B.传输控制协议(TCP):TCP是一个传输层协议,它用于协调IP数据包的传输,以便在两个通信应用程序之间提供可靠的双向抽象连接。尽管一些应用程序采用用户数据报协议(UDP),但Internet主要的传输协议是TCP
C.域名系统(DNS):DNS是一个应用层协议,它控制主机名的转换(如将www.foo.com转换成IP地址,反之亦然)。DNS为很多应用程序提供这种通用服务
3. 网际协议(IP)
IP提供发送数据包的框架,包是一种信息单元----由发送机指定的一定数量数据(以字节为单位)。一条消息被分成多个数据包,Internet上的路由器独立处理每个包,无需维持连续包间的状态。一台主机到另一主机传输的一系列IP包不一定遍历网络的相同的路径。包可能丢失、破坏或不按顺序传递。
在IP之上终端主机实现了传输层协议,这些协议协调应用程序间端对端的数据分发。两个主要的传输协议是:传输控制协议(TCP)和用户数据报协议(UDP)。两个协议都已标准化,且用很多操作系统实现。 TCP提供大多数Internet应用程序需要的主抽象----以有序可靠的方式将一序列字节从发送方传送到接收方的逻辑连接 UDP提供不可靠数据报分发的简单抽象。发送应用程序引导操作系统发送一组字节给远程应用程序。UDP数据报在IP包中发送给接收机。IP包可以在网络上丢失或延迟。若需要,重传丢失数据报由发送应用程序执行。UDP最适合于可容许一定数据丢失的应用程序。例如,多数多媒体应用程序使用UDP包流来传音频和视频内容,数据包丢失会降低接收机的音频或视频质量,但应用程序仍可播放。
4. IP地址
是一个32位数字(4个字节),分成网络号和主机号两部分,Internet的路由选择就是依据网络号来进行的,然后把包分给相应的网络,最后根据主机号来将包发送给相应的目的主机 IP地址是放在IP数据包的包头中的,包括源IP地址,目标IP地址,以及其他一些重要的信息。
5. 传输控制协议
传输控制协议(TCP)在一对应用程序之间协调数据传输,应用程序通过读写套接字(socket)通信,套接字将数据表示为有序可靠的字节流。
6. 套接字抽象
套接字抽象在两个应用程序之间提供可靠的双向信道通信,应用程序一般运行在不同机器上,这种抽象由传输控制协议(TCP)提供,一般由连接到internet的每台主机的操作系统实现这个协议。 应用程序通过创建套接字(很像打开一个文件)来使用TCP。两个端点都需要标识套接字的精确方法。知道两台机器的IP地址是不够的,单一机器可运行多个应用程序,单一应用程序(如WEB服务器)可有多个套接字,所以每个套接字与每个端点的一个端口号相关。端口号是16位整数,范围为 0-65535,1024以下的号码是众所周知的端口,它们保留给特定应用层协议(如HTTP的端口80),其余端口号(1024-65535)可由任意应用程序使用。
套接字由5块信息标识----两个IP地址(用于运行这两个应用程序的机器)、两个端口号(用于两个应用程序端点)和协议(TCP)
套接字 socket = (IP地址:端口号)
应用程序通过操作系统实现的系统调用来创建套接字。假设应用程序A(如WEB客户机)建立到远程应用程序B(WEB服务器)的一个套接字,应用程序A通过调用系统调用来初始化并创建套接字:
1)在UNIX操作系统中,socket()函数用于创建新套接字,
2)然后应用程序调用connect()将套接字与应用程序B的IP地址以及B的端口号关联上
3)在执行connect()调用中,操作系统也会为应用程序A选择未用的本地端口号(1024-65535),至此,运行应用程序A的操作系统知道了两个IP地址和两个端口号,它们惟一标识两个应用程序间的双向连接。
4)操作系统初始化到应用程序B的TCP连接
5)在建立连接后,connect()调用返回,应用程序A可开始向套接字读/写数据
然而在建立套接字时,应用程序B起初扮演消极作用。应用程序B在特定端口号监听创建连接的请求,在UNIX中,这包括创建套接字socket()和调用bind()来分配本地端口(如商品80),然后调用listen()函数,这表示B想等待远程应用程序的连接,这会启动操作系统响应来自这个端口的任何建立TCP连接的请求,应用程序通过调用accept()来获知这些新TCP连接,默认情形下,在新连接都不可用前,accept()调用一直处于等待状态,一旦连接可用,系统调用就会完成套接字的创建,至此应用程序B就可开始对套接字读/写数据了。
//一旦连接建立,应用程序就可读/写数据。实际上两个应用程序可同时读写,因为套接字提供双向通信信道。在WEB上,客户机(应用程序A)初始化通信,客户机将HTTp请求写到套接字里,服务器(应用程序B)等待数据到达它的套接字,数据一旦到达,服务器就从套接字读取HTTP请求。在处理请求后,服务器将HTTP响应写到套接字,同时客户机等待数据到达它的套接字,然后客户机从套接字里读取响应,这种模式就是典型的客户机/服务器应用程序。
操作系统处理两个应用程序之间建立逻辑连接和协调IP包传送的细节:
1)对于A,操作系统执行socket()和connect()函数,如果远程主机没有响应,操作系统通知应用程序A:请求创建套接字失败
2)对于B,操作系统执行socket(),bind(),listen()和accept()
3)在两个应用程序下面,操作系统协调包的发送和接收,创建有序可靠的字节流抽象
7. 滑动窗口流控制
TCP发送机限制数据的传输,以避免接收机缓冲空间的溢出。从理论上讲,无论何时应用程序写数据到套接字,TCP都可传输数据。但TCP限制数据的传输,主要因为:
1)发送机传输的数据量不应超过接收机缓冲区大小
2)发送机传输数据的速度不应超过网络的处理速度----发送太快可使网络超载,导致网络拥塞,增加通信延迟和丢失数据包的可能。每个TCP发送机使用滑动窗口流控制来限制传输数据
8. 重传丢失包
重传丢失包在TCP提供可靠传递字节流中有着重要作用。IP不通知TCP发送机关于包的丢失时间,相反,发送机必须根据接收机的响应(或缺少响应)来推断包已丢失。
9. TCP拥塞控制
TCP发送机根据滑动窗口调整数据传输,滑动窗口既依赖于接收机的可用缓冲空间,又依赖于网络可用带宽,这两个因素分别由接收机窗口和拥塞窗口表示。发送机根据这两个值中的最小值传输数据,避免接收机缓冲溢出,并防止网络拥塞。拥塞链路导致丢失IP包,在检测到包已丢失时,发送机会减少拥塞窗口大小,降低传输速率;在缺少包丢失的情况下,TCP发送机逐渐增大拥塞窗口,以便较快地传输数据。
10. 4种先于WEB出现的应用层协议
A.Telnet协议 Telnet允许用户连到远程机器的一个账号,用户的机器上运行的客户程序使用Telnet协议与远程机器上运行的服务器程序通信。
B.文件传输协议(FTP) FTP既允许用户复制文件到远程机器上,也允许用户从远程机器复制文件。客户机程序发送命令给服务器程序,代表用户协调两台机器之间的文件复制。
C.简单邮件传输协议(SMTP) SMTP支持传送电子邮件,SMTP用于将电子邮件消息从本地邮件服务器发送到远程邮件服务器,此外,SMTP也用于将电子邮件消息从用户邮件代理发送到本地邮件服务器
D.网络新闻传输协议(NNTP) NNTP支持电子新闻组相关文章的传送,用户代理使用NNTP与本地新闻服务器通信。
11. SSL
安全套接字层(SSL)是位于传输层与应用层之间的一种协议。SSL的主要目的是允许客户机使用TCP/IP协议作为它的传输层连接,以便将二进制数据安全地传送给能理解SSL的服务器。安全性源于一个经验证的客户机同服务器之间建立的加密连接。一条消息经加密后,只有知道如何解密的某一方才可以本机形式读取它,为了将明文转换成所谓的密文,需要使用一种算法以及一个密钥。 对于SSL协议来说,它的两个部分分别是记录和握手协议。握手协议类似于TCP的握手协议,其作用是建立连接。在握手阶段,会使用子协议来建立记录层,并用它对端点进行验证。在握手期间,数据交换时所采用的格式是由记录协议决定的,它负责处理必要的加密、压缩和重新组装。记录协议做出的重要贡献在于,通信双方可确信要交换的数据会被加密,同时消息的完整性会得到保持。记录协议将数据分割为不大于16KB的组块,同时可选择在压缩之后,再计算完整性检查,对结果进行加密,再随一个标头发送出去。消息自WEB服务器返回时,所经历的步骤正好相反:解密、检查完整性、可选的解压缩以及发送给另一方
12. 带宽优化
为节省带宽,人们研究出了各种方法。在这些方法中,资源会被转换、精简或根本不予发送。与充分利用带宽有关的、对HTTP的3种改动如下:
A.范围请求机制
如果不需要传输整个资源(如:用户只对资源的一部分感兴趣),就可节省带宽,协议需要扶持一些方式,以便指定资源的所需部分并传输给它们
在下载资源的过程中,由于某种原因,可能下到一半便中断连接了,然后,再次对该资源发送一次新请求,可这次请求又会导致下载全部资源(从头下载该资源),这就浪费了带宽并增加了等待时间。因此,需要有这么一种机制,可以支持对资源的某部分请求而不是全部。 在HTTP/1.1中引入了Range标头来指定本次请求所需要哪些部分资源,从而实现范围请求机制。
B.期望/继续机制
如果发送方预先知道接收方不能处理消息主体,发送方最好根本不要发送资源,可通过交换一些控制信息,对这种情况进行验证,从而避免传输不必要的数据
如果HTTP服务器不能处理大量请求(如:提交大型PUT或POST表单),在请求发出之前便让客户机知道这点是非常有用的。在发出大规模请求前,客户机若知道它的要求能被满足,这会使客户机从中受益。Expect(期望)机制提供了一个完美的解决方案。它允许客户机知道服务器是否能够满足有特定请求的客户机的期望。如果服务器能满足期望并处理请求,它可先通过只发送 100 Continue响应来告知客户机,并不发送响应体,客户机在接收到100 Continue响应后,可在已经打开的连接上继续发送(大型)实体主体;如果服务器不能处理请求时,会根据请求时特定情况发送恰当的响应码来告知客户机,例如:
1)如果请求过长,服务器会发送413 Request Entity Too Large
2)如果要禁止客户机发出这种请求,服务器会发送403 Forbidden。
3)如果服务器接收到没有料到的Expect权标,或者它知道上游服务器不能处理Expect机制,会发送417 Expectation Failed状态码响应
Expect机制是逐路段的,所有的期望最多只能寄托在下一路段上,而且如果下一个路段服务器不能满足期望,服务器就应返回 417 Expectation Failed状态码。然而,Expect请求标头是一个端到端标头,如下一路段服务器(例如代理)由于自己不能处理请求,从而转发了这一请求,那么Expect请求标头也必须同时发送
C.压缩技术
在发送前转换资源,然后在接收方重构它,发送前进行转换可有效地缩减资源的大小
在传送响应前,可考虑由原始服务器对其进行压缩,同样,在请求中包含大型实体主体的客户机也可压缩实体
13. 连接管理
使用TCP作为传输协议,要经过三次握手方式来建立连接(慢速的开始阶段),并用另外4个数据包关闭连接(4次握手), 建立连接和关闭连接是需要耗时间和资源的,对于HTTP消息交换中常见的短时连接【用完就关,后续请求又得重新建立连接】,使得TCP连接的利用没有得到充分的优化。
解决这一问题的方法就是让TCP连接保持持久性连接,后续请求可以继续使用,而不用重新再次建立TCP连接
A. HTTP/1.0的Connection:Keep-Alive机制
Keep-Alive的原理与HTTP/1.1中的持久连接相似。对保持连接打开感兴趣的客户机实际上会请求原始服务器不要关闭连接。具体实现如下:
GET /home.html HTTP/1.0
...
Connection: Keep-Alive
如果服务器也对保持连接打开感兴趣,它将发送如下响应: HTTP/1.0 200 OK ...
Connection: Keep-Alive
...
<响应主体>
然而,如果HTTP/1.0服务器正在传输动态的内容,正在接收的客户机在服务器不关闭连接的情况下,没有办法检测到响应的结束。因为等待计算内容长度时,会增加客户机的延迟时间,所以动态生成的内容通常不包括Content-Length标头
B.HTTP/1.1中的持久连接有3个主要目标:
1)减少TCP连接代价(进行较少的建立和拆除)
2)通过避免多个TCP慢速开始阶段而缩短延迟
3)避免浪费带宽,并减少总体拥塞
14. 消息传输
交换HTTP消息的一个重要目标是确保参与各方认识到它们已接收到了完整消息。响应的长度是一个非常有用的指示符,接收方据此可知道何时收到了完整的响应。HTTP/1.0原始服务器可用来指定实体主体长度的惟一机制便是通过 Content-Length字段。静态资源的长度可很容易地确定(通常执行一个操作系统调用即可),但是,对于动态生成的响应来说,为计算它的真实长度,只好等它完全生成。只有等整个响应都生成之后,才能正确地填写Content-Length标头字段。因此,除非这个标头字段被正确的填写,否则原始服务器是不会开始发送响应的,这便要求缓存整个响应,从而增大了最终用户的延迟。在HTTP/1.0中,服务器通过关闭连接来指出动态内容的结尾。如果关闭连接是指示响应结尾一惟一办法,那么持久连接便不可能实现了,所以在HTTP/1.1中,通过引入被称为组块(chunked)的传输编码方法,从而解决了安全传输消息的基本问题。
组块(chunked)的传输编码方法:该方法使发送方能将消息主体分割为任意大小的组块(chunk),并单独地发送它们。在每个组块的前面,都加上了那个组块的长度,使接收方可确保自己能够完整地接收这个组块。更重要的是,在最末尾的地方,发送方生成了长度为0的组块,接收方可据此判断整条消息都已安全地传输完毕。