对TCP/IP协议簇有些了解的同学们应该都知道,TCP/IP协议通过精简ISO网络7层协议(其实了解历史渊源的话,TCP/IP协议本来目的并不是简化ISO的7层协议,只是因为ISO协议簇制定速度慢于互联网通信技术发展的速度,TCP/IP协议已经被各大厂商私下广泛商用化,ISO网络通信标准才制定完成,所以,鉴于已经广泛应用的TCP/IP协议已经成为各大通信商的实际默认标准,ISO只能作为理论研究标准来供研究之用)来保证我们的网络通信的基础架构。
自己对HTTP协议的了解基本停留在大学计算机网络的了解程度,一直想深入了解点HTTP的细节,所以最近在拜读《HTTP完全解析》这本著作,觉得堪称HTTP解读经典,由于全书的内容太多,一边拜读一边做做笔记加深印象和理解。
1.TCP协议的贡献
HTTP协议是TCP/IP协议簇应用层的传输协议,Web浏览器、服务器和移动客户端应用层、服务器交互都是通过HTTP互相通信。HTTP协议是基于传输层可靠的数据传输协议TCP协议实现,而且HTTP无需操心网络通信的细节,它把联网的细节到交给通用、可靠的TCP/IP协议簇的应用层下面几层协议处理。顺便说下,TCP协议向HTTP协议屏蔽了数据传输的细节,TCP至少给HTTP协议的工作提供了一下保证:
- 无差错的数据传输;
- 按序传输(数据总是会按照发送的顺序到达);
- 未分段的数据流(可以在任意时刻以任意尺寸将数据发送出去)。
2.HTTP协议的基本结构
讨论HTTP协议的细节,我们离不开HTTP传输协议的基本数据单元,HTTP报文是HTTP应用之间发送的基本单位。HTTP报文都可以分为两类:请求报文(Request Message)和响应报文(Response Message)。如果大家对HTTP报文的基本结构还不是很清楚的话,转到《Http Message结构学习总结》,我觉得这部分看我的笔记肯定还不如他的通俗易懂。
3.HTTP连接管理和HTTP优化
我们知道HTTP协议直接紧挨着TCP协议,因为TCP为HTTP提供了一条可靠的比特传输通道,从TCP连接的一端填入的字节会从另一端已原来的顺序、正确地传送出来,让HTTP协议不用考虑传输的细节,可以集中尽力处理应用层的事情。那我们可以这样理解,HTTP传输的性能很大程度上直接取决于底层TCP通道的性能。我们只有更好的理解了TCP的某些性能特点之后,就可以更好的理解HTTP的连接优化特性,这样我们才可以考虑到我们的软件有更高的HTTP性能。下面说说TCP协议对HTTP性能的影响:
1.HTTP的时延
在 HTTP 请求的过程中会出现哪些网络时延,并以此开始我们的TCP 性能之旅。图 1 描绘了 HTTP 事务主要的连接、传输以及处理时延。
与因特网传输请求报文,以及服务器处理请求报文都需要时间。建立 TCP 连接,以及传输请求和响应报文的时间相比,事务处理时间可能是很短的。除非客户端或服务器超载,或正在处理复杂的动态资源,否则 HTTP时延就是由 TCP 网络时延构成的。
HTTP 事务的时延有以下几种主要原因:
客户端首先需要根据 URI 确定 Web 服务器的 IP 地址和端口号。如果最近没有对URI 中的主机名进行访问,通过 DNS 解析系统将 URI 中的主机名转换成一个 IP地址可能要花费数十秒的时间。
接下来,客户端会向服务器发送一条 TCP 连接请求,并等待服务器回送一个请求接受应答。每条新的 TCP 连接都会有连接建立时延。这个值通常最多只有一两秒钟,但如果有数百个 HTTP 事务的话,这个值会快速地叠加上去。
一旦连接建立起来了,客户端就会通过新建立的 TCP 管道来发送 HTTP 请求。数据到达时,Web 服务器会从 TCP 连接中读取请求报文,并对请求进行处理。因特网传输请求报文,以及服务器处理请求报文都需要时间。
然后,Web 服务器会回送 HTTP 响应,这也需要花费时间。
这些 TCP 网络时延的大小取决于硬件速度、网络和服务器的负载,请求和响应报文的尺寸,以及客户端和服务器之间的距离。TCP 协议的技术复杂性也会对时延产生巨大的影响。
2.性能聚集区域
- 本节其余部分列出了一些会对 HTTP 程序员产生影响的、最常见的 TCP 相关时延,其中包括:
- TCP 连接建立握手;
- TCP 慢启动拥塞控制;
- 数据聚集的 Nagle 算法;
- 用于捎带确认的 TCP 延迟确认算法;
- TIME_WAIT 时延和端口耗尽。
下面我们主要说说TCP连接的握手时延、延时确认、TCP慢启动。
1.TCP连接的握手延时
建立一条新的 TCP 连接时,甚至是在发送任意数据之前,TCP 软件之间会交换一系列的 IP 分组,对连接的有关参数进行沟通(参见图 2) 。如果连接只用来传送少量数据,这些交换过程就会严重降低 HTTP 的性能。
TCP 连接握手需要经过以下几个步骤。
- 请求新的 TCP 连接时,客户端要向服务器发送一个小的 TCP 分组(通常是 40 ~60 个字节) 。这个分组中设置了一个特殊的 SYN 标记,说明这是一个连接请求。
- 如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个TCP 分组,这个分组中的 SYN 和 ACK 标记都被置位,说明连接请求已被接受。
- 最后,客户端向服务器回送一条确认信息,通知它连接已成功建立。现代的 TCP 栈都允许客户端在这个确认分组中发送数据。
HTTP 程序员永远不会看到这些分组——这些分组都由 TCP/IP 软件管理,对其是不可见的。HTTP 程序员看到的只是创建 TCP 连接时存在的时延。
通常 HTTP 事务都不会交换太多数据,此时,SYN/SYN+ACK 握手会产生一个可测量的时延。TCP 连接的 ACK 分组通常都足够大,可以承载整个 HTTP 请求报文4,而且很多 HTTP 服务器响应报文都可以放入一个 IP 分组中去(比如,响应是包含了装饰性图片的小型 HTML 文件,或者是对浏览器高速缓存请求产生的 304 Not Modified 响应) 。
最后的结果是,小的 HTTP 事务可能会在 TCP 建立上花费 50%,或更多的时间。后面的小节会讨论 HTTP 是如何通过重用现存连接,来减小这种 TCP 建立时延所造成的影响的。
2.确认延时
由于因特网自身无法确保可靠的分组传输(因特网路由器超负荷的话,可以随意丢弃分组) ,所以 TCP 实现了自己的确认机制来确保数据的成功传输。每个 TCP 段都有一个序列号和数据完整性校验和。每个段的接收者收到完好的段时,都会向发送者回送小的确认分组。如果发送者没有在指定的窗口时间内收到确认信息,发送者就认为分组已被破坏或损毁,并重发数据。
由于确认报文很小,所以 TCP 允许在发往相同方向的输出数据分组中对其进行“捎带” 。TCP 将返回的确认信息与输出的数据分组结合在一起,可以更有效地利用网络。为了增加确认报文找到同向传输数据分组的可能性,很多 TCP 栈都实现了一种“延迟确认”算法。延迟确认算法会在一个特定的窗口时间(通常是 100 ~ 200 毫秒)内将输出确认存放在缓冲区中,以寻找能够捎带它的输出数据分组。如果在那个时间段内没有输出数据分组,就将确认信息放在单独的分组中传送。
但是,HTTP 具有双峰特征的请求 - 应答行为降低了捎带信息的可能。当希望有相反方向回传分组的时候,偏偏没有那么多。通常,延迟确认算法会引入相当大的时延。根据所使用操作系统的不同,可以调整或禁止延迟确认算法。
在对 TCP 栈的任何参数进行修改之前,一定要对自己在做什么有清醒的认识。TCP中引入这些算法的目的是防止设计欠佳的应用程序对因特网造成破坏。对 TCP 配置进行的任意修改,都要绝对确保应用程序不会引发这些算法所要避免的问题。
2.TCP慢启动
TCP 数据传输的性能还取决于 TCP 连接的使用期(age) 。TCP 连接会随着时间进行自我“调谐” ,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐被称为 TCP 慢启动(slow start) ,用于防止因特网的突然过载和拥塞。
TCP 慢启动限制了一个 TCP 端点在任意时刻可以传输的分组数。简单来说,每成功接收一个分组,发送端就有了发送另外两个分组的权限。如果某个 HTTP 事务有大量数据要发送,是不能一次将所有分组都发送出去的。必须发送一个分组,等待确认;然后可以发送两个分组,每个分组都必须被确认,这样就可以发送四个分组了,以此类推。这种方式被称为“打开拥塞窗口” 。
由于存在这种拥塞控制特性,所以新连接的传输速度会比已经交换过一定量数据的、“已调谐”连接慢一些。由于已调谐连接要更快一些,所以 HTTP 中有一些可以重用现存连接的工具。
3.HTTP连接性能优化
针对TCP传输过程中的报文时延,有一些优化处理办法的办法来提供HTTP的连接性能:
1.并行连接
比如:我们的浏览器请求服务器资源的HTML页面,HTML页面包含很多文字和多个图片资源,如果我们对文字和图片都一个一个请求,页面加载效果实在太慢。
目前主流浏览器的解决办法是多个线程发起HTTP请求处理,并行的执行多个事务。
但是,并行连接并不是越多越好,并行连接可能可以提高页面的访问速度,但是并行连接不一定一直都是更快。可能还有很多其他的制约因素,比如:客户端的网络宽带不足,在低速率的网络环境下,一个HTTP事务可能就已经耗尽了网络的带宽。
而且,打开大量连接会消耗很多内存资源,从而引发性自身的性能问题。所以,目前的主流浏览器虽然使用了并行连接,但是他们对并行连接的总数都限制在一个较小的数字
2.持久连接
我们的客户端(App 或者 web浏览器)经常打开到同一个站点的连接。比如:一个Web页面上的大部分内嵌图片通常都来自同一个Web站点,而且相当一部分指向其他对象的超链接通常都指向同一个站点。因此,初始化对某个服务器的HTTP请求的应用程序很可能会在不久的将来对那台服务器发起更多的请求(比如:获取图片),这种性质叫站点本地性。
因此,HTTP/1.1(以及HTTP/1.0的各个增强版本)允许HTTP设备在事务处理结束之后将TCP连接保持在打开状态,以便为未来的HTTP请求重用现存的连接。在事务处理结束之后仍然保持在打开状态的TCP连接称为持久连接。非持久连接会在每个事务结束后关闭。持久连接会在不同事务之间保持打开状态,直到客户端或者服务器决定将其关闭为止。
重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地经行数据的传输。
关于持久连接的好处,下面引用原著的一幅图来说明:
从图我们知道,简单的HTTP串行连接上实现4个HTTP事务的时间线与在一条持久连接上实现同样事务所需的时间线进行了比较。由于去掉进行连接和关闭连接的开销,所以时间线有所缩短。
4.HTTP协议连接处理
客户端请求时,给 HTTP请求头加入”Connection: Keep-Alive”属性,开启keep-alive机制,通过使用keep-alive机制,可以减少TCP连接建立次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高服务器的吞吐率(更少的TCP连接意味着更少的系统内核调用)。
下面说说下HTTP/1.0 和HTTP/1.1 下的keep-Alive的差别:
HTTP/1.0
在HTTP/1.0版本中,并没有官方的标准来规定Keep-Alive如何工作,因此实际上它是被附加到HTTP/1.0协议上,如果客户端浏览器支持Keep-Alive,那么就在HTTP请求头中添加一个字段 Connection: Keep-Alive,当服务器收到附带有Connection: Keep-Alive的请求时,它也会在响应头中添加一个同样的字段来使用Keep-Alive。这样一来,客户端和服务器之间的HTTP连接就会被保持,不会断开(超过Keep-Alive规定的时间,意外断电等情况除外),当客户端发送另外一个请求时,就使用这条已经建立的连接。
HTTP/1.1
在HTTP/1.1版本中,官方规定的Keep-Alive使用标准和在HTTP/1.0版本中有些不同,默认情况下所在HTTP1.1中所有连接都被保持,除非在请求头或响应头中指明要关闭:Connection: Close ,这也就是为什么Connection: Keep-Alive字段再没有意义的原因。
1.Keep-Alive细节
说说两点Keep-Alive模式的一些细节,让我们对Kepp-Alive模式有更详细的了解:
1. Keep-Alive模式下,客户端如何判断请求所得到的响应数据已经接收完成?
1.使用消息首部字段Conent-Length
故名思意,Conent-Length表示实体内容长度,客户端(服务器)可以根据这个值来判断数据是否接收完成。
2.使用消息首部字段Transfer-Encoding(chunk:分块)
当客户端向服务器请求一个静态页面或者一张图片时,服务器可以很清楚的知道内容大小,然后通过Content-length消息首部字段告诉客户端需要接收多少数据。但是如果是动态页面等时,服务器是不可能预先知道内容大小,这时就可以使用Transfer-Encoding:chunk模式(分块传输)来传输数据了。即如果要一边产生数据,一边发给客户端,服务器就需要使用”Transfer-Encoding: chunked”这样的方式来代替Content-Length。
2. Keep-Alive握手需要客户端和服务器一起说的才算吗?
如果服务器愿意为下一条请求在连接保持在打开状态,就在相应中包含相同的首部。如果相应中没有Connection:Keep-Alive首部,客户端就认为服务器不支持keep-alive,会在发送相应报文之后关闭连接。
还有当服务器给客户端报文包括 Connection:close首部,表示服务器告诉客户端要关闭连接。
2.Keep-Alive选项
Keep-Alive首部只是请求将连接保持在活跃状态。发出keep-Alive请求之后,客户端和服务器并不一定会同意进行keep-alive会话。他们可以在任意时刻关闭空闲的Keep-Alive连接,并可随意显示Keep-Alive连接所处理事务的数量。
可以用Keep-Alive通用首部中制定的、由逗号分隔的选项来调节Keep-Alive的行为。
- 参数timeout是在Keep-Alive响应首部发送的。它估计了服务器希望将连接保持在活跃状态的时间。这并不是一个承诺值。
- 参数max是在Keep-Alive响应首部发送的。它估计了服务器还希望为多少个事务保持此连接的活跃状态。这并不是一个承诺值。
- Keep-Alive首部还可支持任意未经处理的属性,这些属性主要用于诊断和调试。语法为name [=value].
以上部分为本人学习HTTP的报文和连接的笔记记录,关于HTTP与Cookie机制深入理解,推荐大家转到这篇文章《细说Cookie》。
解析HTTP、 Socket、 WebSocket等几个概念:
Socket是什么呢?
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。就是我们经常说的套接字,在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
实际上,传输层的TCP是基于网络层的IP协议的,而应用层的HTTP协议又是基于传输层的TCP协议的,而Socket本身不算是协议,就像上面所说,它只是提供了一个针对TCP或者UDP编程的接口。
HTTP 协议跟Socket的最大的区别
HTTP 你不是你要,服务器怎么知道你要呢,所以,你要跟服务器说撒(request)。 HTTP是健忘鬼,刚还在问服务器伸手(request)要东西,要完马上就完了。
Socket 客户端和服务是双向连接,一次建立连接,除非主动断交,不然就双方记住了。
举个例子:比如我们实现一个简单的聊天室程序:如果我们用HTTP实现,最大难题HTTP太健忘(无状态特性),请求一次下次就不记得上次干了啥,会话状态很难保证;而且,我们如果HTTP是单向请求,我们客户端需要不断跟服务器轮询请求,是否有人跟我聊天,说了啥,首先做不到实时聊天,而且,轮询还需要考虑资源、流量问题,还有想要实现群聊更是灾难。以上遇到的问题,恰恰是Socket的长处。
WebSocket又是什么?
WebSocket protocol 是HTML5一种新的协议,跟HTTP协议基本没有关系,主要是为了兼容现有浏览器的握手规范而已。Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。
WebSocket 是基于 TCP 协议;HTTP 也是基于 TCP 协议。WebSocket连接要保持还是关闭是由你服务器应用控制说的算。WebSocket还是一个全双工的协议,支持服务器端主动给客户端发送消息。
HTML5为什么引入WebSocket协议呢?一方面为了解决HTTP协议无状态、无连接、单向请求不好处理,个人觉得WebSocket引入很大一部分就是为了解决客户端与服务端实时通信。举个例子:如果用HTTP来实现网页的通知消息功能,我们只能用轮询的方式不断向服务器发送请求来处理,然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源。而WebSocket协议建立后,服务器就可以主动向客户端推送消息。 比如,我们以前实现在线聊天室的功能的话,会话状态怎么保存,怎么知道对方有没发送聊天信息,群聊怎么处理都是很难处理的问题,WebSocket协议的出现,实现在线聊天室的功能就简化许多。PS:有一些移动端App实现的实时聊天功能就是用WebSocket协议来通信。
因此,我们从功能上可以理解,WebSocket更像是Socket功能实现,但实际跟Socket协议没有关系。此处补充下,Socket协议支持TCP和UDP两种方式,而WebSocket只支持TCP。
转载请注明出处:http://blog.csdn.net/Johnnyz1234/article/details/47859563