建立连接
上面我们通过DNS访问得到了目的地的IP地址,接下来,是我们如何向其发送数据了,比如我们的要获得到百度的首页,我们是如何访问的到的,本篇文章,将从拿到IP到页面展示在我们面前,从应用层到链路层进行一个分析。
首先来一个整体的架构,根据这个架构,我们再去完善我们在访问的过程中走的每一步。
我们和百度建立的连接的过程,我们中间经过若干个路由器,然后数据返回到我们本地,我们便可以看到百度首页了。
我们在浏览器中输入百度的链接
上一篇文章中讲了如何抽取url,然后将其交给操作系统,然后获取IP地址的,所以这里我们只考虑如何从ip地址出发获取到百度首页。
我们走了那些路由
首先要讲的是tracetroute,通过这个命令我们可以看得到我们在通向百度的服务器的时候,我们中间经过的路由有哪些?先看测试效果,一会再讲其原理。
开始通过traceroute得到的是一堆的星,完全看不出,其经过了那些路由,在windows上就不会出现这种情况,通过查资料知道,原因是在Unix和Linux系统上,traceroute的默认实现是通过TCP或者UDP,而windows是通过ICMP,通过ICMP实现我们就可以获得路由的完整路径,通过对其指定一个参数tracerooute -i+add,我们得到如下返回结果。
通过对于路由器IP的分析,我们可以发现第一跳通过我们的局域网路由,然后通过杭州教育网,然后杭州联通,到北京联通,之后再到北京移动,最后结束。
看完演示结果,说下原理了,其采用的是ICMP协议,属于网络层协议,网络控制报文协议,当我们的在寻址,不可达时,返回给我们的都是ICMP报文,我们熟悉的ping命令,也是通过该协议来实现的,通常,我们操作系统发送一个ICMP回显请求的命令,然后目的地返回给我们一个0类型的回显应答报文。回到我们的traceroute,这个是如何实现的呢?也是借助了ICMP协议,先看下这个协议具体有哪些类型吧。
当进行traceroute的时候,我们主机发送一系列到目的地的IP数据报,其中包含一个不可到达端口号的UDP数据报,然后我们给每一个数据报设置不同的寿命,寿命逐个增加,这个在后面对于IP数据报中会有详解,这里只需知道,这个寿命值,每当我们经过一个路由器会减1即可,此时,我们不断的发送数据报,因为我们给每一个包设置了不同的寿命值,所以当其经过一个路由的时候,路由检测到其已经过期,便会返回给我们一个ICMP包,为11类型的ICMP包,在我们发的每一个包的时候,我们的本地也会启动一个定时程序,ICMP包携带者路由器的名字和IP回来,这个时候,我们就可以知道到这个路由器的往返时间和路由器名字,接下来要面对的一个问题是如何让主机停止发送呢?因为我们携带的是一个不可达端口,所以当我们的最终到达目的地主机的时候,这个时候返回的不再是11类型的ICMP包,而是一个3类型的不可达ICMP包,这个时候,我们知道了要如何得到已经到达了终点,因此停止发送。一个traceroute的过程就完成了。
我们发送出去了什么
我们通过chrome来进行对百度的访问,然后通过wireshark抓包进行分析,我们可以得到
可以看出,有一个get包,一个返回的数据报,有一个我们本地局域网内路由IP地址,和一个我们访问的百度的IP地址,接下来,我们对包从上到下进行一个分析,通过包的格式内容。我们发送了什么,所以我们先要分析的是HTTP请求报文。常规的HTTP请求报文格式如下图。
通过抓取得到的是
我们此get请求方法,使用HTTP1.1版本,采用长连接的方式,接受的内容有图片,html,xml,图片等,我们本地主机的信息是关于我们浏览器的信息和主机的详细信息都有,然后是我们所能够接收的语言是中文和英文。
然后,我们继续向下探索到达我们的运输层了此处采用的是TCP,先看下标准的TCP结构.
这里的数据偏移也就是我们所说的数据长度。其中的紧急数据指针是设计之初,想到我们在网络中发送的数据,给其一个不同的优先级,但是后来,由于路由器不支持原因,没有得到实施。
再看下我们得到抓到数据包是什么样子的?
源端口号是59569
目的端口80
TCP段长度681
序列号
下一个序列号682
确认号1(defalut)
头部长度20bytes
窗口长度8192
校验和
紧急指针
对于TCP中的窗口的作用是用来进行拥塞和流量控制,和序列号的问题会在后面继续讲,还有TCP连接建立前的三次握手。
现在我们只是从高到底各个层次进行一个全局的体系,对于具体细节,此处还不细究。运输层的下一层协议网络层,网络层中的数据区包含着我们的TCP包,继续向下看我们的网络层协议的报文格式标准为
然后在看下我们抓取到的包进行一个比较
版本号 4
首部长度 20
服务类型
总长度 721
16位标示
三位标记
偏移量
TTL 生命周期
运输层协议 TCP
校验和(这个是对我们的报文中进行的一个求和,然后溢出后,进行回卷,然后再进行取反运算)
32位源地址
32位目的地址
数据报中有GEOIP目的地和源地址的,通过名字应该可以猜的出来是Geolocation IP,基于IP查询的地理位置。
然后数据包中包含我们的TCP协议包,现在就开始准备向外发送了。
发送出去的时候,我们的包要转化为链路层的帧结构,我们的帧结构是这样的
这个时候还存在一个问题,就是我们的帧大小是有限制的,就是1500,但是我们的ip数据报可能超过了这个界限,如何进行处理呢?因为这个要涉及到我们在端系统接收的时候要进行一个重组,IP设计者也是很早就考虑好了这个问题,让我们再回到我们的IP数据报中,在头部中有一个16位标志,3位标志,还有一个13位的偏移量,IP数据包的划分就是根据这几个参数来进行的一个端系统重组,这个过程是如何进行的呢?首先是一个16位的标志,这个是我们每一个段的一个标记,也就是说,即使我们的包被划分成了多个段,我们仍然还是将其进行一个划分,然后是一个3位的标志位,如果我们发送的数据报的为该分片的最后一个其值为0,否则为1,根据这个来确定,我们的值是否已经重传完成了,然后后面的偏移量是以8字节为单位的,我们通过这个对过来的数据包进行一个排序。
【IP地址和MAC地址问题】
帧中封装的是我们的源mac地址和我们目的主机的mac地址,还是第一跳路由的MAC地址呢,答案是我们的上一跳路由地址。对于帧中国的mac地址可能有疑问的地方是在我们对于,为什么不可以直接通过一个IP进行去找到,为什么还要用MAC地址呢?前面提到过,IP是帮助我们在路由寻址的时候,能够帮助我们锁定一个区域范围,然后提升查找的速度,当我们将范围到了我们要找的位置之后,我们就会出现的一个问题是,这个时候IP并不能够再起作用了,因为我们再去交付的时候,我们要交给一个网卡,路由只是通过IP地址的计算找到具体怎么走,然后在走到下一个目的地的时候,就需要我们的链路层发挥作用,通过其mac地址,去找,如果是这个mac地址的,那么它就接收下来,要考虑的一个问题是直接通过IP不也是可以的吗?对的,但是我们MAC地址最好的之处是帮助我们记录了我们在每一跳路由的信息,也就是我们的下一跳路由的信息,如果我们在数据报中添加下一跳字段,也就没有必要去做这个了。当我们最开始开机的时候,我们是没有IP地址的,如果没有MAC地址的话,那么我们在再去探寻IP地址之后,就回不来了,如果能够记录上一跳的话,那么其倒是还可以回来的。
这三个过程完成了,似乎明白了怎么回事,但是这个过程的转换是如何在操作系统中实现的呢?声明一点,对于其中的TCP的连接还有三次握手,这里没有做介绍。操作系统如何将三个环节连接的起来的呢?这就涉及到我们的socket编程了,这里浏览器通过socket编程实现,在我们建立socket的时候,我们是需要根据源和目的地的IP地址,进行建立的,这个时候,我们拿到DNS解析出来的IP和我们本地的IP进行了建立一个socket,通过将其包装成一个IP包,然后进入网络中,进行寻路,然后寻到目的子网的局域网后,在局域网内通过MAC地址或者是子网IP找到我们的.
我们又收到了什么
现在,看下我们收到了什么?
此时我们从低向上来,链路层帧通过MAC地址找到我们主机,然后我们的开始对该帧数据进行处理,首先是将帧解开,此时IP数据报也就不再起作用了。其作用的就是我们的运输层,网络层在路由转发的时候其作用,链路层在找到具体主机的时候发挥作用,运输层在数据到达的时候分发给我们的应用的缓冲区的时候发挥作用,这个缓冲区也就是我们的socket标示的。每个连接持有一个socket,根据端口转发给我们的socket,然后根据包中的内容找到具体连接的socket标示缓冲区。然后提供一个HTTP的数据报给我们的浏览器,我们的浏览器进行标签解析,渲染。得到我们的网页。
TCP和IP层还有链路层基本没有什么可讲了,基本是源和目的地做一个交换,这个时候有一个问题是,在TCP中还存在一个问题是,关于确认的问题,还有序列号的问题,上图
对于确认号没有什么问题,对于序列号,存在一个问题,为什么我们的数据是第一次发,我们的序列号不是1或者而是这个呢?为什么呢?原因在于如果我们从0开始,如果一个段不够长,那么另一个段和其出现在了同一个窗口,就会出现一个问题是,可能会出现确认号返回重复的问题,这样就出现了数据丢失,而我们的却毫无察觉,因此其采用的措施是随机生成一个序列号,然后在该序列号作为起始,然后在其后面追加。
再来看下我们的http响应报文
标准的HTTP响应报文包括以下几个属性
HTTP/1.1 200 ok
Connecton:
Date:
Server:
Last-Modfied:
Content-Length:
Content-type:
data + data + data
首先是状态,然后是告诉客户端,这次数据发送完,是否断开连接,然后是日期,这个日期是我们的服务器发送出数据之后的时间,然后是我们的服务器种类,之后是我们的最后修改日期,这个是我们服务器中数据的最后修改日期,之后是数据长度和类型。
我们抓取到的是
我们报文中涉及到了一些关于cache的字段,还有就是我们状态码返回的是302,而不是200,表示的是我们的访问的URL,暂时被重定位到了另一个地址,然后在下面的Location中包含了这个地址,之后,我们在下一次的访问中将会对这个提供的地址进行访问。
说到缓存,我们主机本地有缓存,还有就是我们一些web缓存服务器,CDN也起到类似的作用,都是为了减轻主服务器的压力,同时加快用户的访问速度,提升用户体验,然后采取的一种措施,将内容分散,深入到用户群体集群中,然后对于其内容的更新成了一个问题,如何来更新我们的内容呢?一种方式是周期性发送检测数据报,二是主服务器修改后,向个服务发送更新消息,然后对其进行更新。可以有效的解决链路瓶颈和主服务器的压力。CDN的同步更新,就需要考虑的问题比较多了,针对CDN问题后面还会再讲。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。