从输入URL到网页呈现的过程大致如下:
- DNS解析
- 建立tcp连接
- 客户端发送HTPP请求
- 服务器处理请求
- 服务器响应请求
- 浏览器展示HTML
- 浏览器发送请求获取其他在HTML中的资源。
用户单击鼠标后所发生的事件按顺序如下(以访问清华大学的网站为例)
- 浏览器向DNS请求解析www.tsinghua.edu.cn的IP地址。
- 域名系统DNS解析出清华大学服务器的P地址。
- 浏览器与该服务器建立TCP连接(默认端口号为80)。
- 浏览器发出HTTP请求:GET/chn/index.htm6)
- 服务器通过HTP响应把文件 index htm发送给浏览器。
- 释放TCP连接。
- 浏览器解释文件 index. htm,并将Web页显示给用户。
DNS解析
域名服务器分类,由高到低为四类:
- 根域名服务器
- *域名服务器
- 权限域名服务器
- 本地域名服务器
域名解析是指把域名映射成为IP地址或把IP地址映射成域名的过程。前者称为正向解析,后者称为反向解析。
当客户端需要域名解析时,通过本机的DNS客户端构造一个DNS请求报文,以UDP数据报方式发往本地域名服务器。
域名解析有两种方式:递归查询和递归与迭代相结合的查询。
递归查询的过程,本地域名服务器只需向根域名服务器査询一次,后面的几次查询都是递归地在其他几个域名服务器之间进行的[步骤③~⑥]。在步骤⑦中,本地域名服务器从根域名服务器得到了所需的IP地址,最后在步骤⑧中,本地域名服务器把查询结果告诉发起査询的主机。由于该方法给根域名服务造成的负载过大,所以在实际中几乎不使用。
常用递归与迭代相结合的查询方式如图所示,该方式分为两个部分。
1.主机向本地域名服务器的查询采用的是递归查询
也就是说,如果本地主机所询问的本地域名服务器不知道被查询域名的IP地址,那么本地域名服务器就以DNS客户的身份,向根域名服务器继续发出查询请求报文(即替该主机继续查询),而不是让该主机自己进行下一步的查询。两种查询方式的这一步是相同的。
2.本地域名服务器向根域名服务器的查询采用迭代查询
当根域名服务器收到本地域名服务器发出的迭代査询请求报文时,要么给出所要查询的I地址,要么告诉本地域名服务器:“你下一步应当向哪个*域名服务器进行查询”。然后让本地域名服务器向这个*域名服务器进行后续的查询。同样,*域名服务器收到查询报文后,要么给出所要查询的IP地址,要么告诉本地域名服务器下一步应向哪个权限域名服务器查询。权限域名服务器一定可以得到当前域名对应的IP地址,最后,知道所要解析的域名的IP地址后,把这个结果返回给发起查询的主机。
解释:假定某客户机想获知域名为xyz.com主机的IP地址,域名解析的过程(共使用了8个UDP报文)如下:
①客户机向其本地域名服务器发出DNS请求报文(递归查询)。
②本地域名服务器收到请求后,查询本地缓存,若没有该记录,则以DNS客户的身份向根域名服务器发出解析请求报文(迭代查询)
③根域名服务器收到请求后,判断该域名属于com域,将对应的*域名服务器dns.com的IP地址返回给本地域名服务器。
④本地域名服务器向*域名服务器dns.com发出解析请求报文(迭代查询)。
⑤*域名服务器dns.com收到请求后,判断该域名属于abc.com域,因此将对应的授权域名服务器dns.abc.com的P地址返回给本地域名服务器。
⑥本地域名服务器向授权域名服务器dns.abc.com发起解析请求报文(迭代查询)
⑦授权域名服务器dns.abc.com收到请求后,将查询结果返回给本地域名服务器。
⑧本地域名服务器将査询结果保存到本地缓存,同时返回给客户机。
建立TCP连接
HTTP协议是使用TCP协议作为其传输层协议的,在拿到服务器的IP地址后,浏览器客户端会与服务器建立TCP连接。该过程包括三次握手:
第一步:客户机的TCP首先向服务器的TCP发送连接请求报文段。这个特殊报文段的首部中的同步位SYN置1,同时选择一个初始序号seq=x。TCP规定,SYN报文段不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入SYN-SENT(同步已发送)状态。
第二步:服务器的TCP收到连接请求报文段后,如同意建立连接,则向客户机发回确认,并为该TCP连接分配缓存和变量。在确认报文段中,把SYN位和ACK位都置1,确认号是ack=x+1,同时也为自己选择一个初始序号seq=y。注意,确认报文段不能携带数据,但也要消耗掉一个序号。这时,TCP服务器进程进入 SYN-RCVD(同步收到)状态。
第三步:当客户机收到确认报文段后,还要向服务器给出确认,并为该TCP连接分配缓存和变量。确认报文段的ACK位置1,确认号ack=y+1,号seq=x+1。该报文段可以携带数据,若不携带数据则不消耗序号。
成功进行以上三步后,就建立了TCP连接,接下来就可以传送应用层数据。TCP提供的是全双工通信,因此通信双方的应用进程在任何时候都能发送数据。另外,值得注意的是,服务器端的资源是在完成第二次握手时分配的,而客户端的资源是在完成第三次握手时分配的,这就使得服务器易于受到SYN洪泛攻击。
客户端发送HTTP请求
浏览器构建http请求报文,并通过TCP协议传送到服务器的指定端口。http请求报文一共包括三个部分:
请求行:指定http请求的方法、url、http协议版本等
请求头:描述浏览器的相关信息,语言、编码等。
请求体:当发送POST, PUT等请求时,通常需要向服务器传递数据。这些数据就储存在请求正文中。
服务器处理HTTP请求
服务器处理http请求,并返回响应报文。响应报文包括三个部分:
客户端页面渲染
浏览器接受到http服务器发送过来的响应报文,并开始解析html文档,渲染页面。具体的渲染过程包括:构建DOM树、构建渲染树、定位页面元素、绘制页面元素等。
断开TCP连接
客户端与服务器四次挥手,断开tcp连接。
TCP连接释放的过程通常称为四次握手
第一步:客户机打算关闭连接时,向其TCP发送连接释放报文段,并停止发送数据,主动关闭TCP连接,该报文段的终止位FN置1,序号seq=u,它等于前面已传送过的数据的最后一个字节的序号加1,FIN报文段即使不携带数据,也消耗掉一个序号。这时,TCP客户进程进入FNWAIT-1(终止等待1)状态。TCP是全双工的,即可以想象为一条TCP连接上有两条数据通路,发送FIN的一端不能再发送数据,即关闭了其中一条数据通路,但对方还可以发送数据。
第二步:服务器收到连接释放报文段后即发出确认,确认号ack=u+1,序号seq=v,等于它前面已传送过的数据的最后一个字节的序号加1。然后服务器进入 CLOSE-WAIT(关闭等待)
第三步:若服务器已没有要向客户机发送的数据,就通知TCP释放连接,此时其发出FIN=1的连接释放报文段。设该报文段的序号为w(在半关闭状态服务器可能又发送了一些数据),还须重复上次已发送的确认号ack=u+1。这时服务器进入 LAST-ACK(最后确认)状态。
第四步:客户机收到连接释放报文段后,必须发出确认。把确认报文段中的确认位ACK置1,确认号ack=w+1,序号seq=u+1。此时TCP连接还未释放,必须经过时间等待计时器设时间2MS(晶长据立段命)后,客户机才讲入 CLOSED(连接关闭)状态。
补充:
为什么不采用“两次握手”建立连接呢?
这主要是为了防止两次握手情况下已失效的连接请求报文段突然又传送到服务器而产生错误。
考虑下面这种情况。客户A向服务器B发出TCP连接请求,第一个连接请求报文在网络的某个结点长时间滞留,A超时后认为报文丢失,,是再重传一次连接请求,B收到后建立连接。数据传输完毕后双方断开连接。而此时,前一个滞留在网络中的连接请求到达服务器B,而B认为A又发来连接请求,此时若使用“三次握手”,则B向A返回确认报文段,由于是一个失效的请求,因此A不予理睬,建立连接失败。若采用的是“两次握手”,则这种情况下B认为传输连接已经建立,并一直等待A传输数据,而A此时并无连接请求,因此不予理睬,这样就造成了B的资源白白浪费。
为何不采用“三次握手”释放连接且发送最后一次握手报文后要等待2MSL的时间呢?
原因有两个:
保证A发送的最后一个确认报文段能够到达B。如果A不等待2MSL,若A返回的最后确认报文段丢失,则B不能进入正常关闭状态,而A此时已经关闭,也不可能再重传。
防止出现“已失效的连接请求报文段”。A在发送最后一个确认报文段后,再经过2MSL可保证本连接持续的时间内所产生的所有报文段从网络中消失。造成错误的情形与下文(疑难点6)不采用“两次握手”建立连接所述的情形相同。注意:服务器结束TCP连接的时间要比客户机早一些,因为客户机最后要等待2MSL后才可进入 CLOSED状态。