计算机网络是由若干结点和连接这些结点的链路组成,计算机网络是互联互通的,无主从关系的计算机集合.网络之间可以通过路由器连接起来,组成一个更大的计算机网络,称为互连网.即网络的网络.网络许多计算机连接在一起,互连网把许多计算机网络通过路由器连接在一起.与网络相连的计算机通常称为主机.
互连网(internet)是一个通用名词,它泛指由多个计算机网络互连形成的计算机网络.而大写的Internet(互联网)则泛指当前世界最大的,最开放的,由众多网络相互连接而形成的特定互联网,采用TCP/IP协议族作为通信的规则,前身是美国的ARPANET.
互联网从其工作范围分为两个部分,一部分为边缘部分,一部分为核心部分.边缘部分是所有连接在互联网的主机组的,这部分是由用户直接使用的.另一部分是核心部分,由大量网络和大量路由器构成,为边缘部分提供服务.
我们实现互联网的目的就是为了不同的主机之间可以进行通信,即主机A上的某个进程a要和主机B的某个进程b进行通信,即计算机之间通信.
在网络边缘部分进行通信通常划分为两种方式:客户端(Client)/服务器(Server)与对等方式(P2P).
客户端-服务器方式即我们耳熟能详的Client/Server结构,这种方式是互联网最常用的,最传统的方式.服务器为提供服务方,客户端为请求服务方.
对等方式P2P方式是指两台主机在通信时并不区分哪一个是服务请求方,哪一个是服务提供方.只要这些设备都安装了P2P软件,就可以互相进行通信.
在网络核心部分起到核心作用的是路由器,路由器是实现分组交换的关键构件,任务是转发收到的分组.
为了弄清分组交换,我们先简单介绍一下电路交换.在电话问世不久后,所有的电话机都需要互相连接,但是所以电话机之间两两相连接是不现实的,有N个电话机需要连接则需要构建N*(N-1) / 2条线路.所以为了解决这一问题,出现了交换机,每一个电话机只需要连接到交换机,由交换机负责电路通信.
从资源的分配角度看,交换是按照某种方式动态的分配传输线路的资源.双方通信需要建立一条专用的物理通路,在双方通信时,资源不会被释放,分为建立连接-占用资源-释放连接三个步骤.这三个步骤的交换方式称为电路交换.
我们注意到当两个电话机进行通信时需要一直占用资源,当电路用来传输计算机数据时, 传输效率会很低.电路交换为了提供工作效率的其他内容如多路复用技术等内容则不做过多介绍.
分组交换采用的是存储-转发的方式.我们将要发送的整块数据称为报文,在发送报文之前会将报文分割成若干份,在每一份数据上加一些必要的控制信息组成头部,就形成了一个分组,相比于直接发送报文,分组是互联网中最常见的传送数据单元内,分组的优势在于可以不用等待一个报文接收完毕再去发送下一个报文,利用分组加上路由器转发的特点,就像流水线一样并行进行,极大地提高了信道利用效率.每个分组的传输利用链路的全部带宽.
与之相对应的,报文交换也采用的是存储-转发的方式.假如我们不是每次去发送一个个的分组,而是一次性发送一整个报文,整个报文先传送到相邻结点,全部存储下来再去查找转发表,转发到下一个结点,这样的方式就是报文交换.
传输延时是指一个分组从一端到另一端所需要的时间,网络中的时延是由不同的部分组成的.
具体可以分为以下四种类型:发送时延、传播时延、处理时延、排队时延.
发送时延:发送时延是主机或路由器发送数据帧所花费的时间.
发送时延=数据帧长度/发送速率.
传播时延:
传播时延是数据帧在传输过程中所产生的延迟.
传播时延=信道距离/电磁波在信道上的速率.
处理时延:
主机或者路由器接受到分组需要花费一定的时间进行转发,这样就产生了处理时延.
排队延时:
分组在进行网络传输时,要经过许多的路由器,在分组进入路由器中要先在路由器队列中等待处理,就产生了排队时延.
OSI七层模型 (Open System Interconnect),全称为开放系统互连参考模型,是国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)1984年联合制定的开放系统互联参考模型,为开放式互联信息系统提供了一种功能结构的框架。
OSI的七层协议体系结构的概念非常清楚,理论比较完善,但是它即复杂同时也不实用,TCP/IP的体系结构则完全不同,只保留了应用层,传输层,网际层和网络接口层.虽然OSI七层模型也有很大的研究价值,但是实际上TCP/IP现在在我们的网络世界中得到了非常广泛的应用.
我们将TCP/IP四层协议模型网络接口层拆开,形成五层模型.本篇笔记将以五层模型的视角来一层一层概述,了解到每一层所负责的功能和具体实现.实际应用还是TCP/IP的体系结构设计.
本文将采用自顶向下的方向依次来描述计算机网络中,各个层之间功能的划分和简单了解具体的实现算法.我们在学习计算机网络前接触最多的应该就是应用层相关的内容,比如DNS域名解析,HTTP以及HTTPS等等协议.在这之前,我们需要先复习一下,常见的网络应用的三种架构.
分别为客户机/服务器结构(C/S),点对点结构(P2P),混合结构(Hybrid),与之前的网络边缘部分常见的两种体系结构相比,多了一个混合结构,也就是将两种结构混合在一起,那么两种混合在一起可以利用两者的优点规避两者的缺点吗?
C/S结构中,服务器提供不间断的服务,在客户端中需要与服务器进行通信,使用服务器提供的服务,不能和其他的客户机直接通信.在P2P结构中则没有一直在线的服务器,任意端系统/结点之间可以直接通信,可自由伸缩,但是难以管理.
所以在混合结构中,我们需要尽可能地利用两者的优点,规避两者的缺点,比如在文件传输时可以采用P2P结构,在文件搜索时可以采用高效的C/S结构.
在我们使用浏览器搜索网络中的资源时,见过最多的应该就是万维网WWW,即World Wide Web,在Web中所遵循的协议就是超文本传输协议,HTTP.在C/S结构中,客户机向服务端请求服务,服务器收到客户机的请求后,响应客户机的请求,发送对应的报文.
在HTTP早期1.0版本中,使用的是非持久性连接,浏览器每次请求都需要与服务器建立一个 TCP 连接,服务器处理完成后立即断开 TCP 连接(无连接),服务器不跟踪每个客户端也不记录过去的请求(无状态),所以每个TCP连接中只允许传输一个对象.
HTTP是一种无状态协议,即服务器不保留与客户交易时的任何状态。
也就是说,上一次的请求对这次的请求没有任何影响,服务端也不会对客户端上一次的请求进行任何记录处理。
一个无状态连接的过程:
我们可以看到,每次都解析都需要进行TCP的重新连接,在这个过程中消耗了大量的资源.如果可以减少连接就可以减轻服务器的负担,所以为了解决这一问题出现了持久性HTTP(HTTP1.1).
HTTP1.1避免了连接建立和释放的开销;通过 Content-Length 字段来判断当前请求的数据是否已经全部接受。不允许同时存在两个并行的响应。
从客户端发送一个很小的数据包到服务器并返回的时间称为RTT(Round Trip Time).在非持久性连接中,每个对象需要2个RTT的参数时间.在持久性连接中,发送响应后,TCP保持TCP连接,后续的HTTP消息可以通过这个连接发送,所以每个被引用的对象耗时为1个RTT.
HTTP协议有两类消息,请求消息和响应消息
请求消息的格式如图所示,由请求行+请求头+请求空行+请求主题构成
在HTTP1.0中只有POST,GET和HEAD方法,在软件开发中GET和POST方法很常见,所以不做过多叙述,本文只是简单的描述一下HEAD方法.
POST:在请求消息的消息体中上床客户的输入,例如网页需要提交表格单元的内容
GET:输入信息在请求行的URL字段上传
HEAD:是让服务器端不要将所请求的对象放入响应消息中
在HTTP1.1中新增了两个方法,分别为PUT和DELETE方法
PUT:将消息体中的文件上传到URL字段所指定的路径
DELETE:删除URL字段所指定的文件
HTTP的响应消息与请求消息格式相似,HTTP响应消息由状态行+头部行+空行+响应消息主体构成.
常见的状态码 | 状态码的对应状态 |
---|---|
200 | 客户端请求成功 |
304 | 客户端发送了一个带条件的GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个304状态码 |
400 | 客户端请求有语法错误,不能被服务器所理解 |
401 | 请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 |
403 | 服务器收到请求,但是拒绝提供服务 |
404 | 请求资源不存在,举个例子:输入了错误的URL |
500 | 服务器发生不可预期的错误 |
503 | 服务器当前不能处理客户端的请求,一段时间后可能恢复正常 |
HTTP协议本身是一种无状态的协议,当一个客户机多次向服务器发起请求,服务器并不能分辨是否为同一用户,但是我们在使用网络应用时又需要实现一种有状态的会话,Cookie与Session技术解决了这一问题.
Cookie:
cookie机制采用的是在客户端保持 HTTP 状态信息的方案。当浏览器访问WEB服务器的某个资源时,WEB服务器会在HTTP响应头中添加一个键值对传送给浏览器,再由浏览器将该cookie放到客户端磁盘的一个文件中,该文件可理解为cookie域(键值对的集合),往后每次访问某个网站时,都会在请求头中带着这个网站的所有cookie值。
每一个cookie都有一个name和一个value,且name是唯一的。相同名字时,后者会覆盖掉前者
Session:
session机制采用的是在服务器端保持 HTTP 状态信息的方案。为了加速session的读取和存储,web服务器中会开辟一块内存用来保存服务器端所有的session,每个session都会有一个唯一标识sessionid,根据客户端传过来的jsessionid(cookie中),找到对应的服务器端的session。为了防止服务器端的session过多导致内存溢出,web服务器默认会给每个session设置一个有效期, 若有效期内客户端没有访问过该session,服务器就认为该客户端已离线并删除该session。
我们知道http协议是无状态的,所以如果有连续的请求访问同一个网页,他就会一直重复发送,这个时候,如果有一个“有状态”的服务器,对之前的请求有记录,那么它在第二次以后的请求中,就可以在缓存中直接发送给客户。
一个使用代理服务器情况下的流程
采用了缓存服务器的结构降低了链路通信的流量,但是存在请求内容在缓存服务器中已经被更改的情况,即服务器是更改后的最新数据,缓存中还是之前的数据,所以通过HTTP请求头标签If-Modified-Since来解决.
If-Modified-Since是标准的HTTP请求头标签,在发送HTTP请求时,把浏览器端缓存页面的最后修改时间一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较。
如果时间一致,那么返回HTTP状态码304(不返回文件内容),客户端接到之后,就直接把本地缓存文件显示到浏览器中。
如果时间不一致,就返回HTTP状态码200和新的文件内容,客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示到浏览器中。
域名系统DNS用来便于人们使用的URl地址转换为IP地址,可以采用分布式层次式数据库,比如查询一个edu结尾的URL,需要先从跟服务器解析到对应的eduDNS服务器,在解析到对应的IP地址.
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
在两个不同设备上的应用要进行网络通信就需要用到Socket套接字来完成,需要注意的是这些方法都是由操作系统内核来完成,不同的操作系统的具体实现可能略有差异,但是核心函数依然为下图所示:
//返回sockfd
int socket(int protofamily, int type, int protocol);
socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。
//把一个地址族中的特定地址赋给socket
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
对应的参数类型为:
通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
网络字节序与主机字节序
主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下:a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。**由于TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。**字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。
注意:在将一个地址绑定到socket的时候,请先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。由于这个问题曾引发过血案!公司项目代码中由于存在这个问题,导致了很多莫名其妙的问题,所以请谨记对主机字节序不要做任何假定,务必将其转化为网络字节序再赋给socket。
如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen函数参数类型:
connect函数参数类型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//返回连接connect_fd
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了。
sockfd:参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号
addr:这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL。
addrlen:它也是结果的参数,用来接受上述addr的结构的大小的,它指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL。
accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。此时我们需要区分两种套接字:
监听套接字: 监听套接字正如accept的参数sockfd,它是监听套接字,在调用listen函数之后,是服务器开始调用socket()函数生成的,称为监听socket描述字(监听套接字)
连接套接字:一个套接字会从主动连接的套接字变身为一个监听套接字;而accept函数返回的是已连接socket描述字(一个连接套接字),它代表着一个网络已经存在的点点连接。
一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
连接套接字socketfd_new 并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd一样的端口号。如果我们只采用一个套接字的话,那么在同一时刻只能有一个进程在被服务,显然不是我们所希望的。
至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网络中不同进程之间的通信,网络I/O操作常用的一般为recvmsg()/sendmsg().具体的细节就不做过多描述了。
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。
close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。
int close(int fd);
传输层协议为运行在不同Host上的进程提供了一种逻辑通信机制,是端对端之间的,在下一层的网络层则是主机之间进行逻辑通信.在传输层中一般采用TCP或者DUP来实现.
TCP协议全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议.
UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议.
在传输层中,是应用进程之间的,一个主机会有多个进程;因此会需要多路复用和多路分用.
也就是在网络层中可能会接收来自其他不同的主机的请求,而每一个主机又会有不同的进程等待处理,所以需要在传输层进行多路复用技术和多路分用技术,来缓解这一问题.
接收端采用多路分用技术,依据收到的Segment交给正确的Socket,即不同的进程.
发送端采用多路复用技术,为每个数据块封装上头部信息,生成Segment交给网络层.
UDP协议的无连接分用:
TCP协议面向连接的分用:
TCP协议面向连接的多线程分用:
注意在上图中
P2-P6
和P3-P5
,它们仅仅只有源端口号不同而在下图中服务器用一个进程创建多个线程即P4创建了多个线程
UDP协议是基于网络层的IP协议,能够进行简单的错误校验.但是是一种Best effort的服务,也就是尽力而为的策略,可能出现丢包,非按序到达等情况.
虽然UDP传输可能会出现一些情况但是因为UDP是一种无连接的协议,所以实现简单,无需维护连接状态,头部开销小,也没有拥塞控制,可以更好的控制发送时间和速率.常用于一些容忍一定的数据丢失并且对速率敏感的应用如流媒体应用等.
既然UDP是一种尽力而为的协议,那么如何在UDP如何实现可靠的数据传输?
在UDP中有一种简单的检测方式,就是利用校验和来检测UDP段在传输中是否发生错误(如位反转)
发送方:
接收方:
校验和计算示例:
1110 0110 0110 0110
1101 0101 0101 0101
11011 1011 1011 1011
将进位1加入16位中得到1011 1011 1011 1011+1=1011 1011 1011 1100
再将16位sum求反得0100 0100 0100 0011
为了保证数据的可靠性,我们就需要做到不错,不乱,不丢.可靠传输协议对应用层,传输层,网络层都很重要,但是信道的不可靠特性决定了可靠传输协议RDT的复杂性.
在应用层中,我们只需要关注数据的发送而不用关心数据是如何传递的,对于应用层来说下面的所有层都是透明的,不可见的,而传输的信道是一种不可靠的,
在RDT1.0版本中,我们假设在不可靠的信道中传输的数据是不会发生错误的,同时也不会丢弃分组,在RDT各个版本的状态描述中,本文采用的用状态机来描述不同状态下的处理过程.
因为在RDT1.0中假设没有错误,所以发送方和接受方只需要发送数据/接受数据就可以,是一种理想的参数模型.
我们之前提到了RDT1.0版本中,假设信道是不会出错的,但是在实际的信道中,可能在传输中产生位错误的信道,所以在2.0版本中,增加了一些简单的纠错机制.
在RDT2.0中,发送方新增了一个状态用来接收ACK/NAK信号,而接受方根据分组是否损坏来判断给发送方ACK或NAK信号.
rdt2.0有着一个致命的缺点,只考虑了发送方到接收方的数据传输,如果ACK/NAK信号出现了位反转,那么就离我们所预期的情况相距很远.
由此rdt2.1应运而生,在rdt2.0的基础之上,发送方在打包数据包时添加了0或者1编号,同样ACK,NAK字段上也添加了0,1字段,表示0.1号字段的确认或者否定。发送方就有了2种状态发送0号数据包,1号数据包,接收方也有了2种状态等待0号数据包和等待1号数据包。现在假设情景发送方向接收方发送0号数据包,如果接收方接收到0号数据包,返回ACK,但是ACK出现翻转,接收方处于等待1号数据状态,发送方重复发送0号数据,接收方会拒绝0号数据,避免重复。如果接收方接收到0号数据包出现错误,返回NAK,但是NAK出现翻转,接收方处于等待0号数据状态,发送方继续发送1号数据,接收方会拒绝1号数据,避免错序.
答案很简单,因为是SW协议,两个进程需要保持同步状态后才进行下一次操作,只需要保留当前的序号包以及下一次的序号包就可以,所以不需要保留更多的状态.
当ACK信号产生位反转后,发送方会重新发送0号分组,说明我们之前发送的ACK状态产生了错误,所以我们需要告诉发送方已经成功接受到了,重新ACK,保持两个进程的同步状态.
RDT2.2是基于RDT2.1的一种没有NAK消息的协议,我们一定需要ACK/NAk两个状态来表示信息的状态吗,可不可以只采用一个ACK来代表两个状态?
信道传输中,既可能发生错误,同时在路由器转发中可能会出现分组丢失的情况,如果ACK状态在存储-转发的过程中被丢失了,这样两个进程之间都不能进行同步,会一直等待对方的状态信息,因此RDT3.0中新增了一个超时重传的机制.
虽然有了timeout这个重要的机制,但是如何设置timeout的时间就成为了我们的问题
如下图所示,根据timeout设置的不同,可能会出现各种的问题:
GBN协议(Go-Back-N回滚N帧协议),中采用了流水线技术,为了解决RDT3.0中效率底下的问题,因为RDT3.0采用了Stop-Wait协议,所以需要一直等待一个分组被正确接受后才开始下一个.
也就是在RDT协议中,不管是发送方还是接受方,双方都只有一个缓存用来接受数据
在流水线协议中,我们希望可以缓存扩充,同时序列号也扩充,允许发送方在收到ACK之前连续发送多个分组.
在GBN协议中将发送方的缓存空间称为窗口,窗口的尺寸设为N,即最多允许N个分组未确认,GBN是一种累积确认的机制,接受到ACK(n)说明序列号n(包含n)的分组已经被正确接受
为空中的分组设置计时器
超时Timeout(n)事件:重传序号大于等于n,还未收到ACK的所有分组.
因为发生超时事件后,会重传所有序列号大于等于n的,因此存在资源浪费的情况
在GBN协议中,发送方有缓存空间,而接收方只有一个分组的空间,如果没有接受到就一直发送对于的ACK信号,希望发送方重新发送
数据链路层采用后退N帧(GBN)协议,发送方已经发送了编号为0~ 7的帧。当计时器超时时,若发送方只收到0、2、3号帧的确认则发送方需要重发的帧数是多少?分别是那几个帧?
解:根据GBN协议工作原理,GBN协议的确认是累积确认,所以此时发送端需要重发的帧数是4个,依次分别是4、5、6、7号帧.
假如某一层采用了GBN协议中,假如pkt2分组发送中丢失了,那么在发送方何时重新发送,接受方会是什么状态?
如图所示,会在超时后重新发送pkt2及后面的分组,而接受方在收到其他的分组时会丢弃,一直发送ACK1(上一个的ACK状态),告诉发送方pkt2丢失了,直到成功接收pkt2后发送ACK2状态.
GBN协议后我们发现当一个分组丢失后,后面的所有分组都需要重新发送,就会产生大量的重复分组,因为在接受方每次只能接受一个分组,所以SR(Selective Repeat)协议相比于GBN协议,多了一个接受方的窗口.SR单独确认,可以接收乱序到达的分组.
如果收到ACK,假如该帧序号在窗口内,则SR发送方将那个被确认的帧标记为已接收。如果该帧序号是窗口的下界(最左边第一个窗口对应的序号),则窗口向前移动到具有最小序号的未确认帧处。如果窗口移动了并且有序号在窗口内的未发送帧,则发送这些帧.
每个帧都有自己的定时器,一个超时事件发生后只重传一个帧.
SR接收方将确认一个正确接收的帧而不管其是否按序。失序的帧将被缓存,并返回给发送方一个该帧的确认帧(收到谁确认谁),直到所有帧(即序号更小的帧)皆被收到为止,这时才可以将一批帧按序交付给上层,然后向前移动滑动窗口.
如果收到了窗口下界之前的帧,就返回一个ACK。其他情况,就忽略该帧.
TCP协议也是在传输层中常用的协议之一,TCP协议是一种可靠的传输方式.TCP协议在通信双方发送数据之前要先创建连接,TCP连接包括Socket,连接控制变量,缓存等内容.下图为TCP报文段的格式,可以看到,相比于UDP,TCP的头部更为复杂.
TCP的实现中,如果发送超时事件,超时时间间隔将重新设置,将超时时间间隔加倍,会导致超时时间很大,重发丢失的分组之前要等待很长时间.
为了解决这一问题,TCP采用了快速重传机制,通过重复的ACK来检测分组丢失,如果发送方收到同一数据的3个ACK,则假定该数据之后的段已经丢失,在超时之前进行重传.
因此TCP在IP层提供的不可靠服务基础上实现了可靠数据传输,采用了流水线机制,累计确认机制等.触发重传的事件不只是超时了,当收到3个重复ACK,TCP就会启动快速重传机制.
在TCP中需要进行流量控制,防止发送方传输的数据大于接受方接收的速度,会导致溢出.
接受方需要在数据段的头部字段将可用窗口大小告诉发送方,发送方接收到后需要限制自己已经发送的未收到ACK的数据不超过接受方可用窗口大小.
可用窗口大小可用粗略的计算为接受方窗口大小-[最后接收到的数据-最后读到的数据].
当可用窗口等于0时,说明接受方已经不能接受数据了,所以双方不能再继续进行信息互相发送,
所以在可用窗口等于0时,依然可用给发送方发送很小的数据,以便携带信息.
三次握手的目的是建立可靠的通信通道,说到通信,简单来说就是数据的发生与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是否正常.
第一次握手:Client什么都不能确认,Server确认了对方发送正常,自己接收正常
第二次握手:Client确认了:自己发送、接收正常,对方发送正常、接收正常;Server确认了:对方发送正常,自己接收正常
第三次握手:Client确认了:自己发送、接收正常,对方发送正常接收正常;Server 确认了:对方发送正常,接收正常,自己发送正常,接收正常。
下面解释明明两次就可以建立连接的为什么还要加第三次的确认。
如果发送两次就可以建立连接话,那么只要客户端发送一个连接请求,服务端接收到并发送了确认,就会建立一个连接。
可能出现的问题:如果一个连接请求在网络中跑的慢,超时了,这时客户端会从发请求,但是这个跑的慢的请求最后还是跑到了,然后服务端就接收了两个连接请求,然后全部回应就会创建两个连接,浪费资源!
如果加了第三次客户端确认,客户端在接受到一个服务端连接确认请求后,后面再接收到的连接确认请求就可以抛弃不管了
当在一个计算机网络中,有太多的主机发送了太多的数据导致网络瘫痪.当数据被丢失后,发送方会重新发送数据,导致不断有新增的数据以及新增的重传数据.在一个有多个路由器转发的计算机网络中,一个分组被丢弃,那么上游的路由资源都会被浪费.
为了解决这一问题,我们采用了两种拥塞控制机制:端对端的拥塞控制和网络辅助的拥塞控制.
这里我们只描述一下ATM ABR拥塞控制:
在交换机中设置RM cell位(网络辅助),分别为NI 不允许增长,CI拥塞指示.在数据传输中随机插入几个网络辅助位,如果发生了拥塞,将cell中的EFCI位置1,前面的EFCI位被置1,那么在发送方返回的RM cell中置CI位.
在端对端的拥塞控制中,如果发生了超时事件或者接收到三个重复的ACK,就可以感知到网络拥塞,发生丢失事件后,发送方应该降低速率.常见的有以下几种中调整速率的算法.
加性增-乘性减AIMD
线性加(一个一个的加),乘性减(直接减半,快速减)
逐渐增加发送速率,只到发送丢包,当发送loss后将发送窗口大小减半,即乘性减,快速的减少发送量
慢启动SS
在连接建立时,将窗口大小设置为1
每收到一个RTT就将窗口大小翻倍,初始速率很慢,但是会指数级增长
Threshold变量
线性增长与指数型增长的各有自己的优点,何时使用切换规避掉相应的弊端?
- 设Threshold变量为界限,前期采用慢启动SS算法.
- 当发送丢包后,Threshold更新为前窗口大小的1/2.
- 以Threshold为界限,指数增长到Threshold后开始线性增长.
因为数据会在传输层进行汇总,其他的应用程序无法知道网络是否阻塞,不能去调整自己的流量.
在传输层中,我们只关注了网络边缘部分端对端的服务,在网络层中我们开始进入网络核心部分,在网络层中实现的是主机到主机之间的数据传输.在网络层需要进行转发和路由的功能,在转发前我们需要路由协议来选择路径,选择后就可以根据IP地址来寻找目的地址发送给相应主机,在这个过程中如果发生了错误,我们还需要ICMP协议来进行传送差错报告.
一个数据段从发送方主机发送,接受主机接收后向上层传输层交付数据段.数据段经过的链路上每个主机和路由器都运行着网络层协议,网络核心最重要的服务是分组-转发:路由器根据数据报,将数据报转发到正确输出端口.
其中有两个名词需要解释一下:
转发: 将分组从路由器的输入端口转移到合适的输出端口
路由: 确定分组从源到目的经过的路径
路由算法确定了网络中主机到主机的路径
具体的转发地址需要在路由器中建立一个路由转发表,用来确定本路由器如何转发分组
数据分组传输之前两端主机需要首先要利用路由器等网络设备建立虚拟/逻辑连接.
- 网络层连接:两个主机之间(路径上所有路由器等网络设备参与其中)
- 传输层连接:两个应用进程之间(对中间网络设备透明)
与传输层的有链接服务TCP协议,无连接服务UDP协议类似.网络层也有两种服务模型,即有连接与无连接的两种网络层服务模型.数据报网络与虚电路网络就是典型的两类分组交换网络.
无连接服务:
- 不预先为分组确定路径
- 不同分组不同路径
- 不同的分组可能传输路径不同
- 数据报网络采用的就是无连接服务.
连接服务:
首先为系列分组的传输确定从源到目的经过的路径,建立连接
每个分组都是按照相同路径传输
传输结束后拆除连接
虚电路网络采用的是有连接服务
虚电路是指一条从源主机到目的主机,类似于电路的路径,与电路传输不同的时,分组需要利用链路的全部带宽,而电路传输有时分复用,频分复用,以及码分复用等技术共享链路.
在虚电路网络中,呼叫建立网络之后才能进行数据传输,在传输结束后需要拆除呼叫.
每个分组需要携带一个虚电路标识(VCID),而不是目的主机地址.由虚电路经过的每个网络设备来维护虚电路连接状态.携带相同虚电路标识的分组就会沿着相同的路径进行传输.
每一个虚电路包括从源主机到目的主机的一条路径,沿路每段链路需要有一个VCID.沿路每个网络层设备利用转发表记录经过的虚电路.
虚电路信令协议(signaling protocols),用于VC的建立,维护与拆除.但是在目前的因特网中不采用.
VC虚电路网络是由电话网络演化而来,双方需要有可靠性需求和有保障的服务,为此简化了网络边缘部分,将网络复杂化.
Internet网络中,采用的是数据报网络.因为是无连接状态,所以每个分组都需要携带目的地址,路由器在通过分组的目的地址转发分组,每个分组独立选路.如果路由算法更新可能会导致相同目的地址的分组走不同的路径.
在路由转发表中保存所有的IP地址显然是不可能的,因此转发表记录的是目的地址范围.
路由器根据目的IP地址的范围来确定从哪个接口进行转发.同时如果**转发表中两个地址范围是重合的,那么优先匹配最长前缀的接口.**关于为什么这样匹配我们后面会提到.现在只需要简单了解网络层的工作内容.
数据包网络常用于计算机之间的数据交换,没有严格的时间要求,链路类型众多难以统一服务,需要端系统(计算机)可以自适应,性能控制和差错恢复,将网络简化,复杂化边缘部分.
- 版本号: 4和6分别代表IPv4和IPv6版本
- 首部长度字段占4位:IP分组首部长度;(4bit的话最大15),视为4字节为单位
- 例如:5代表IP首部长度为20(5*4)字节
- 服务类型(TOS)字段占8位:指示期望获得哪种类型的服务
- 1998年改名为区分服务;
- 只有在网络提供区分服务(DiffServ)时使用;
- 一般情况下并不使用,通常IP分组的该字段(第2字节)的值为00H
- 总长度:IP分组的字节数(首部+数据),最大的IP分组的长度为65535B,最小的IP分组为20B,所以可以封装的最大数据数为65535-20=65515B
- 生存时间TTL: IP分组在网络中可以通过的路由器数,没通过一个路由器TTL-1,如果TTL=0,则路由器丢弃该分组.
- 协议字段: 指示IP分组封装的是哪个协议的数据包,6代表TCP段,17代表UDP段,实现复用/分解.
- 首部校验和:实现对IP分组的首部差错检测,计算校验和时字段置位0,利用反码算数求和,和的反码作为首部校验和字段
- 选项字段:携带安全,源选路径,时间戳,路由记录等内容,很少被使用,后面的填充字段为选项字段服务,符合32位对齐,即保证首部长度是4的倍数.
MTU为网路链路层数据帧可封装数据的上限,不同链路的MTU可能不同,如果下一个链路的MTU小于当前链路的MTU,那么大的IP分组就需要"分片"成多个分组.路由器只进行"分片",最后统一在目的地进行重组,在IP首部需要有字段来标志分片以及分片相对顺序.即IP首部的片偏移字段.IP协议利用一个计数器,每产生一个分组计时器加一,作为该IP分组的标识.
- 标识字段: 标识一个IP分组,作为该IP分组的标识
- 标志位字段: 占3位,有一个位空闲,其余两位为DF位与MF位
- DF 置1 说明禁止分片 置0说明允许分片
- MF 置1 说明不是最后一片 置0说明是最后一片
- 片偏移字段: 一个IP分组分片封装原IP分组数据的相对偏移量,以8字节为单位.
假设原IP分组总长度为L,待转发链路的MTU为M,若L 因此一个最大分片可封装的数据为: 需要的总片数为: 每片的偏移字段取值为: 每片的总长度字段为: 每片的MF标志位为: IP分组例题: IP编址是针对于实现网络层功能的,交换机不在网络层而在链路层是不编址的,主要是对路由器和端系统进行IP编址.主机/路由器与物理链路的连接叫做接口.IP地址与每个接口关联. 一个IPv4地址有32比特,用来标识主机,路由器的接口.IP地址每8bit一组,一般转换为十进制表示.如果随意分配IP地址,会将路由器转发表造成不能承受的负担. 在IP地址中,前几位用作网络位,后几位被用作主机号,IP子网就是具有相同网络号的设备接口,可以不跨越路由器就可以彼此物理连接的接口.根据网络号的不同,IP地址被划分为几个不同的编址. 路由器接收到一个IP地址,根据IP地址判断地址类型: 注意D 类和 E 类地址是没有主机号的,所以不可用于主机 IP,D 类常被用于多播,E 类是预留的分类,暂时未使用. 在这些IP地址中,还有一些特殊的IP地址: A类,B类,C类地址中的私有IP地址: 但是这种IP分类的IP地址缺少了地址的灵活性,同一网络下没有地址层次,同时与现实的网络不能很好地匹配,C类包含的主机数量只有28-2个,头尾两个IP地址不能表示主机.而B类地址包含的主机数又高达216-2个地址,所以出现了无地址分类地址CIDR. 这种方式不再有分类地址的概念,单纯的被划分为网络号+主机号.利用子网掩码来确定这个IP中前几位是网络号. 例如一个IP地址为: 172.32.1.112 子网掩码为 255.255.254.0 换成二进制可以表达成这样: 10101100 00100000 00000001 01110000 11111111 11111111 11111110 00000000 进行异或运算,就可以得到IP地址中的网络号. 虽然这样分配IP地址,极大地增加了地址的灵活性但是,每一个可分配地址范围去掉主机域全0和主机域全1的IP地址.当划分子网时,会造成头尾两个IP地址不能表示主机,主机号全为0的地址为网络地址.主机号全为1的地址为广播地址.虽然划分子网会有一定IP地址损失,但是性能会大大提升. 还记得之前提到过的转发表吗? 路由器根据分组的目的IP地址和转发表转发到不同的接口,如果每个IP地址都加入到转发表,那么显然负担是很大的.所以我们将拥有相同网络号的进行路由聚合操作. 比如223.1.0.0/23,223.1.2.0/23,223.1.3.0/24三个IP地址都要经过路由器R1的2号接口进行转发,我们可以将路由器中这几个IP地址进行路由聚合,在转发表中合并为223.1.0.0/22,通过2号接口进行转发.这样操作后路由表会进行大量的简化. 但是我们在进行网络划分时不一定会这么完美覆盖到一整个子网地址,如果这个属于其他组织的IP地址被转发到其他的组织一定会引发混乱,所以在转发表中采用了最长前缀匹配机制,即优先匹配最符合的IP地址,匹配后根据转发表转发相应的接口. 我们已经了解到可以通过IP地址来确定主机的位置,那么如何为主机分配IP地址呢? 在我们的计算机中,可以通过设置计算机中的静态IP地址和子网掩码来确定主机的IP地址,但是如果有大量的主机,显然是工作量巨大.所以我们希望可以动态的给主机分配IP地址,这就是DHCP协议的由来. 在主机静态配置中有静态IP地址和子网掩码,还有一个网关的地址:这个地址就是这个子网的数据报要离开子网的时候,应该送往的接口地址. 在DHCP协议中,IP地址,子网掩码,默认网关都会从服务器中动态获取,做到"即插即用"的特性. 可以支持移动用户加入网络. 下面是DHCP的工作过程: 0.0.0.0表示本机IP地址;255.255.255.255是广播地址 DHCP协议在应用层实现,请求方需要将DHCP报文封装到UDP数据报中,通过IP广播,链路层广播.DHCP服务器方收到后会构造ACK报文,分配IP地址,子网掩码,默认网关地址,DNS服务器地址.在DHCP协议中,IP分配是有时间限制的,过期了需要重新发起请求. 网络地址转换技术NAT(Network Address Translation)主要用于实现位于内部网络的主机访问外部网络的功能。当局域网内的主机需要访问外部网络时,通过NAT技术可以将其私网地址转换为公网地址,并且多个私网用户可以共用一个公网地址,这样既可保证网络互通,又节省了公网地址.这些私有地址只需要从ISP中申请一个地址,减少了对IPv4地址的浪费. 本地设备IP变更后也不需要对外界进行通告,于此同时外部的ISP地址进行变更时,也不用修改内部网络设备IP地址,这些使用私有地址的设备对外界网络是不可见的,不可直接寻址. 私有地址访问外部网络时,需要经过路由器进行NAT地址的转换,与路由器的转发表类似,路由器中还有一个NAT地址转换表将私有地址转换为路由器中分配的一个可以被外部网络访问到的IP地址. 穿透发生在外部主机向路由器划分的一个子网中的私有地址发起请求,但是客户端不能直接访问服务器,对外唯一可见地址为138.76.29.7. IP协议如果发生错误,就会丢弃该分组,同时通过ICMP向主机发送差错报告.互联网控制报文协议(ICMP)支持主机或者路由器,ICMP可以用来返回差错报告和网络探询操作. ICMP报文也是要封装到IP数据报中传输的. ICMP的头部: ICMP的头部共有8个字节,前4个字节是分别为类型、代码、检验和. 后面4个字节取决于TCP数据报或者UDP数据报等. ICMP差错报告报文数据: 在ICMP的数据部分中把收到的需要进行差错报告的IP数据报首部提取出来,加上IP数据报数据内容的八个字节. 思考一下后面八个字节是什么内容,为什么要加上? 因为我们在计算机网络体系中,数据报会一层层被封装向下一层传输,所以在IP数据报的数据字段中封装的是TCP或者UDP数据包的报文,所以前八个字节封装了目的端口和源端口的内容. 差错报告有五种报文: 几种不发送ICMP差错报告报文的特殊情况: 网络探询有两种报文: 几种ICMP报文已不再使用: 信息请求与应答报文,子网掩码请求和应答报文路由器询问和通告报文 在之前的内容中,我们知道了物理层的核心功能是路由-转发,那么我们就需要通过路由算法求解确定去往目的地网络的最佳路径. 在数据结构中,有一种数据结构叫做图.我们可以将实际中的路由器之间的关系抽象成图. 图: G = (N, E) N = 路由器集合= { u, v, w, x, y, z } E = 链路集合 ={ (u,v), (u,x), (v,x), (v,w), (x,w), (x,y), (w,y), (w,z), (y,z) } 费用(Costs):链路的费用,C(u,v) = 2 路由器分组转发是通过路由表转发的,而路由表是通过各种算法得到的,从能否随网络的通信量或拓扑自适应地进行调整变化来划分. 路由算法: 寻找源到目的最小费用路径的算法. 路由算法可分为两大类:静态路由与动态路由. 静态路由:需要手工配置,路由更新慢,优先级高,不适用于大型网络. 路由器间彼此交换信息,按照路由算法优化出路由表项. 动态路由算法分为全局信息和分散信息: 全局信息:所有路由器掌握完整的网络拓扑和链路费用信息,链路状态(LS)路由算法 分散(decentralized)信息:路由器只掌握物理相连的邻居以及链路费,邻居间信息交换、运算的迭代过程,距离向量(DV)路由算法. Dijkstra 算法:所有结点(路由器)掌握网络拓扑和链路费用,通过“链路状态广播”使所有结点拥有相同信息,计算从一个结点(“源” )到达所有其他结点的最短路径获得该结点的转发表,k次迭代后,得到到达k个目的结点的最短路径 符号标记: (2)D(v): 从源到目的v的当前路径费用值 (3)p(v): 沿从源到v的当前路径, v的前序结点 (4)N’: 已经找到最小费用路径的结点集合 根据Dijkstra 算法: 可能出现的问题: 由于动态更新,有可能有一个数据报从B刚到D,结果路径改变,有传回B,来回反复,形成震荡;最终形成TTL变成0,分组被丢弃. Bellman-Ford方程(动态规划): 假设,dx(y):=从x到y最短路径的费用(距离) 核心思想:每个结点不定时地将其自身的DV估计发送给其邻居,当x接收到邻居的新的DV估计时, 即依据B-F更新其自身的距离向量估计:Dx(y) ← minv{c(x,v) + Dv(y)} for each node y ∊ N,Dx(y)将最终收敛于实际的最小费用 dx(y),结点获得最短路径的下一跳, 该信息用于转发表中. 特点: 每个结点的工作流程: 当有链路费用发生改变时,就需要重新计算. 如图所示,当某条链接的费用减少时,我们称之为有一个“好消息”。在网络中,好消息的传递往往很迅速。某一时刻,Y检测到它到X的链路费用由4减少为1,好消息当然要告诉大家了,于是它更新了自己的距离向量,并通知了Z。Z在收到Y的更新报文后,也更新了自己的距离向量(由5减为2),并向邻居们发送更新报文。而后,Y又收到了Z的更新报文,但它发现并没有改变自己的最低费用,于是保持不变。这样,仅仅经过了少次迭代网络就达到了静止。好消息通过网络得到了迅速传播。 但是当链路费用增加(甚至断开)时,就会出现一些无穷计数问题,如例题所示: 还是X、Y、Z三个节点。此时Y检测到它到X的路径费用由4增加到了60。此时节点Z的距离向量为: d(Z->X) = 5, d(Z->Y) = 1, d(Z->Z) = 0. 于是Y在更新向量时发现,咦,Z到X的距离只有5诶,那可以先到Z再到X,于是Y的距离向量更新为: d(Y->X) = (Z到X的最短距离)5 +(Z到Y的最短距离)1 = 6, d(Y) = 0, d(z) = 1. 这个逻辑显然是错误的**,因为Z到X的距离为5的前提是要经过Y,但Y更新后的路径又要经过Z,这就形成了一个选路环路(routing-loop)问题**. 因为Y的距离向量更新了.虽然是错误的,但它还是向Z发送了更新报文。 Z收到更新报文后,发现d(Z->X) = (Y到X的最短距离)6+(Z到Y的最短距离)1 = 7,这个距离小于直接到X的距离,于是Z也更新的自己的距离向量,然后又将更新后的距离向量发给Y。 Y收到后又更新向量为8,然后再发给Z。。。这样循环往复,更新报文在Y和Z之间传来传去,直到第44次迭代后,Z算出它经由Y的路径费用大于50为止。此时,Z最终确定到X的最短路径费用是直接到达X的费用50,而Y也得到了最短路径是经Z到X的费用51。 可以看出,虽然最后还是得到了正确的信息,即最后的50和51.但是需要这样一直更新,知道这个距离大于所以其他的最短路径为止,如果X和Y之间的费用为10000,Z和X的费用是9999时,就会出现计数到无穷(count-to-infinity)的问题. 为了解决无穷计数问题,我们有很多种算法可以实现,这里首先介绍毒性逆转. 它的基本思想是:如果Z的最短路径要通过邻居Y,那么它将告诉Y自己到目的节点的距离是无穷大.使得只要Z经过Y选路到X,它就会一直持续这个状态,这样Y也就不会从Z选路到X了,也就避免了环路问题. 我们将毒性逆转技术应用于上例。Y在更新自己的距离向量时,发现Z到X的距离是无穷大,于是它将d(x)无奈地更新为60,并向Z发送了更新报文。Z收到报文后更新自己的d(X)为50(直接选路到X),并发给Y更新报文(此时因为Z不需要经过Y进行选路,因此将告诉Y自己到X的距离为50)。Y在接收到Z的报文后,重新将距离更新为1 + 50 = 51,并告诉Z自己到X的距离是无穷大(实际是51)。Z收到报文后,发现最低耗费并没有改变,因此算法进入静止状态。 它的基本思想是定义一个最大的有效费用值,如15跳步/16跳步来表示无穷. 当出现无穷计数问题时,就会部分路由中一直反复更新链路,所以跳步当超过链路的最大有效费用值时就将距离当做无穷,视为距离不可达. 在之前的算法中,我们将路由器抽象称为一个点,当实际的网络中这显然是不可行的,因为假如有一个6亿目结点的网络,每个转发表要存储这么多的信息对于网络来说,交换量巨大会将整个链路淹没掉,同时我们也希望在每个网络管理中,可以自主控制网内的路由,实现管理自治. 基于这些需求,所以提出了层次路由的分层策略. 在之前我们需要先知道自治系统AS:自治系统是在单一技术管理体系下的多个路由器的集合,在自治系统内部使用内部网关协议(例如RIP、OSPF)和通用参数来决定如何路由数据包,在自治系统间则使用AS间路由协议来路由数据包(例如BGP). 网络是由不同的自治系统AS组成的,同时这些自治系统互相连接,所以在AS中既要设置AS内部的路由算法,还需要AS间的路由算法. 自治系统间(Inter-AS)路由任务: 假设有AS1,AS2,AS3三个自治系统如图所示连接,在AS1内某个路由收到一个在AS1之外的数据报,路由器要将报文转发给正确的网关路由器,所以对于AS1路由器就需要学习到哪些网络可以通过相邻AS2系统到达,哪些可以通过AS3到达,学习后需要将这些网络可达性信息传播给AS1内部路由器. 假设AS1通过AS间路由协议学习到: 子网X可以通过网关1c路由器到达,但是不能通过AS2到达. 对于路由器1d,需要利用AS内部路由协议信息,确定到达网关路由的最小费用路径接口L,然后在转发表中增加入口(X,L). 在多个AS间选择: 上述例题中,子网X只能通过AS3来到达,在网络中,可以会有多个AS系统可以到达,这也是需要AS间路由协议来完成,因此在实际中我们采用的是"热土豆路由"策略. RIP协议是随着BSD-UNIX操作系统发布的,是一种距离向量路由算法. 在RIP协议的实现过程: 每间隔30S,邻居之间交换一次DV,成为通告,假如180S没有收到通告说明链路失效了,就需要重新计算路由,并向邻居发送新的通告,邻居再依次向外发送通告,过程中可能出现无穷计数问题,利用毒性逆转来预防环路,同时也设置了最大跳步数为15跳,16跳为距离无穷大. RIP协议是应用层进程实现的协议,利用一个叫做routed的应用层进程实现,但是根据功能划分层,路由是网络层功能,因此是网络层协议.通告报文周期性通过UDP数据报发送. 路由器运行由应用层进程实现的RIP协议,那也会运行有UDP/TCP运输层协议,但是网络参考模型中,路由器只到网络层,因为上面层次的协议也是为了完成网络层的功能,路由器本身并不是端系统到端系统的. 路由器D的转发表: 路由协议OSPF全称为Open Shortest Path First,即开放的最短路径优先协议.OSPF 最显著的特点是使用链路状态算法,区别于 RIP 路由协议使用的距离向量算法. OSPF协议计算过程: OSPF的优点: 区边界路由器同时是一个区域的路由器也是主干网络的路由器 主干路由器负责在主干区内运行OSPF路由算法. 边界路由器负责AS内部的路由连接外部的AS自治系统 BGP协议 (Border GatewayProtocol):是事实上采用的标准域间路由协议,将Internet“粘合”为一个整体的关键. BGP为每个AS都提供了一种手段: **EBGP:**从邻居AS获取子网可达性信息,运行于不同 AS 之间的 BGP 称为 EBGP。为了防止 AS 间产生环路,当 BGP 设备接收 EBGP 对等体发送的路由时,会将带有本地 AS 号的路由丢弃 **IBGP:**运行于同一 AS 内部的 BGP 称为 IBGP。为了防止 AS 内产生环路, BGP 设备不将从IBGP 对等体学到的路由通告给其他 IBGP 对等体,并与所有 IBGP 对等体建立全连接,向所有AS内部路由器传播子网可达性信息. 基于可达性信息与策略,确定到达其他网络的“好”路径.容许子网向Internet其余部分通告它的存在. BGP会话通过两个BGP路由器交换BGP报文,通告去往不同目的子网的路径,与距离向量路由算法不同的是,BGP是到其他AS路由的距离,而距离向量路由算法是计算AS内部之间的距离. BGP报文交换基于半永久的TCP连接BGP报文: 当AS3通告一个子网给AS1时: BGP路由选择 网关路由器收到路由通告后,利用管理者输入的策略决策接受/拒绝该路由,与此同时路由器可能获知到达某目的AS的多条路由,基于以下准则选择:
IP编址
无地址分类地址CIDR
路由聚合
DHCP(动态主机配置协议)
第一次主机发出的广播(发现报文),所有主机都可以接收到,但是只有DHCP服务器才会进行响应(提供报文)
为该主机分配IP地址
yladdr就是DHCP服务器给该主机分配的IP地址;
这个提供报文也是通过广播地址发出去的
第二次主机发出的仍然是广播,也是告知其他的DHCP服务器已经跟现在的这个链接上了(请求报文)
在DHCP服务器第二次回应之后,就确认绑定该IP地址的了
网络地址转换(NAT)
NAT地址穿透
互联网控制报文协议(ICMP)
差错报告报文
网络探询报文
路由算法与路由协议
静态路由
动态路由
链路状态(LS)路由算法:
(1)c(x,y): 结点x到结点y链路费用;如果x和y不直接相连,则=∞// 初始化:
N’ = {u}
for 所有结点v
if v毗邻u
then D(v) = c(u,v)
else D(v) = ∞
Loop
找出不在 N’中的w ,满足D(w)最小
将w加入N’
更新w的所有不在N’中的邻居v的D(v) :
D(v) = min( D(v), D(w) + c(w,v) )
//到达v的新费用或者是原先到达v的费用或者是已知的到达w的最短路径费用加上w到v的费用
until 所有结点在N’中
距离向量(DV)路由算法:
则有:
选路环路(routing loop)和计数到无穷(count-to-infinity)
while(1){
等待本地局部链路费用变化或者收到邻居的DV更新;
重新计算DV;
如果DV中到达任一目的距离发现改变,通告所有邻居;
}
毒性逆转方法(The Reverse-Poison(Split-horizon) Hack)
最大度量算法(maximum metric)
层次路由策略:
Internet网络中的路由协议
AS内部路由协议
RIP协议(距离向量路由)
目的子网地址(IP地址+子网掩码)
下一个路由地址(IP地址+子网掩码)
到目的子网的跳步数
W
A
2
Y
B
2
Z
B
7
X
–
1
OSPF协议(链路状态路由)
距离向量路由协议的工作原理:运行距离向量路由协议的路由器周期性的泛洪自己的路由表,通过路由的交互,每台路由器都从相邻的路由器学习到路由,并且加载进自己的路由表中,而对于这个网络中的所有路由器而言,他们并不清楚网络的拓扑,他们只是简单的知道要去往某个目的应该从哪里走,距离有多远. 相比之下链路状态路由协议就要复杂的多.
AS(自治系统)间路由协议
BGP(边界网关协议)