一. 解析域名地址为IP地址
浏览器DNS缓存:以Chrome为例,在浏览器窗口中输入chrome://net-internals/#dns,就可以查看当前浏览器DNS缓存记录,chrome的DNS缓存过期时间还是比较短的,大约为1分钟。
本机DNS缓存:在Windows命令行模式下输入ipconfig /displaydns,就可以查看本机DNS缓存记录。许多主机在启动时从本地域名服务器下载域名和地址的全部数据库,维护存放自己最近使用的域名的缓存。
本地域名服务器:在Windows命令行模式下输入ipconfig /all,就可以查看本地DNS服务器的IP地址,一般而言本地域名服务器都是由ISP提供,主机通过UDP和本地域名服务器通信。如果在本地域名服务器高速缓存中搜索不到要转换的域名,就向更高级别的域名服务器发起迭代查询,按根域名服务器(一般查询根域名服务器都是遵循就近原则,中国有3个根服务器,位置分别为北京,香港,台北)-> 顶级域名服务器-> 权限域名服务器的顺序查询。假如本地域名服务器高速缓存中存在顶级域名服务器.com的IP地址,那么本地域名服务器可以不向根域名服务器进行查询,而是直接向com顶级域名服务器发送UDP请求报文,这样就可以大大减轻根域名服务器的负荷。维护本地域名服务器数据库的主机自然应该定期地检查域名服务器以获取新的映射信息,而且主机必须从缓存中删掉无效的项。
二. TCP 连接建立
由上一步获取www.cnblogs.com的IP地址42.121.252.58后,客户端主机就会选择一个未使用的端口与42.121.252.58:80通信,在Windows命令行模式下输入netstat,就可以查看当前正在活动的TCP连接。
Client首先发送一个连接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个连接请求或连接接受报文,同时表示这个数据报不能携带数据,seq = x 表示Client自己的初始序号。
Server监听到连接请求报文后,如同意建立连接,则向Client发送确认。TCP报文首部中的SYN 和 ACK都置1 ,ack = x + 1表示期望收到对方下一个报文段的第一个数据字节序号是x+1,同时表明x为止的所有数据都已正确收到,seq = y 表示Server 自己的初始序号。
Client收到确认后还需再次发送确认,同时携带要发送给Server的数据。ACK 置1 表示确认号ack= y + 1 有效,Client自己的序号seq= x + 1。
三. 浏览器给服务器发送一个http请求
上一步中有提到Client收到确认后还需再次发送确认,这时就可以携带要发送给Server的数据,这个数据就是HTTP请求报文。
HTTP请求行:GET http://www.cnblogs.com/ HTTP/1.1
HTTP请求首部:
Accept: | text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
Accept-Charset: | GBK,utf-8;q=0.7,*;q=0.3 |
Accept-Encoding: | gzip,deflate,sdch |
Accept-Language: | zh-CN,zh;q=0.8 |
Connection: | keep-alive |
Cookie: | 省略 |
Host: | www.cnblogs.com |
If-Modified-Since: | Mon, 22 Apr 2013 09:12:11 GMT |
User-Agent: | Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.21 (KHTML, like Gecko) Chrome/25.0.1354.0 Safari/537.21 |
这里重点提一下Connection。
Connection设置为keep-alive,就是Server在发送响应后仍然在一段时间内保持这条连接,使同一Client可以继续在这条连接上传送后续的HTTP请求报文和响应报文。对于不同的web服务器,如IIS,Apache,都有不同的keep-alive 过期时间,当然如果过期时间太短,就达不到重用连接的效果,如果过期时间太长,则会造成占用资源的坏处。
四. 服务器给浏览器发送一个http响应
当Server接收到http请求的时候,就把请求交给专门的web服务器处理,并最终把数据发送给Client,这个数据就是HTTP响应报文。
HTTP状态行:HTTP/1.1 200 OK
HTTP响应首部:
Cache-Control: | public, max-age=62 |
Connection: | keep-alive |
Content-Encoding: | gzip |
Content-Type: | text/html; charset=utf-8 |
Date: | Wed, 24 Apr 2013 08:14:03 GMT |
Expires: | Wed, 24 Apr 2013 08:15:04 GMT |
Last-Modified: | Wed, 24 Apr 2013 08:13:04 GMT |
Server: | Tengine |
Transfer-Encoding: | chunked |
Vary: | Accept-Encoding |
X-AspNet-Version: | 4.0.30319 |
X-AspNetMvc-Version: | 3.0 |
X-Powered-By: | ASP.NET |
X-UA-Compatible: | IE=edge |
最后就是HTTP响应主体内容--html文档了,如果需要继续获取嵌套在html中的对象,且这些对象都存在于同一个服务器上时,这些HTTP请求就会重用现存TCP连接。
五. TCP 连接释放
在正常情况下,服务器端的keep-alive过时了,就会主动发出请求释放这条TCP连接。
Server发送一个连接释放报文,FIN = 1 表示Server的数据已发送完毕,seq = v ,v等于前面已传送过的数据的最后一个字节加1。
Client收到Server的连接释放请求后,发送一个确认报文。ACK = 1 表示确认后ack有效,ack = u+1表示期望收到对方下一个报文段的第一个数据字节序号是u+1 ,seq = v , v等于前面Client已传送过的数据的最后一个字节加1。
由于HTTP协议是基于请求-响应模型,所以这时Client再发送请求数据给Server已经无效了,因为Server到Client的TCP连接已关闭,不会再发送响应了。这里的Close-Wait大概是等待主机通知关闭这次TCP连接。
接下来Client就发送一个连接释放给Server,FIN=1,ACK=1,ack = u + 1 与之前发送给Server的确认号一样。这里的seq = v 也与之前的一样。(个人认为Close-Wait期间Client不会再发送数据给Server,所有数据序号并没有发生改变,正确与否有待考证)
Server收到连接释放报文后,便发送一个确认报文。然后进入Time-Wait,而不是立即关闭连接,原因是不保证这个确认报文没有丢失,而Client收不到确认报文则执行超时重传FIN+ACK,这时Server还未关闭,就可以重传ACK。