客户端可能会需要通过协议名指定协议,但是Socket接口是用协议号指定的,这时候我们就需要使用getprotobyname()函数实现协议名到协议号的转换,该函数会返回一个指向protoent的指针。protoent的结构如下:
客户端还有可能通过服务名(如HTTP)标识服务器端口,需要将服务名转换为熟知的端口号。
这时候我们就需要使用getserbyname()函数来将服务名转换为我们熟知的端口号,该函数会返回一个指向结构servent的指针。servent的结构如下:
客户端可能使用域名(如baidu.com)或者IP地址(如187.33.53)标识服务器。
IP协议需要使用32位二进制IP地址,所以我们需要将域名或IP地址转化为32位IP地址。
调用Inet_addr()实现点分十进制IP地址到32位IP地址转换。
函数gethostbyname()实现域名到32位IP地址转换,返回一个指向结构hostent的指针。
循环无连接(Iterative connectionless)、循环面向连接(Iterative connection-oriented)、并发无连接(Concurrent connectionless)、并发面向连接(Concurrent connection-oriented)
传输层为运行在不同主机上的进程提供了一种逻辑通信机制,传输层主要负责将自己从Socket中收到的数据分成一个(UDP)或多个(TCP)Segment(报文段或报文)向下传递到网络层。或者负责将自己从传输层获得的Segment组合成数据向上传递给Socket,继而到达应用层进行处理。
传输层为用户提供的协议主要有两种:TCP(可靠、面向连接、有序、数据流传输,安全)、UDP(无连接,仅实现传输基本功能,像多路分用。)
TCP为了做到有序、可靠,就必须有相应技术,例如拥塞控制、流量控制、连接建立。
以上两种协议都不保证带宽和延迟。
传输层:提供应用程序(进程)之间的逻辑通信机制,仅用于网络层之上,依赖于网络层服务,增强网络层提供的服务。
网络层:提供主机之间的逻辑通信机制。
多路分用和多路复用是传输层实现基本功能必须实现的。
如上图:host2上面有进程P1和进程P2,host3、host4上分别有进程P4、P3。P3和P4都给主机host2发送消息,分别对应host2上面的P1和P2。
多路分用(就接收端而言),当P4和P3的数据报(Segment)从网络层传到传输层时,传输层通过每个Segment的“消息头”来确定Segment归属于哪个进程,而后将信息交给对应Socket。
多路复用(就发送端而言),host2上两个进程(P1、P2)收到消息后,返回响应信息,通过传输层时,传输层将信息分成一个或多个数据包,并加上头信息(注明要发送到的进程),然后交给网络层。
先看UDP/TCP报文结构:
每个从网络层到传输层的IP数据报会携带一个报文段(Segment),源端口地址、目的端口地址。
多路分用,传输层协议在收到IP数据报后,会根据报文“头信息”提取出目的IP和目的端口,然后整理发送到对应Socket,如此多种来源的数据报可以被导入同一个Socket。
多路复用,应用层调用指令创建Socket,不同Socket用二元组标识(目的IP,目的端口号),传输层协议在发送应用层不同进程数据报时,会分成多个报文段,并加上源主机IP地址,端口号,目的主机IP地址,端口号,以及其他必要信息,最后将传输层数据报发往网络层。
(传输层还做了其他事情,这里重点关注复用和分用故搁置不讲!)
下面是大概流程图:
TCP的分用和复用跟UDP很像,就是多了一个源IP地址、和目的IP地址,使用的是四元组标识。
服务器有可能支持多个Socket,每个Socket都有自己的Socket标识,Web服务为每个CLient开启不同的Socket,每个Socket对应一个进程中的多个线程。大致过程如下图:
UDP实现了传输层最基本的功能:复用/分用,简单的错误校验,之所以要错误校验就是因为网络数据的传输会经过很多层,再数据传输的过程中会发生数据乱码和错误的情况,增加错误校验机制可以减少发送数据的错误,如果接收方发现有错误,就可以要求发送方从新发送。
因为UDP功能简单,故其传输层报文的位数简单(需要8位)而TCP的报文头需要32位,故实现简单。同时因为其发送的数据不是按序发送,没有拥塞控制,所以应用端可以更好控制数据发送的速率,不像TCP协议,会在传输层为了拥塞控制来不断变换数据的发送速率,所以TCP的发送速率无法控制。
UDP无需建立连接,无前置rrl时间,所以比TCP发送数据更高效。
那么传输层是如何进行数据校验机制呢?
发送方把数据内容看做是16bit的整数,校验和计算:计算所有整数的和,然后将和的结果取反得到校验和,放到校验和字段。发送方在发送数据的过程中,连同校验和一起发送给接收方。
接收方收到数据后,再次计算数据的校验和,跟校验和字段中的位数进行对比,如果不一致,就表示发生错误,一致就检测不出错误。
为什么说检测不出错误?因为检测不出错误不代表没有错误,可能会存在数据位两位正好都在传输过程中取反 ,结果也不会发生改变,但是数据也发生了错误。
下面展示的是UDP的数据报结构,一共32位(bit)
在数据校验的过程中,如果数据位的最高位产生了进位,那么最高位的进位要加到最低位上,再取反。
这种数据传输不可靠的协议可以通过在应用层使用可靠的协议来保证数据传输的准确性和可靠性。
虽然传输层在截断分发传输数据报的时候会发送验证信息,如果发现接收端发现错误可以及时返回发送方重传或接收到的消息,从而保证信息传递的可靠性。
为什么会产生数据的错误,从应用层看,应用层只是简单的IP协议,当网络拥塞时,存在丢包的可能性,数据链路层可能受外界影响部分数据发生变化导致数据错误,因此就应用层而言,其如果在传输层不经过任何处理,那么面对的就是一个不可靠的数据通路。
我们在发送方和接收方通过传输层的协议保证数据对于两端的应用层是可靠的,即应用层面对的是一个可靠的通路,这样就减小了应用层软件设计者的设计压力。如上,传输层面对的就是一个不可靠的通路(channel),传输层通过第一段中提到的应答机制实现数据发送端和接收端的一致。
结合下图可以更好理解这种思想:
现在们假设应用层已经面对的是一个可靠的协议,传输层需要将实际不可靠的信道变得可靠,主要依靠有限状态机和ARQ协议(Automatic Repeat reQuest)来实现。
可以理解为一台机器会对应不同的状态,当状态发生改变之后,这台机器会进行不同的操作,这里的机器就是传输层,不同状态就是接收到ACK信息和重传请求两种状态,有限是指,一个状态向另一个状态的转换是固定的,不会同时转向多个状态。
下面是状态机的示意图:
该协议是基于拥有重传机制的rdt2.0协议。
该协议有ACK确认机制,当接收方返回确认收到无误信息后,发送方才会进入下一个数据发送流程。
下面模拟数据收发过程,分析该协议如何利用有限状态转换机和消息接收确认机制实现变不可靠信道为可靠信道的过程。
发送方分析:如上图,左边是消息发送方,rdt_send(data)应用层将数据发送到传输层,传输层将数据分段snkpkt=make_pkt(data,checksum),同时添加验证码(用于检查错误),然后不可靠发送数据udt_send(sndpkt),发送完毕,状态机发生状态迁移,由等待请求变为等待ACK 或者 NAK响应。
下面有两种情况,情况一,接收方接收到数据,并且数据无误,则发送方状态机通过rdt_rcv(rcvpkt)&&isNAK(rcvpkt)来接收到ACK反馈,后跳转状态到等待接收下一条指令(Wait for call from above)。
情况二,接收方接收到数据,数据发生错误,返回NAK信号,则发送方状态机还是通过rdt_rcv(rcvpkt)&&isNAK(rcvpkt)来接收到NAK反馈,之后跳转到本状态(Wait forACK or NAK),并且重新发udt_send(sndpkt)。
接收方分析:如上图,右边是消息接收方。两种情况。
情况一,在接收到错误数据时,rdt_rcv(rcvpkt)&&corrupt(rcvpkt)为true,则执行udt_send(NAK)命令,发送错误接收到发送端,同时状态机跳转为本状态(因为仅有一个状态)。
情况二,再接收到无误数据时,rdt_rcv(rcvpkt)&¬corrupt(rcvpkt)为true,则udt_send(ACK)发送确认信息到发送端(send)。
Rdt2.0中使用ACK和NAK发送到发送方来确认接收端收到数据的情况,但是没有考虑发送的ACK/NAK消息错误问题,Rdt2.1就是解决这个问题的。
所以我们需要在发送方传输层的状态机中增加状态,用来处理当返回ACK/NAK消息错误情况的。
这里先假设我们发送方收到了错误的确认消息,然后重发,接收方就接收到了两次一样的数据,故需要在数据上增加序列号,来区分两次一样的数据,从而让接收方可以判断重复数据,而将重复数据丢弃。
序列号只需要0和1两个即可,因为这里Rdt还是停等协议,收不到确认消息不会发送下一条。
下面根据一张状态图来分析,当ACK/NAK发生错误时,发送方状态机的步骤流程:
从上面开始看起,应用层发送数据到传输层rdt_send(data),传输层收到后,分组并添加验证码和组(包)号,然后发送到接收端udt_send(sndpkt),同时进入等待状态(Wait for ACK or NAK 0)。
这时候如果接收方返回数据错误或返回确认信息有误,都会使corrupt(rcvpkt)||isNAK(rcvpkt)的值为false,从而触发重发机制,重发组号为0的组。然后继续进入原状态。
(这里有个注意点,重发后,接收方接收到了两个序号(序列号)为0的数据包,就会判断重复,所以会保留一个丢掉一个,这也是我们加序号的原因)
如果重发后,接收端返回的是正确的信息同时为ACK,则状态机跳转到下一状态等待组号1的包从上层发送(Wait for call 1 from above),然后发送下一个数据包到接收端。
然后接受端还是有两种情况跟数据包0情况分析一致就不再分析。
下面是接收方流程图:
我们看到Ret2.2返回消息还是两种ACK(信息正确接收)/NAK(消息存在错误),这样两个消息可以合并为一个消息ACK 序号即可,从而减小接收方存储容量,提高效率。
那如何判断接收方返回的接收错误消息?接收方只会返回已经接收成功的包,例如ACK 0,如果下一个接收到的1发生错误,还会返回ACK 0,则发送方就明白包1发生错误回到原状态重新发送。
整个过程如下图:
如上图,在0号消息通过udt_send(sndpkt)发送出去之后,状态机来到状态Wait for ACK 0,如果返回消息为1号接收成功,证明0号发送失败。isACK(rcvpkt,1)|| corrupt(rcvpkt)会因为返回ACK错误、序号为1、消息存在错误而为false从而触发重发机制,重新发送0号序号数据包。
左下是接收方的流程逻辑。
Ret2.2中减小了发送方的信号类型,只使用ACK即可,但是前面所有假设都没考虑到丢包的情况,Rdt3.0重点解决了发送包丢包的问题。
如果我们发送的数据包在传输过程中因为网络拥塞(路由储存不够丢包等)等情况丢失,那么我们一定收不到接收方返回的任何信息。
所以我们需要给发送方设定一个超时时间(timeout),一旦发送数据包后超时时间到就重新发送数据。如果在超时时间内收到确认消息就“无事发生”。
我们需要给接收方增加计时器来记录超时时间。
下面给出修改后发送方的状态机图:
这种情况值的分析,上图我们可以看到,接收方因为网络延迟原因ACK1到达发送方前,发送方就重发了包1,而后收到了接收方对上一条包1的迟到回复,然后发送包0。
但这就产生了问题,第二个包1的回复会怎样被发送方处理呢?很明显因为接收到第二个ACK1时,发送方处在等待ACK 0的状态中,所以会触发发送方重发机制重发包0,接下来只要一切正常(网络无延迟),接收方会发送两次ACK0,两次ACK0都会在发送方接收到第一次ACK0时准备发送包1时到达,从而一切回到正轨(后面是个人猜测,有不对还请斧正!)
要研究这个问题我们要先知道原来的Rdt3.0协议的停等模式所带来的带宽的严重浪费问题。
假设我们现在的链路是1Gbps,15ms端到端传播延迟,1KB分组,我们先计算1Gbps链路传送1kb需要的时间(链路是以bit为单位传输的),所以计算过程如下:
可见我们需要8ms。
那么我们发送方的利用率:(发送方发送时间百分比)
RTT是数据往返时间,两次数据发送需要等待一个数据往返时间。所以计算出来的发送方对带宽的利用率是0.00027,并且在1Gbps的链路上每30毫秒才发送一个分组,1秒才发送33KB。
所以可以看出Rdt3.0协议限制了物理资源的利用,所以我们就需要引入流水线机制。
引入流水线机制前,我们的发送过程如下:
引入流水线之后,我们的发送过程如下:
我们像流水线一样,一个发送完毕在等待的过程中,继续下一个消息发送,这样就相对压缩了RTT时间。这样我们的利用率就提高为0.0008,提高了3倍。
要想实现流水线就需要用到滑动窗口协议,当滑动窗口协议。
滑动窗口协议有哪些特征呢?首先它规定的窗口概念允许使用很多序列号(原来的0和1已经不够用了),窗口尺寸N:代表最多有N个等待确认的消息。
为何为滑动?因为随着协议的运行,窗口序列号空间会向右(前)滑动,发出的已经接收到的消息会被移出窗口,从而扩展窗口的有效容量。下面是示意图:
send_base是窗口起始位置。nextseqnum指向窗口中下一条要发送的消息。window size指窗口总共能够支持多少消息等待ACK返回。窗口内的黄色部分,没有一条收到ACK后,窗口就会向右滑动。绿色代表已经发送并接收到返回信息的消息,已经被划到了窗口以外。黄色代表已经发送出去消息但是还没有收到ACK的消息,蓝色代表窗口内剩余的可用序号。最右侧是还没有移到窗口内的部分。
其是滑动窗口协议的一种具体实现,将k-bit序列号包含在分组的头部,窗口尺寸为N,因为支持多个发送方流水线发送请求,所以接收方接收数据后的确认消息为累加确认,如接收方接收到5个数据,返回确认信息ACK 5 即代表序号5以前的数据都被我收到了!
那么GBN协议的接收方如果碰到数据乱序发送该如何处理?比如本来3接收完毕,应该该4了,但是却收到了5,这时接收方就会直接丢掉5,返回ACK 3,表示接收到3号。
GBN发送方的重传机制通过计时器(timer)控制,一旦到规定时间,已经发送的数据没有得到回应的序号就会被依次重新发送,下面分析具体细节:
数据发送方的有限状态机:
从图中最上方看起,先发送数据rdt_send(data),然后三个条件判断:
右方就是计时器(timer)时间到后,重启计时器start_timer并且重新发送已经发送但未收到回复的消息的过程,udt_send(sndpkt[base])...
下方过程是发送方接收到接收方确认消息(ACK)后的操作。先判断接收到的消息是否为ACK正确接收(rdt_rcv(rcvpkt)&¬corrupt(rcvpkt)),然后根据累计确认消息序号再加上1,得到新的窗口左端(也意味着已经收到ACK的部分被移出窗口),然后判断是否窗口中所有已经发送的消息都被确认(base=nextseqnum),如果为true,则停掉计时器,如果为false,保持计时器开启。
数据接收方的有线状态机
在开始等待之前,有一个期待序号expectedseqnum=1(1是下一个要取入的序号),如果不是期待的序号,则会被直接丢弃,如期待的是1,但是因为链路原因,2先到达,直接无脑丢弃,我只需要序号1的数据。
默认(default)如果接收序号正确,则会执行udt_send(sndpkt)发送逻辑,上层收到(rdt_rcv(rcvpkt))后判断是否无误、是否是期待的序列号数据(hasseqnum(rcvpkt,expectedseqnum)&¬currupt(rcvpkt)),如果都为true,则正常向上层发送数据。deliver_data(data),然后打包确认信息(sndpkt = make pkt(expectedseqnum,ACK,chksum)),最后发送确认信息(udt_send(sndpkt))并将期待序号加1(expectedsqnum++)。
上面的协议导致严重的资源重传,从而产生了大量的资源浪费。因为每次重传都是将其序号及以下序号全部重新发送。
该协议,接收方接收一个消息就确认一个消息(不同于GBN协议累计确认),接收方也有一个滑动窗口。
该协议接收放增加了缓存区,当接收到没有按序提前到达的数据,就将数据放到缓存区中然后发送确认接收。
发送方每个数据项都对应一个计时器(timer),当计时器时间耗尽时,仅会重发对应的一条数据。
协议具体图示如下:
发送方:黄色部分是已经发送但还未收到接收方ACK的数据,青色是已经收到接收方ACK的数据,蓝色是在窗口内还没有相关序号数据发出的空余的序号。当最左端收到ACK,则窗口则会向右滑动至第一个已发出但没有回复的数据(黄色)。
接收方:接收方窗口中的数据是等待接收的数据,灰色是还没有收到的数据序号,红色是乱序收到已经放在缓存区中并返回ACK信息的数据。蓝色是空闲的还没有用的序号。最左端每确定收到一个数据后窗口就会向右滑动。
下面模拟一次数据的发送:
发送方依次发送四个序列数据0、1、2、3。2组数据在传输过程中丢失,接收方先收到0组数据,窗口向右滑动1位,然后又收到1组数据,窗口继续向右移动1位,此时接收方最左边是期待的第2组数据,但是2组数据已经丢失。第三组数据到达后,因为2组数据还没收到,3组数据就被放到数据缓存区中,并返回ACK信息给发送方。此时2组数据的timer时间还没有耗尽,发送方继续发送4、5组数据,4、5组数据都顺利到达,被接收方放到了数据缓存区中。此时发送方中的所有序号都已经用光,因为2组数据还在等待,故窗口无法右移。接收方同样期待的数据(窗口中的数据)除了2组数据都已经接收到了,窗口因为2组数据也无法右移。同样窗口停止等待2组数据。接收方2组数据的timer时间耗尽,重发2组数据,接收方接收到2组数据后,连同之前放在数据缓冲区中的3、4、5组数据一块发送给上层应用,同样窗口一下子直接向右移动4位最左端(send_base)指向6组数据。发送方同样在收到接收方的ACK后,直接向右移动4位,开启新的数据发送。
SR的困境(注意点)
像上图两种发送与接收的情况,最后两个接收方虽然都是接收到了0号数据,但是却不是同意个数据。
上面的接收方,因为返回的三个ACK消息都丢失了,所以当0组数据timer时间到后,重新发送的还是原来的0组数据。
下面的接收方收到了发送方的0、1、2号消息,跟上面一样,窗口都移动为3、0、1。当接收方返回0组消息ACK时,发送方同时发送3组数据,但是3组数据丢失了,这样接收方最左端在3组数据重发之前,窗口就无法向右移动,这时发送方已经接到了1组数据的ACK,窗口移动到了2、3、0的位置,第二个0组数据发送,但是接受方因为窗口一直无法右移一直在等待第一个0组数据,因为接收方仅识别序号,故把发送方第二个0号数据当作第一个0组数据进行接收从而导致数据错乱。
解决这个问题就是增加序号总量,这里我们必须满足:发送方窗口序号容量 + 接收方数据容量 <= 要发送的数据组数量。
source port\dest port 源端端口号/目的端端口号、
sequence number 序列号,这里序列号是根据已经发送的字节数来分配的,比如上一条segment是8字节,则该条报文段(segment)的序号就是9。
acknowledgement number 对某一报文段的收到ACK回复。
URG 紧急的数据(用序列号标识)通常不用、ACK 有效ACK标识、PSH 立即向上层发送数据,通常很少用、RST,SYN,FIN 开始建立连接,维持连接,终端连接、Internet checksum 互联网验证位(码)。
application data 数据发送位。
这里省略了Host A 和 Host B建立TCP连接的过程!
左边例子:HostA 发送第一个报文段(segment),序号为92,大小是8字节。Host 收到消息之后回复收到ACK 100(TCP采用累计确认机制,因为92字节加上8字节为100字节),表示收到100字节数据,下一个发送方应该从101序号发送。但是不幸接收方的ACK 100丢失了。发送方等待时间耗尽,则重新发送92号数据,然后接收方再次发回确认信息。
右边例子:在流水线(窗口协议)的控制下,发送方依次发送两组数据92、100,并且收到ACK 100 ACK 120,但是接受方timer设置太短,在收到ACK前已经重发了92号数据,接收方收到数据后发现不是自己期待的121号数据,故返回上次收到的序号确认 ACK 120。
再看下面例子:
因为是累计确认,所以即使接收方Host B接收返回ACK 100丢失了,但是ACK 120相当于累计确认了100号数据,故发送端100号数据不需要重发。
情况一、当接收方按收到了发送方传来的数据时,并不会立即返回ACK消息,会等待50ms,看看是否能够收到下一条数据。
情况二、当接收方收到发送方传来的数据的时候,并且发送方还存在之前发送过但并未收到ACK时,接收方会在收到数据后立即返回ACK信息。
情况三、当接收方收到不是自己期待的数据时,会返回前一个成功收到的数据序号。
情况四、当发送的数据报文有缺口时,会发送报文缺口处的序号给数据发送方。
当TCP协议建立连接的过程中,接收方会分配缓冲区(buffer),缓冲区中专门用来存放接收到的数据包括乱序到达的数据,因为buffer有大小,如果发送方发送数据报文(segment)速度太快,那么接收方的buffer就会被占满,从而导致数据丢失。并且上层应用读区速度要慢于接收速度,故很需要流量控制。
如下图:
如上图,最右边箭头是应用进程,它会读区缓存区中的数据,绿色条纹部分是已经占用的buffer部分,蓝色是空域部分。
spare room 可以如下计算:LastByte指buffer中已经有的字节,LastByteRead指应用进程已经读区的数据字节数)
RcvWindow = RcvBuffer -(LastByteRcvd -LastByteRead)。
每次接收方接收到Segment时,都会在返回ACK报文中的Revwindow项中标注buffer剩余的空间大小,发送方收到之后就会做出相应的合理的流量发送,调整发送速度。
当接收方接收数据后,buffer被占满后,返回ACK信息含有Revwindow满的信息,接收方收到后并不是不会再向接收方发送消息(因为如果没有发送方给接收方发送消息,接收方就无法通过ACK把最新的buffer容量回馈给发送端),而是为了避免死锁,还是会不断向接收端发送小数据端,以期获得最新buffer情况。
都由client端发起连接请求,第一次Client初始化自身缓存区和序列号,然后向服务端发送SYN标识为1的请求,第二次接收方再收到之后,分配自己的buffer和Seq(序列号)等信息,然后发送ACK确认消息。第三次Client在收到ACK消息后,再发送SYN=0的消息,表示自己收到了服务端确认收到的信息。连接建立。如下图:
之所以Client等待(第四步)原因,是自己的ACK信息存在丢失可能,等待一会是server端一段时间收不到ACK重新发送FIN,确保两端都断开链接。
在网络中太多主机同时发送了太多数据,而导致网络处理不过来,路由缓存被占满,网络不通畅。跟流量控制不同的是拥塞控制关注的是共同的网络,流量控制关注的是发送方和接收方。
网络拥塞多表现为:路由器缓存被占满,数据包丢失;数据在路由器的缓存中排队,导致延迟时间过长。
假设路由器有无限缓存,同时因为有无限缓存,数据不会丢失,所以发送方不需要重发。
original data就只有要发送的数据包,接收端也仅需要接收新的数据包即可。
当Host A、Host B向网络中同时快速发送大量数据包时,路由器处理不过来就将数据包放到缓存中排队发送。这时我们可以根据下面图分析,发送速率in 和 输出速率out的关系:
当发送速率超过C/2(带宽的一半)时,数据就会开始排队,所以一直会有输入,排的队伍越来越长,而out数据远慢于in数据的速度,故可以从右图中看出当发送速率in无限接近C/2时,延迟时间(delay)也就无限长了。
为什么时一半带宽,我的考虑是网络的分层结构,需要预留空间给其他层增加消息头,同时因为双工通信原因,所以需要预留一半空间(个人理解)。
因为这里路由器的缓存是有限的,所以会存在数据丢失可能性,TCP协议会重新发送数据,重新发送的数据会占用流水发送增大in的大小,图上表示为‘in,我们要明确重新发送的数据因为之前发送过,所以重发时不算在有效数据中,它只是在弥补之前的数据丢失。
由上可知,因为路由缓存有限,存在数据重新发送,导致有效数据占比变低,所以当发送方速率和数据量增加时,out会在顶峰后,无限接近0,因为你速度越快,丢失的数据包越来越多,触发发送方重发,重发数据越来越大,故有效输出变得极低,无限接近于0。
上图中每个颜色都是一条数据流,我们观察红线(从Host A发送到 Host C)它发送数据包经过了两个路由器(上、右),再观察绿线(Hsot D 到 Host B),绿线发送经过两个路由器(右,下),这两条线路共同通过右路由器,当Host D、 Host A发送的数据过大过快时,右路由器产生拥塞,A、D两个主机的数据都有可能丢失,这样A的数据经过上路由器时的处理被浪费(上游处理能力全部被浪费掉)。
类型一:端到端拥塞控制(Internet网络采用)
不需要网络层配合,端系统通过丢包(loss)率、和延时(delay)情况等做出网络是否发生拥塞的判断。TCP即采用该种方法。
类型二:网络辅助拥塞控制
路由器会向发送方显示地反馈网络拥塞信息。简单的拥塞指示(改变数据中某一位代表拥塞),同时这种拥塞控制可以指示允许的最大速率。ATM网络等就采用该方式。
简单介绍ATM的网络辅助拥塞控制
如上图,RM cells 是发送方在发送数据过程中夹杂的RM(控制 cell),每经过交换机(ATM中的数据包),交换机都可以指示NI bit位 (不允许速率增长) 、CI bit位(拥塞指示)、ER (能够支持的最小速率)。这些RM到达接收方后,接收方会发回发送方,发送方可以根据这些RM cells中记录的信息获得拥塞情况,并作出相应调整。
当然data cells中也可以包含EFCI位,发生拥塞的交换机可以将EFCI位置1,当接收方发现EFCI为1,则会在接下来的RM cells中置 CI(拥塞位)发送到接收端。
采用端到端拥塞控制,根据发送端发出的数据被接收到的数量,确定自己是否产生拥塞,有两种情况:
拥塞控制下,发送速率不是直接开到最大,开始时是指数增长,当到达Threshold时,就变为线性递增,当达到拥塞点时,就更新Threshold(Threshold = 1/2 * 拥塞点速率),然后根据引起拥塞类型判断拥塞控制的速率应该降到何种程度,如果是timeout事件引起,则速率直接降到0,如果是3次重复的ACK,则降到拥塞点的一半继续线性增长,线性增长每次的值为MSS。
上面的过程就像下图所示,所谓加性增-乘性减。
传输层针对的是端到端层面的连接,UDP和TCP协议看到的网络层是透明的;网络层需要两端的路径中所有网络设备参与其中(比如路由器),网络层协议
主要功能:转发:将从传输层来的数据报从输入端口导向合适的输出端口。路由,根据自身的路由表和路由算法得出要将数据输入到哪一路径上,这就叫做路由。
不同网络层协议如下:
不会事先为传输确定固定的传输路径,每个数据报传输的路径可能不同,可能会产生数据报乱序到达的问题。
代表网络:数据报网络(datagram network)
首先为系列分组确定从源到端的传输路径,确定之后,每个数据报都会携带一个VCID虚电路号,路径上的所有网络设备(路由)都要参与维护虚电路的连接状态,每个数据报传输的路径相同,传输结束后拆除连接。
代表网络:虚电路网络(virtual - circuit network)
可以通过预存技术实现电路网络的通信保障,同时源主机和目的主机维持逻辑连接。
虚:不像电路网络靠分频来分配用户,虚电路网络依然是分组交换网络,所以没个数据报发送过程中都是占用完整带宽。
同一条路径,在每段上的VCID不同,因为在不同的链路,其最大带宽有区别,所以支持的链路有不同,虚电路号范围自然不同。
网络设备(路由器),在收到数据报后,根据转发表改写到来的数据报,将VCID改写为下一个需要的VCID。转发表大致格式如下:
它是实现虚电路路径中生成转发表,建立连接路由时的规范,是实现虚电路的协议。
下面是建立连接,拆除连接的过程:
VCID在初始呼叫和接受呼叫过程中就被确定,(路径选择过程)数据流流动完毕后,连接就可以被断开。
该协议目前被ATM、帧中继网络广泛采用,Internet是数据报网络,没有采用信令协议。
因为没有固定线路,所以每个路由器都要根据数据报中的目标地址IP做出判断,指出其下一个路由器地址,但是全球有40亿IP地址,如果每一个地址都分别在路由器中有对应,那么对于路由器来讲是不可实现的,故我们用地址范围取代具体的地址值,这样就大大减少了路由器处理的容量,路由器对应的IP范围如下:
当数据报的地址对应两个范围,两个范围对应不同的输出端口,这时我们应该以范围最精确的对应输出端口为主。如下图所示例子:
对应匹配的有两个接口1、2,因为1接口的范围更小,更精确,最终会从链路接口1中输出信息。
路由器根据路由协议计算出路径对应信息,储存在转发表中,IP协议负责规定寻址规范,规定数据报格式,ICMP协议和IP协议相伴而生,负责规定数据报差错控制规范,路由器发送信令信息规范。
在网络层的下一层数据链路层中,因为不同的链路有不同的MTU(最大传输单元),所以当IP数据报大于链路层的MTU时,就需要对IP数据报作分片操作,就是将数据分多个部分传输,这些小的部分就是片。
路由器在进行分片之前,会按照IP数据报中的标志位中的DF位来确定是否进行分片,如果IP不允许分片,则会被丢弃。
因为internet网络的简单原则,分片之后,要到目的主机之后才能够从新组装。设想如果在网络层中某个主机组装,那么势必还会遇到MTU更小的链路,从而导致再次分片,浪费资源拖慢效率。
分完的片,首部信息中ID段的值还是相同的,用来确定是同一个数据报的不同分片,如果接收端主机在接到片后,发现少片,会等待一定时间,一旦超时,则会丢弃,对应数据报的全部片信息。
路由器根据自己生成的报文数量赋予IP报文标识(ID)字段值,没有什么参考意义,因为不同路由器会都会发送第n条数据。
标志位3个bit分别是保留位、DF(Don‘t Fragment)、MF(More Fragment),
那么如何区别MF的0,到底是分片的最后一片还是没有分片,这需要我们根据片偏移来判断。
片偏移量占用13位,以8字节为单位(即每增加1,代表8字节)。所以片偏移只能是8字节的倍数。
上面的问题很好回答,如果片偏移为0,且MF=0,肯定是没有分片,如果MF=0,且片偏移为非零,则肯定是分片的最后一片。
IP地址32位bit,编号标识主机、路由器的接口,因为二进制的32位数不便于记忆,所以一般人们都转化为每八位为一组的十进制表示,如:223.1.1.1
前面提到过,全世界拥有的计算机数量超过40亿,每一台都有一个唯一IP,放在路由器的转发表中无疑是不现实的。
IP地址的组成:网络(子网)号 + 主机号;
所以我们在分配地址时,将同一地区,互相通信不需要跨越路由器的多台主机标识为一个子网,用IP地址的前一部分标识子网,在同一个子网中的主机,路由器在转发时看作一个整体。
主机用来和链路通信的,路由器用来和链路通信的部件叫做网络接口,同一个主机/路由器可以有多个网络接口,如笔记本电脑,有无线网络接口和有线网络接口等。
如上图,根据网络ID占用位数和HostId占用位数的不同,来将Ip地址分为不同类型,A类地址占用8位。这样剩下24位就负责子网的定位,这样就导致子网数量过少。B类地址占用两个8位,作为网络地址。C类地址占用三个8位作为网络ID,为了区别不同类别的IP地址,用最前面的位数来识别,比如第一位为0的就是A类地址。D类地址没有HostID,一般用来作为广播之用。E类地址多用于研究。
一些特殊的IP地址也不能使用,如下:
较之前的IP结构,我们不同网络还需要划分不同子网,故结构作如下变更:
那么紧跟着另一个问题就又来了?有了子网,我们应该如何确定一个IP地址是否划分子网,划分子网占用了HostID的多少位?
所以我们就引入了子网掩码的概念。
子网掩码取值很简单,就是将网络ID(netID)和子网ID(subID)对应为全部取1,HostID位全部取0,在这一规则下,不同类型网络的点分十进制形式如下:
如何根据子网掩码和IP地址结合起来,知道子网的地址范围?
只需要将IP地址和子网掩码地址按位进行与运算(当二者相同为1,否则为0),最后就能得出子网地址范围:172.32.0.0 ~ 172.32.1.255 因为HostID全0或全1有特殊含义所以真正范围为172.32.0.1 ~ 172.32.0.254.可见这里第三个8位七位都被作为subID,且SubID中全为0,SubID取值也会影响HostID的取值范围。下面是以C类IP为例,不同SubID取值,hostID可用范围的表。
略微解释,因为SubID是HostID的一部分,所以SubID的取值就决定了HostID的整体取值范围。
上面按类对IP地址进行划分,存在IP地址浪费的情况,比如当一个单位需要2000个IP地址,分配给其一个A类地址太多,一个B类地址太少,无论怎么分必然会造成浪费。同时加上子网掩码,也使IP变得更为复杂。
CIDR就解决了这个问题,它将IP地址中NetID和SubID合为一个叫做前缀。在IP地址后加上一个信息来指定前缀长度,这样就摆脱了IP类的束缚。下面展示使用前后对比:
使用CIRD的优点如下:
提高路由效率,将多个子网构成一个更大的子网,这样在主干网等上层网络的路由器中,路由表就可以简化,如下表:
正常是三个子网三个项,虽然都对应同一个输出端口2。
通过改变前缀我们就可以将表项简化为红色部分,这样就提高了路由效率。
在本地计算机中指定即可:
通过网络中的DHCP服务器动态分配,在动态分配IP地址同时,也会动态分配子网掩码、默认网关地址、DNS服务器名称及IP地址。
优点:随时可用,允许地址重用、支持地址续租、支持移动用户加入网络。
流程:要加入网络的主机先发布广播报文(DHCP discover),如果网络中有正在运行的DHCP 服务器的话,就会发送DHCP offer进行响应,收到响应后,主机再次发送DHCP request(请求报文),收到后DHCP服务器分配IP地址 DHCP Ack(确认报文)。
随着IPV4地址的分配殆尽,NAT思想横空出世。它一般在路由器中实现,通过一张地址转换表,将只能在内部使用的IP地址转换成能够在公网使用的公网IP。
这样整有几大优势:
A、工作原理:
当路由器接收到来自本地网络的IP地址和端口号后,就会将信息中的IP地址修改为公网IP地址,当有消息进来后,再根据转换表中内容将公网地址转换为对应本地地址进行消息发送。
因为路由中有NAT机制,所以P2P类应用如何绕过NAT直接实现两个端的通信?
方案一:静态配置NAT,将路由器中NAT转换表中P2P应用对应的公网地址和端口固定下来,这样就可以实现P2P通信。
方案二:自动配置,利用UPnP(互联网网关设备协议)让一端能够实时获取到NAT转换表中信息,能够实时控制转换表中公网和本地IP地址的对应关系。
方案三:中继(如Skype),通过互联网总一个服务器实现连接的保持,这样也可以实现连接。如下图:
违背了层次性结构的原则,因为路由器属于网络层,网络层应该只负责第三层功能,但是NAT却干涉传输层,将传输层数据进行修改,这就违背原则,虽然解决了IP地址不足的问题,但是很多人还是认为IPv6才是解决IP地址不足问题的最终归宿。
ICMP协议用来处理数据再发送过程中出错、线路探寻等情况。ICMP报文有两种类型:
类型一:差错报告报文,以下五种情况会触发差错报告报文:
类型二:网络探寻报文
对应不同的类型和代码代表不同的情况,具体情况明细如下:
以差错报文举例,见ICMP报文装入IP数据报文的结构:
IPv6取消了IPv4可变首部长度,固定位40字节,同时取消了可选项,代之以扩展首部。
取消了校验和,减少每跳处理时间;同时衍生出ICMP6协议,该协议支持多播组的管理功能。
优先级:通过该字段指定数据的优先级,供网络更合理分配资源。
流标签:标识数据所用于的服务类型(讨论中)统一流中数据用于同一服务。
源地址/目的地址:32位变为128位。
压缩形式不允许同时使用多组双冒号代替0。
IPv4-嵌入形式将将一部分用0占位一部分用1占位,留32位来集成IPv4的地址。
多播和任意播类型的地址只能做目的地址。
隧道技术:
在遇到仅支持IPv4的路由器时,上一个支持IPv4和IPv6的路由器就将IPv6的数据报文再封装一层变成IPv4数据报文,直到遇到支持IPv6协议的路由器后再拆为IPv6数据报。