[读书笔记]图解TCP/IP(概览)

注:本篇基于以太网
0.准备工作
1)工具
Ethereal 0.99.0 : 抓包
wget : 避免多个HTTP产生干扰,使用wget来访问网页
2)准备/运行

  • 关闭其他可能会产生网络请求的程序(如QQ)
  • 清除ARP高速缓存:arp –d
  • 清除本地dns缓存:ipconfig /flushdns
  • 打开ethereal,去掉混编模式选项(把如下打勾去掉),开始抓包

[读书笔记]图解TCP/IP(概览)_第1张图片

  • 访问:wget http://www.sina.com.cn
  • 停止ethereal抓包

 [读书笔记]图解TCP/IP(概览)_第2张图片
1.TCP/IP协议族

  • 网络接口层(数据链路层):包括以太网协议和两个辅助协议:ARP(地址解析协议)和RARP(反地址解析协议)
  • 网络层:包括IP协议和两个辅助协议:ICMP(Internet控制消息协议)、IGMP(Internet组管理协议)
  • 传输层:TCP/UDP协议
  • 应用层:FTP/SNMP/SMTP ...

2.网络接口层(数据链路层)和ARP
      大概地可以猜测到,当我们访问网页时,第一步当然是通过DNS服务器将域名解析成IP地址,DNS的服务IP在网络启动的时候已经获到(ipconfig /all可以看到),那么通过路由表(见第3部分)可以查到,到达DNS服务器需要通过网关(192.168.0.1),那么,发出去的包,192.168.0.1网关是如何知道此包是发送给它的呢?
      我们知道,以太网在逻辑上是属于总线结构,在以太网上发送一个数据包,那么局域网内的所有主机都会接收到该包,如果每个包都这么层层往上传递,当然会浪费那些对该包并不感兴趣的主机的计算资源(CPU),所以网卡(接口层)只过滤那些目标地址为本机地址(网卡地址)或者目标地址为广播地址的数据帧(当然,我们也可以设置网卡的工作模式为混编模式,这时候所有的包都可以接收到),因此,只要我们发送的目的网卡地址为192.168.0.1网关的网卡地址,则网关会对该包进行处理(路由转发)。然而,这里又引发了另外一个问题,本机是如何获得网关的网卡地址的呢?
      网络接口层已经为此准备了一种协议:ARP地址解析协议。如上图,我们发现首先发送的就是一个ARP广播请求(网卡地址是ff:ff:ff:ff:ff:ff),局域网内的所有主机接收到该请求,会判断,如果ARP指定的IP地址与本机一样,则发送一条响应,否则则忽略之。如下图,2中网关192.168.0.1回复192.168.0.150本身的IP
 
3.路由
   主机/路由器是如何判断包如何发送或者转发的呢?
路由可以分成两个主题:路由查找和路由更新。
1)    路由查找
    路由查找决定了包该如何路由出去,通过如下命令可以获取到当前的路由表信息:
    netstat –rn
[读书笔记]图解TCP/IP(概览)_第3张图片
    如上的话题,当我们的包发送给DNS服务器202.96.128.86时,通过该路由表,我们可以看到没有一条路由记录匹配该IP,因此最终这个请求包将会被发送到默认网关192.168.0.1上,而192.168.0.1将会维持自己的路由表,并通过层层转发,最终将包数据传递到202.96.128.86上
2)    路由更新
     为了确保数据使用比较合理的路由,或者由于某些路由节点断开后,数据能够使用其他的路由方式送达目的地,或者路由器能够智能地在启动后能够初始化合理的路由表,路由器之间必须有一些手段来交换路由信息以维持路由的更新,一般有如下协议:

  • RIP:Routing Information Protocol,自治系统(譬如一个公司网络或者一个学校网络系统)路由器之间路由更新协议
  • OSPF:Open Shortest Path First,用于替代RIP协议
  • BGP:Border Gateway Protocol,用于骨干网路由器之间交换路由信息的协议

通过如上的两个手段,主机/路由器就能够保证数据最终以比较合理的方式发送到目的地
4.DNS
1)DNS解析过程
   当我们通过域名的方式访问网页,最终又是如何处理的呢?
   如上,主机通过发送一个DNS请求,向DNS服务器查询相应域名的IP。DNS是以层次化的结构来组织所有的域名的,如下是一个可能的组织结构
   Root DNS
       |
       |
   Local DNS(202.96.128.86)  ——  Sina DNS
       |
       |
     Local(192.168.0.150)
     当我们本地网卡初始化的时候,能够获得到本地网络提供商的DNS Server(202.96.128.86,通过ipconfig /all可以看到),则首先Local向Local DNS Server请求www.sina.com.cn的域名解析。DNS请求可以是递归的或者非递归的,对于递归的DNS请求,DNS Server必须把最终域名的IP查询到返回给请求者,而对于非递归的DNS请求,DNS Server可以只返回下一个DNS解析服务器的IP。譬如如上图,Local会向Local DNS发送一个递归的DNS请求,而Local DNS首先从高速缓存中获取是否已有该域名的解析结果,如果没有,Local DNS则可以向Root DNS发送一个非递归请求(对于Root DNS,只允许非递归的DNS请求),Root DNS不知道该域名的解析结果,但它知道 Sina DNS可以解析该域名,则Local DNS接收到Root DNS的返回结果后,再向Sina DNS请求,最终Sina DNS返回www.sina.com.cn的解析IP。
2)如何用DNS实现CDN
   通过CDN(Content Delivery Network),我们可以让用户访问最靠近用户的服务器,以获得更好的响应速度。那么如何用DNS来实现CDN呢?
   假设我们现在在上海/北京/深圳/成都四个方向各有一些服务器提供服务,那么当上海的用户发起请求的时候,我们当然是希望用户去访问上海的服务器,那么如何做呢?通过DNS的解析就可以做到。首先上海的用户通过上海网络提供商的Local DNS查询IP的时候,则Local DNS会向Sina DNS询问域名对应的IP,则很简单的,Sina DNS可以获取到Local DNS的IP知道其所在的位置,那么此时可以响应一个上海服务器的IP给Local DNS。
5.TCP交互
   现在一切准备就绪,我们获得了www.sina.com.cn的IP地址,可以开始发送请求了
1)3次握手

     首先通过3次握手过程建立连接,本地主机首先发送一个连接请求(如上5的SYN请求),服务器接收到连接请求后,将确认+连接请求(SYN,ACK)发送给客户端,本地主机再对服务器的SYN请求做一个确认(ACK),双方3次握手完成。
2)数据传送

[读书笔记]图解TCP/IP(概览)_第4张图片
   握手完成之后,可以开始传送数据,如上图,双方开始数据交互,注意一下如上几个重要的参数:

  • Seq=104,相当于一个连接的标识,通过该连接发送/响应的请求都会携带该标识
  • Ack=2905,表示到目前为止已经接收到2904位的包,期望得到2905的包
  • Win=65340,当前的窗口大小,用于流量控制,发送方一般会连续发送几个数据包给接收方,接收方告诉发送方当前“窗口”(可以理解为针对这个连接的缓冲区)还有多大,发送方如果发现窗口为0,则会停止发送,等待接收方的确认/窗口更新消息。需要注意的是,如果我们打开了Nagle算法,则发送方总是等待接收到确认消息后才发送下一个消息。

3)4次握手终止连接
 [读书笔记]图解TCP/IP(概览)_第5张图片
   我们会很奇怪地发现,连接使用的是3次握手过程,而关闭使用的确实4次,这是因为TCP是全双工的模式,即我们可以通过2次握手过程,只关闭单向的数据传递,而另外一个方向的传递仍然可以继续。另外一种终止连接的方式发送RST复位请求,我们可以在下面中看到这两种方式的区别。
4)TCP状态变更
   TCP如下几个状态比较重要:ESTABLISHED、TIME_WAIT、CLOSE_WAIT,我们通过如下图来观察状态的变更

  • 建立连接

         (CLOSED)Client  -----SYN-----  Server (LISTEN)
   (ESTABLISH ED)Client  SYN+ACK--  Server(CLOSED)
   (ESTABLISHED )Client  ----ACK-----  Server (ESTABLISHED )

  • 在数据传递的过程当中,当然是继续维持ESTABLISH的状态
  • 关闭连接

   (FIN_WAIT )  A    ----FIN----->  B (CLOSE_WAIT )
   (FIN_WAIT)   A   <---ACK-----   B (CLOSE_WAIT)
(TIME_WAIT)   A    <---FIN-----   B (LAST_ACK)
(TIME_WAIT)   A    ----ACK---->  B (CLOSED)
     我们可以看到,主动发起关闭的一端A非常倒霉,虽然连接已经关闭,但它还必须维持TIME_WAIT的状态,为什么呢?因为A不能确认B是否真的接收到了它最后发送出去的ACK请求,因此它必须等待确保B不会因为由于没有接收到A的请求而重发FIN请求。A会等待2MSL(Max Segment Lifetime,即2倍包的最大生命周期,即包一去一回的最大时间,一般1MSL为30秒、1分钟或者2分钟,取决与不同的操作系统或配置)。而在TIME_WAIT期间,这个socket的fd当然不能被重新使用,如果我们发现系统中出现too many open file,而又看到有很多TIME_WAIT状态的TCP连接时,那么大概就是发生了这种情况。所以,对于服务端实现来说,为避免出现TIME_WAIT的情况,应该尽量避免主动关闭连接而应该由Client来发起。另外一种不太幽雅的处理这种情况的方式是,使用RST(复位, socket的so_linger选项就是使用RST的方式来关闭连接的)而不是FIN的方式来关闭连接,对于使用了RST的方式,则不管是否有没有包还没有发送出去或者对方有没有接收到,即刻复位连接,这时候我们可能会在客户端看到Connection reset by peer的错误。

你可能感兴趣的:(数据结构,应用服务器,网络应用,网络协议,读书)