文章目录
- IP协议
- 预备知识
- 网络层解决的问题
- 如何理解IP地址
- 关于路由器
- IP协议格式
- 各个字段的含义
- IP报头的本质
- IP如何将报头与有效载荷进行分离
- IP如何决定将有效载荷交付给上层哪一个协议(如何分用)
- 32位源IP地址和32位目的IP地址
- 重新理解socket编程
- 分片与组装
- 数据链路层解决的问题
- 最大传输单元 MTU
- 分片和组装是谁干的
- 分片的过程
- 组装的过程
- 如何区分出普通报文和分片报文
- 分片报文如何聚合在一起
- 分片报文丢包的问题
- 不建议分片
- 如何避免分片
- 网段划分
- IP地址的构成
- 如何进行查找目标主机
- 分段划分法
- 子网掩码划分
- DHCP协议
- 特殊的IP地址
- IP地址的数量限制的问题
- 私网IP和公网IP
- 路由
- 路由表生成算法
IP协议
IP协议全称为“网际互连协议,IP协议是TCP/IP体系中的网络层协议
预备知识
网络层解决的问题
TCP和IP协议的区别,各自负责的是什么任务
完成一件很大的事一般需要有两种角色:一种人来决策,一种人来执行
1)对于网络传输任务来说,TCP协议负责决策,IP协议负责执行
2)TCP作为传输层控制协议,其保证的是数据传输的可靠性和传输效率
3)但TCP提供的仅仅是数据传输的策略,而真正负责数据在网络中传输的则是传输层之下的网络层和数据链路层
- IP协议的能力是以较大概率将数据跨网络传输到对端主机上,也就是IP向上层提供了这样的功能
- TCP协议在此基础上维护网络传输的可靠性和效率.
4)网络层解决的问题是,将数据从一台主机送到另一台主机,因此网络层解决的是主机到主机的问题.
一方传输层从上方进程拿到数据后,该数据贯穿网络协议栈进行封装和解包,最终到达对方传输层,此时对方传输层也会将数据向上交给对应的进程,因此传输层解决的是进程到进程的问题.
例子:
1)双方在进行网络通信时,发送的数据并不是直接从一方的传输层直接发送到了另一方的传输层
2)而是需要传输层将数据继续向下进行交付,在网络层和链路层经过数据封装后再通过网络发送到对方主机
3)对方主机收到数据后也同样需要在链路层和网络层进行数据解包,此时对方的传输层才拿到了发送过来的数据,然后再继续将该数据向上进行交付.
网络通信必须贯穿网络协议栈!就像两个人在互相送数据,这两个人分别在两栋楼的四楼,如果一个人要将数据交给对方,那么这个人就必须先从四楼走到一楼,然后在路上经过路径选择到达对方楼下,最后再上到四楼将数据交给对方
其中,送数据的这个人从四楼下来的过程就是数据封装的过程,这个人在路上经过路径选择到达对方楼下的过程就是数据路由的过程,而这个人再上到四楼将数据交给对方的过程就是数据解包的过程.
网络层要解决的问题就是,将数据从一台主机送到另一台主机,也就是数据的路由
TCP+IP 保证数据可靠的从一台主机送到另一台主机的前提
当双方在进行基于TCP的网络通信时,要保证将数据可靠的从一台主机送到另一台主机,前提是发送方要有将数据送到对方主机的能力,要是发送方连将数据发送给对方的能力都没有,那就更不用谈可靠的将数据送给对方主机了.
- 需要注意的是,发送方有将数据送到对方主机的能力,并不意味着发送方每次发送的数据都能够成功的发送到对方,但如果发送方连将数据发送给对方的能力都没有,那发送方基本就不可能将数据发送给对方.
- 一旦发送方有了将数据发送给对方的能力,就算发送方某次发送的数据没有成功到达对方,此时上层TCP由于没有收到对应数据的应答,此时上层TCP会要求进行数据重发,直到数据成功发送到对方主机为止.
也就是说,在网络层有能力将数据送到对方主机的情况下,虽然网络层不能保证每次都能将数据成功送到对方主机,但在TCP提供的可靠性策略的保证下,最终网络层就一定能够将数据可靠的发送到对方主机.
如何理解IP地址
IP地址包含两个部分,网络号和主机号 IP=网络号+主机号
- 数据包在网络中传输,需要网络号来查找通信主机所在的网段,到达所在网段后,再通过主机号查找该局域网中的通信主机
就比如去广州的广州塔玩, 首先我们是先要到广州,然后在广州找广州塔
关于路由器
数据进行的网络传输一般都是跨网络的,而路由器就是连接多个网络的硬件设备,因此数据在进行跨网络传输时一定需要经过多个路由器
关于路径选择的问题
1)数据路由就像我们旅游一样,当确定了要到达的目标主机后,就需要寻找最短的路径到达该目的地,目的地直接决定了数据路由时的路径选择,这也是跨网络找到目标主机的根本
2)确定数据路由的目的地后,数据就可以在网络中进行路由了,但数据在路由时无法自行进行路径选择,需要通过路由器来帮我们抉择去哪里
3)路由器是“认识路的”,它们将自己的“认路经验”都记录到路由表当中,因此路由器可以通过查路由表找到去特定点的最短路径.因此数据在路由时,会不断通过路由器来进行路径选择,以此来一步步靠近目标网络或目标主机
关于主机和路由器的区别
- 主机:配有IP地址,但是不进行路由控制的设备.但实际现在几乎不存在不进行路由控制的设备了,就连你的笔记本也会进行路由控制.
- 路由器:既配有IP地址,又能进行路由控制.实际现在主流的路由器已经不仅仅具有路由的功能了,它甚至具备某些应用层的功能.
什么是节点
节点是主机和路由器的统称. 网络当中的硬件设备全部都可以称之为节点
IP协议格式
IP协议的标准报头长度为20字节(无选项)
各个字段的含义
- 4位版本号: 指定IP协议的版本,IPv4就是4, 和IPv6是不兼容的
- 4位首部长度 : 表示IP报头的长度,单位是4字节,用来表示报头长度,和TCP一样,标准报头长度是20
- 如果IP报头当中不携带选项字段,那么IP报头的长度就是20字节,此时报头当中的4位首部长度字段所填的值就是20 ÷ 4 = 5,即0101
- 8位服务类型 :
- 3位优先权字段(已经弃用),4位TOS字段,和1位保留字段(必须置为0)
- 4位TOS分别表示:最小延时,最大吞吐量,最高可靠性,最小成本.这四者相互冲突,只能选择一个
- 比如对于ssh/telnet这样的应用程序,最小延时比较重要,而对于ftp这样的程序,最大吞吐量比较重要.
- 16位总长度 : IP报文(IP报头+有效载荷)的总长度,用于将各个IP报文进行分离 ,IP协议不是面向字节流的,它也是一次读取整个报文,所以需要表示整个报文长度
- 16位标识: 唯一的标识主机发送的报文,如果数据在IP层进行了分片,那么每一个分片对应的id都是相同的
- 3位标志字段:
- 第一位保留,表示暂时没有规定该字段的意义
- 第二位表示禁止分片,表示如果报文长度超过MTU,IP模块就会丢弃该报文
- 第三位表示“更多分片”,如果报文没有进行分片,则该字段设置为0,如果报文进行了分片,则除了最后一个分片报文设置为0以外,其余分片报文均设置为1.
- 13位片偏移: 表示分片相对于原始数据开始处的偏移,表示当前分片在原数据中的偏移位置
- 实际偏移的字节数是这个值 * 8 得到的. 因此, 除了最后一个报文之外, 其他报文的长度必须是8的整数倍(否则报文就不连续了
为什么是*8
1)IP报文中有一个16位总长度,也就是整个IP报文最大就是2^16,但是偏移量是13位的, 最大的偏移量就是2^13 + 一个报头的大小
- 2^13的偏移量是没有办法覆盖2^16次方的报文大小的, 所以2^16 / 2^13 = 2^3 = 8, 所以说报文的偏移量的基本大小必须是以8字节为单位的,这样才能够用13位的片偏移,把2^16次方也就是一个报文未来最大的报文数全部覆盖掉
- 8位生存时间: 数据报到达目的地的最大报文跳数,一般是64,每经过一个路由,TTL – ,一直减到0还没到达,那么就丢弃了,这个字段主要是用来防止出现路由循环
- 每经过一个节点生存时间就减1,如果生存时间减到0报文就被废弃,用来清理无效报文
为什么会有8位生存时间
1)报文在网络传输过程中,可能因为某些原因导致报文无法到达目标主机
- 如:报文在路由时出现了环路路由的情况,或者目标主机异常离线了,此时这个报文就成了一个废弃的游离报文
2)为了避免网络当中出现大量的游离报文,于是在IP的报头当中就出现了一个字段,叫做8位生存时间,8位生存时间代表的是报文到达目的地的最大报文跳数,每当报文经过一次路由,这里的生存时间就会减一,当生存时间减为0时该报文就会被自动丢弃,此时这个报文就会在网络中消散
- 8位协议: 表示有效载荷具体要提交给的上层协议,上层协议会将自己填写到8位协议中
10)16位首部检验和: 使用CRC进行校验,来鉴别数据报的首部是否损坏,但不检验数据部分.
11)选项字段: 不定长,最多40字节
- 4位首部长度:4位二进制的取值范围是0000 ~ 1111, 即范围是0~15, 又因为4位首部长度的基本单位是4字节,因此IP报头的最大长度为15 × 4 = 60,因为标准报头的长度是20字节,所以IP报头中选项字段的长度最多是40字节
IP报头的本质
IP报头在内核当中本质就是一个位段类型, 给数据封装IP报头时,实际上就是用该位段类型定义一个变量,然后填充IP报头当中的各个属性字段,最后将这个IP报头拷贝到数据的首部,至此便完成了IP报头的封装
IP如何将报头与有效载荷进行分离
IP分离报头与有效载荷的方法与TCP是一模一样的,当IP从底层获取到一个报文后,虽然IP不知道报头的具体长度,但IP报文的前20个字节是IP的基本报头,并且这20字节当中涵盖4位首部长度,所以先无脑获取IP报文的前20个字节
- 当IP从底层获取到一个报文后,首先读取报文的前20个字节,并从中提取出4位的首部长度,此时便获得了IP报头的大小记为size
- 如果size>20, 则需要继续从报文当中读取size-20个字节的内容,这部分数据就是IP报头当中的选项字段
- 读取完IP的基本报头和选项字段后,剩下的就是有效载荷了
IP就是通过这种“定长报头+自描述字段”的方式进行报头和有效载荷的分离的.但**需要注意的是,**IP报头当中的4位首部长度描述的基本单位与TCP报头当中的4位首部长度一样,都是以4字节为单位进行描述的,这也恰好是报文的宽度
IP如何决定将有效载荷交付给上层哪一个协议(如何分用)
基于IP协议的传输层协议不止一种,因此当IP从底层获取到一个报文并对其进行解包后,IP需要知道应该将分离后得到的有效载荷交付给上层的哪一个协议
1)在IP报头当中有一个字段叫做8位协议,该字段表示的就是上层协议的类型,IP就是根据该字段判定应该将分离出来的有效载荷交付给上层的哪一个协议的 即:8位协议中填充的就是上层协议是谁,就交付给谁
2)该字段是发送方的IP层从上层传输层获取到数据后填充的,是传输层填充的!!!
- 比如是上层TCP交给IP层的数据,那么该数据在封装IP报头时的8位协议填充的就是TCP对应的编号
问:UDP的客户端能否和TCP服务器通信?
不能!所有的报文都是从上一层传输层TCP/UDP交互给下一层网络层IP,上层在交互时要参与IP报头字段的填充,上层UDP协议填充IP报文的8位协议的时候填的是UDP对应的编号, 这个报文也一定是传给对方的UDP 所以UDP只能和UDP通信 TCP只能和TCP通信
注意:IP报文要交互给上层的UDP和TCP,也要解决粘包问题,也要区分开报文和报文的边界, 4位有效长度和16位总长度就可以区分IP报文的报头和有效载荷
32位源IP地址和32位目的IP地址
IP报头当中的32位源IP地址和32位目的IP地址,分别代表的就是该报文的发送端和接收端对应的IP地址
1)数据在网络传输过程中会遇到一个个的路由器,这些路由器会帮助网络当中的数据进行路由转发,使得网络中的数据慢慢趋近于目标主机
2)路由器在帮助数据进行路由转发时,会提取出该数据的IP报头当中的目的IP地址,并以此作为数据路由转发的重要依据.
3)当接收端收到了发送端发来的数据后,接收端可能也想要给发送端发送数据,因此发送端在发送数据时除了需要指明该数据的目的IP地址,还需要指明该数据的源IP地址,也就是发送端的IP地址
4)即便接收端收到数据后没有数据想要发送给发送端,但至少接收端需要向发送端发送一个响应报文,表明发送端发送的数据已经被接收端可靠的收到了,因此发送出去的数据除了需要指明该数据的目的IP地址,还需要指明该数据的源IP地址
重新理解socket编程
- 在进行socket编程的时候,当一端想要发送数据给另一端时,必须要指明对端的IP地址和端口号,也就是发送数据的目的IP地址和目的端口号.
- 这里的IP地址就是给网络层的IP用的,用于数据在网络传输过程中的路由转发,而这里的端口号就是给传输层的TCP或UDP用的,用于指明该数据应该交给上层的哪一个进程. (ps: IP+端口号确定互联网上唯一的一个进程)
- 发送数据时我们不需要指明发送数据的源IP地址和源端口号,因为传输层和网络层都是在操作系统内核当中实现的,数据在进行封装时操作系统会自行填充上对应的源IP地址和源端口号.
分片与组装
数据链路层解决的问题
首先我们要知道:IP能够将数据跨网络从一台主机送到另一台主机,而数据在进行跨网络传送时,需要经过一个个的路由器进行路由转发,最终才能到达目标主机
例子:
比如此时要将数据从主机B跨网络传送到主机C,
主机B需要先将数据交给路由器F,路由器F再将数据交给路由器G,…,最终由路由器D将数据交给主机C
结论就是: IP进行数据跨网络传送的前提是: 需要先将数据从一个节点传送到和自己相连的下一个节点,这个问题实际就是由IP之下的数据链路层解决的,其中数据链路层最典型的代表协议就是MAC帧
- 两个节点直接相连也就意味着这两个节点是在同一个局域网当中的
- 要讨论两个相邻节点的数据传送时,实际讨论的就是局域网通信的问题
最大传输单元 MTU
MAC帧作为数据链路层的协议,它会将IP传下来的数据封装成数据帧,然后发送到网络当中.但MAC帧携带的有效载荷的最大长度是有限制的,(底层数据链路层一次发送报文的有效载荷的大小是有限制的)也就是说IP交给MAC帧的报文不能超过某个值,这个值就叫做最大传输单元MTU,这个值的大小一般是1500字节
使用
ifconfig
命令可以查看对应的MTU
分片
由于MAC帧无法发送大于1500字节的数据,因此IP层向下交付的数据的长度不能超过1500字节,这里所说的数据包括IP的报头和IP的有效载荷. 如果IP报文过大,就必须分成多个数据包交给链路层,这就是分片.
分片和组装是谁干的
1.分片是网络层IP协议的事情,所以组装也是对端网络层的事情.即数据的分片和组装都是在IP层完成的,上层的传输层和下层的链路层并不关心.
- 如果IP层要传送的数据超过了1500字节,那么就需要先在IP层对该数据进行分片,然后再将分片后的数据交给下层MAC帧进行发送
- 如果发送数据时在IP层进行了分片,那么当这些分片数据到达对端主机的IP层后就需要先进行组装,然后再将组装好的数据交付给上层传输层
2.传输层只负责为数据传送提供可靠性保证,比如当数据传送失败后,传输层的TCP协议可以组织进行数据重传.
- 当TCP将待发送的数据交给IP后,TCP并不关心该数据是否会在IP层进行分片,即TCP并不关心数据具体的发送过程.
- 当TCP从IP获取到数据后,TCP也不关心该数据是否在IP层经过了组装.
3.而链路层的MAC帧只负责,将数据从一个节点传送到和自己相连的下一个节点.
- 当IP将待发送的数据交给MAC帧后,MAC帧并不知道该数据是IP经过分片后的某个分片数据,还是一个没有经过分片的数据,MAC帧只知道它一次最多只能发送MTU大小的数据,如果IP交给MAC帧大于MTU字节的数据,那MAC帧就无法进行发送.
- 当MAC帧从网络中获取到数据后,MAC帧也不关心这个数据是否需要进行组装,MAC帧只需要将该数据的MAC帧报头去掉后直接上交给上层IP就行了,而至于该数据的组装问题则是IP需要解决的.
- **链路层无法得知也不需要知道网络层的任何细节,**如果要在链路层分片,就一定要保证链路层知道网络层的IP地址,端口号,给新分出来的报文添加IP报头,这不是链路层应该做的!
因此,数据的分片和组装完全是由IP协议自己完成的(在网络层进行),传输层和链路层不必关心也不需要关心.
分片的过程
假设IP层要发送3420字节的数据,由于该数据超过了MAC帧规定的MTU,因此IP需要先将该数据进行分片,然后再将一个个的分片交给MAC帧进行发送
- IP报头如果不携带选项字段,那么其标准大小就是20字节,假设IP层添加的IP报头的长度就是20字节
- 链路层有效载荷最大是1500个字节,再去掉网络层IP协议的报头20字节,也就是说如果不想在网络层发生分片的话,IP协议的有效载荷最大为1480个字节
分片的伪代码:
// 伪代码
while (msg > 1480)
{
new_msg = 1480; // 拆分出新报文
msg -= 1480; //剩余的字节数
box.push_back(new_msg + 20字节的报头内容); // 压入新报文
}
box.push_back(msg);//最后拆剩下的数据
假设网络层存在一个3420大小的IP报文.那么就可以分片成3个报文,有效载荷分别是1480, 1480, 440,再给它们加上报头即可
- 3420 = 20 + 3400 = 20 + ( 1480 + 1480 + 440 ) 分成三个报文 = > ( 20 + 1480 ) + ( 20 + 1480 ) + ( 20 + 440 )
分片报文 | 总字节数 | IP报头字节数 | 数据字节数 |
---|---|---|---|
1 | 1500 | 20 | 1480 |
2 | 1500 | 20 | 1480 |
3 | 460 | 20 | 440 |
需要注意的是,分片后的每一个分片数据都需要封装上对应的IP报头 (这些分片的IP报头是一样的!!! )
**分片报文到达对方的IP层后需要被重新组装起来,**因此IP层在对数据进行分片时需要记录分片的信息,而IP报头当中的16位标识, 3位标志和13位片偏移实际就是与数据分片相关的字段
- 16位标识 : 唯一标识主机发送的报文, 独立报文的标识各不相同, 被分片的报文的标识是一致的.可以根据标识查找分片的报文,如果数据在IP层进行了分片,那么每一个分片报文的16位标识是相同的
- 13位片偏移: 表示分片相对于原始数据开始处的偏移
- 3位标志: 第一位保留,表示暂时没有规定该字段的意义.第二位表示禁止分片,表示如果报文长度超过MTU,IP模块就会丢弃该报文.第三位表示“更多分片”,如果报文没有进行分片,则该字段设置为0,如果报文进行了分片,则除了最后一个分片报文设置为0以外,其余分片报文均设置为1
因此上述四个分片报文对应的16位标识都是一样的,假设四个分片报文的16位标识都是321,则上述的3个报文对应的16位标识、3位标志中的“更多分片”和13位片偏移分别如下:
分片报文 | 总字节数 | IP报头字节数 | 数据字节数 | 16位标识 | “更多分片” | 13位片偏移(方便理解版本) | 13位片偏移(真实填充的) |
---|---|---|---|---|---|---|---|
1 | 1500 | 20 | 1480 | 123 | 1 | 0 | 0 |
2 | 1500 | 20 | 1480 | 123 | 1 | 1480 | 185 |
3 | 460 | 20 | 440 | 123 | 0 | 2960 | 270 |
如图所示,第一个报文的片偏移为0,长度为1480,第二个报文的片偏移就是1480,以此类推.组装的时候就可以按片偏移进行升序排序即可
实际上:13位片偏移当中记录的字节数是当前分片在原数据开始处的偏移字节数的值÷ 8得到的,比如分片报文2在原始数据开始处的偏移字节数是1480,其对应的13位片偏移的值就是1480 ÷ 8 = 185 上述只是为了方便理解
- 13位片偏移量的基本大小必须是以8字节为单位的
问:这个分片的过程需要传输层知道吗
不需要! 因为网络协议栈是解耦的,当前层根本不关心下一层的通信细节
那么在技术上如何实保证让传输层不知道呢?
- 发送端一旦分片经过网络也分片传送,到了对端链路层再向上交付, 如果对端网络层不做任何处理,网络层向上交付,那也是3个报文,此时对端的传输层就知道了曾经在发送方的网络层进行了分片
- 所以:发送端在网络层分片, 当对端的网络层收到这几个报文的时候,需要进行组装! 组装好然后向上交付 所以说:曾经在传输层给网络层的是什么样子,在对端的网络层交给传输层的就是什么样子 这样在网络层的分片和组装,传输层就不知道了!
组装的过程
如何区分出普通报文和分片报文
MAC帧交给IP层的数据可能来自世界各地,这些数据可能是经过分片后发送的,也可能是没有经过分片直接发送的,因此IP必须要通过某种方式来区分收到的各个数据.
规则如下:
对于分片报文来说:
- 第1个分片报文中的13位片偏移的值一定为0, 但是“更多分片”标志位一定为1
- 对于中间的报文:13位片偏移的值不为0, “更多分片”标志位为1
- 最后一个分片报文中的“更多分片”标志位一定为0,但是它的13位片偏移一定不是0 ,
所以:如果片偏移!=0 || 更多分片标志位为1 -> 一定是分片报文
对于普通报文:
13位片偏移的值一定为0&&“更多分片”标志位一定为0
//伪代码
if (片偏移 != 0 || 更多分片 == 1) {
//分片报文;
}
else {
// 普通报文;
}
分片报文如何聚合在一起
- IP报头当中有32位源IP地址,源IP地址记录了发送端所对应的IP地址,因此通过IP报头当中的32位源IP地址就可以区分来自不同主机的数据.
- IP报头当中有16位标识,未分片的数据各自的16位标识都是不同的,而由同一个数据分片得到的各个分片报文所对应的16位标识都是相同的,因此通过IP报头当中16位标识就可以判断哪些报文是没有经过分片的独立报文,哪些报文是经过分片后的分片报文.
因此IP可以通过IP报头当中的32位源IP地址和16位标识,将经过分片的数据各自聚合在一起,聚合在一起后就可以开始进行组装了
-
先找到分片报文中13位片偏移为0的分片报文, 然后根据当前报文的偏移量+当前报文长度=下一个报文的偏移量为依据,找到下一个分片报文拼接
- 实际上是:当前报文的13位片偏移加上当前报文的数据字节数 ÷ 8 所得到的值,就是下一个分片报文的所对应的13位片偏移
-
直到拼接到一个“更多分片”标志位为0的分片报文,此时表明分片报文组装完毕
分片报文丢包的问题
分片后的报文在网络传输过程中也可能会出现丢包问题,但接收端有能力判断是否收到了全部分片报文
问题是:能否通过16位总长度判断报文有没有丢失?
不能!因为16位总长度是就报文论报文, 分片之后的1480 +报头20个字节,我当前报文的的16位总长度就是1500 ,每一个报文当中的16位总长度只对自己当前的报文负责
所以必须根据13位片偏移 和 更多分片标志位进行判断是否有报文丢失
- 如果没有查找到13位片偏移为0的分片报文,就说明分片报文中的第一个分片报文丢包了
- 如果没有更多分片标志位为0的分片报文,那就是分片报文中的最后一个分片报文丢包了
- 如果当前报文的 偏移量+当前报文的长度/8 != 下一个报文的片偏移,则说明中间报文丢失
需要注意的是:未分片报文的“更多分片”标志位 和最后一个分片报文的“更多分片”标志位 都为 0
- 当接收端只收到分片报文中的最后一个分片报文时,接收端不会将其识别成一个未分片的报文,还会检测它的13位片偏移的值
- 因为未分片的报文所对应的13位片偏移的值也应该是0,而最后一个分片报文所对应的13位片偏移的值不为0
- 因此只有当一个报文的13位片偏移为0,并且该报文的“更多分片”标志位也为0时,该报文才会被识别成一个没有被分片的独立报文,否则该报文就会被识别成一个分片报文
不建议分片
虽然传输层并不关心IP层的分片问题,但分片对传输层也是有影响的
为什么不建议分片? 有下述理由:
1)如果一个数据在网络传输过程中没有经过分片,那么只要接收端收到了这一个报文,我们就可以认为该数据被对方可靠的收到了, 如果一个数据在网络传输过程中进行了分片,那么只有当接收端收到了全部的分片报文并将其成功组装起来,这时我们才认为该数据被对方可靠的收到了
- 如果众多的分片报文当中有一个报文出现了丢包,就会导致接收端就无法将报文成功组装起来,这时接收端会将收到的分片报文全部丢弃,此时传输层TCP会因为收不到对方应答而进行超时重传
2)假设在网络传输时丢包的概率是万分之一,如果将数据拆分为一百份进行发送,那么此时丢包的概率就上升到了百分之一.因为只要有一个分片报文丢包了也就等同于这个报文整体丢失了,因此分片会增加传输层重传数据的概率
- 只要分片报文当中的某一个出现了丢包,此时传输层都需要将数据整体进行重传,因为传输层并不知道底层IP对数据进行了分片,当传输层发送出去的数据得不到应答时传输层就只能将数据整体进行重传,因此数据在发送时不建议进行分片
如何避免分片
是在传输层?网络层?数据链路层?哪一层进行控制?
1)传输层应该控制这个问题!因为传输层负责解决数据应该如何发送,何时发送,一次发送多少,出错了怎么办的问题
2)实际数据分片的根本原因在于传输层一次向下交付的数据太多了,导致IP无法直接将数据向下交给MAC帧,如果传输层控制好一次交给IP的数据量不要太大,那么数据在IP层自然也就不需要进行分片
需要注意的是:UDP没有发送缓冲区,(只有接收缓冲区) , UDP直接把报文向下交互给网络层
3)因此TCP作为传输控制协议,它需要控制一次向下交付数据不能超过某一阈值,这个阈值就叫做MSS-最大报文段长度
何时协商MSS的大小
通信双方在建立TCP连接时(三次握手的时候),除了需要协商自身窗口大小等概念之外,还会协商后续通信时每一个报文段所能承载的最大报文段长度MSS值
-
MAC帧的有效载荷最大为MTU,TCP的有效载荷最大为MSS,由于TCP和IP常规情况下报头的长度都是20字节,因此一般情况下 MSS = MTU - 20 - 20,而MTU的值一般是1500字节,因此MSS的值最大就是1460字节
-
TCP双方在握手的时候会在报头写入自己能支持的MSS值,MSS的值是在IP不会被分片处理的最大长度.然后双方得到对方的MSS值之后,选择较小的作为最终MSS.
- 假设主机A的MSS为1460 主机B的MSS为1000 那么主机A和主机B的MSS都为1000(取较小值)
4)一般建议TCP将发送的数据控制在1460字节以内,此时就能够降低数据分片的可能性
- 之所以说是降低数据分片的可能性,是因为每个网络的链路层对应的MTU可能是不同的,如果数据在传输过程中进入到了一个MTU较小的网络,那么该数据仍然可能需要在路由器中进行分片
网段划分
IP地址的构成
IP地址分为两个部分,网络号和主机号:
- 网络号:保证相互连接的两个网段具有不同的标识
- 主机号:同一网段(局域网)内,主机之间具有相同的网络号,但是必须有不同的主机号
通常可以在IP地址的后面加一个 /
并在 / 后面加上一个数字,这个数字就表示从头数到第几位为止属于网络标识.
例子:
下图中路由器连接了两个网段.对于网络标识来讲,同一网段内主机的网络标识是相同的,不同网段内主机的网络标识是不同的
而对于主机标识来讲,同一网段内主机的主机标识是不同的,不同网段内主机的主机标识是可以相同的.
如果在子网中新增一台主机,则这台主机的网络号和这个子网的网络号一致,但是主机号必须不能和子网中的其他主机重复
- 同一个局域网内的主机的网络号一定相同,而主机号一定不同
- 注意:路由器负责跨网络传送,(数据包转发),路由器至少要有两套网络接口,即:至少有两张网卡,配套两个IP,至少要横跨两个网络,要不然就不能跨网络转发了
如何进行查找目标主机
先找目标网络,再找目标主机
1)当IP要将数据跨网络从一台主机发送到另一台主机时,其实不是直接将数据发送到了目标主机,而是先将数据发送到目标主机所在的网络,然后再将数据发送到目标主机 ,
2)因此数据在路由时的第一目的并不是找到目标主机,而是找到目标网络所在的网络,然后再在目标网络当中找到目标主机
3)数据路由时之所以不一开始就以找目标主机为目的,因为这样效率太低了
-
查找的过程本质是排除的过程, 如果一开始就以找目标主机为目的,那么在查找的过程中一次只能排除一个主机,如果一开始先以找目标网络为目的,那么在查找过程中就能一次排除大量和目标主机不在同一网段的主机,这样就可以大大提高检索的效率
-
例如:先根据目的IP中的网络号来查找目标网络,比如目的IP中网络号为192.168.11,这样就找到了对应的网络,进入目标网络后,再查找并对比IP中的主机号,就确定了目标主机
为了提高数据路由的效率,我们对网络进行了网段划分.IP地址中的网络号和主机号,本质就是对IP地址32个比特位进行划分, 通过合理的设置主机号和网络号,就可以保证在相互连接的网络中,每台主机的IP地址都不相同
分段划分法
过去曾经提出一种划分网络号和主机号的方案,就是把所有IP地址分为五类,如下图所示:
因此,各类IP地址的取值范围如下:
- A类:0.0.0.0 (全0序列)到127.255.255.255 (01111111 11111111 11111111 11111111 )
- B类:128.0.0.0 (10000000 …全0 ) 到191.255.255.255. (10111111 11111111 11111111 11111111 )
- C类:192.0.0.0到223.255.255.255.
- D类:224.0.0.0到239.255.255.255.
- E类:240.0.0.0到247.255.255.255.
这种划分的方法叫分类划分法,粗粒度的将第一位为0的IP划分成A类,在剩下的部分中将第二位为0的划分成B类,再在剩下的部分中将第三位为0的划分成C类,以此类推,分成五类
如何判断IP地址属于哪一类?
只需要遍历IP地址的前五个比特位,第几个比特位最先出现0值,那么这个IP地址对应就属于A,B,C,D,E类地址
随着网络的飞速发展,这种划分方案的局限性很快显现出来
- 比如一些学校、公司、实验室等组织想要申请自己的局域网,由于A类地址的网络号只占7个比特位,因此A类地址可申请的网络只有2^7个 ,太少了,于是大多数组织都选择申请B类地址.
- 由于B类地址的主机号占16个比特位,因此理论上一个B类网络当中允许有65536台主机但实际网络架设中,一般不会存在一个局域网当中有这么多主机的情况, 一个子网内没有那么多主机导致浪费了大量地址.
子网掩码划分
针对上述的这种情况提出了新的划分方案 CIDR,是分类划分法的基础上引入一个额外的子网掩码 (subnet mask) 来区分网络号和主机号.
- 在原有的五类网络的基础上继续进行子网划分,这也就意味着需要借用主机号当中的若干位来充当网络号
- 每一个子网都有自己的子网掩码,子网掩码实际就是一个32位的正整数,通常用一串“0”来结尾
- 将IP地址与当前网络的子网掩码进行“按位与”操作,就能够得到当前所在网络的网络号
1)比如在某一子网中将IP地址的前24位作为网络号,那么该网络对应的子网掩码的32个比特位中的前24位就为1,剩下的8个比特位为0,将其用点分十机制表示就是255.255.255.0,
2)假设该子网当中有一台主机对应的IP地址是192.168.128.10,那么将这个IP地址与该网络对应的子网掩码进行“按位与”操作后得到的就是192.168.128.0,这就是这个子网对应的网络号
3)实际在用子网掩码与子网当中主机的IP地址进行“按位与”操作时,本质就是保留了主机IP地址中前24个比特位的原貌,将剩下的8个比特位的值清0了而已,也就是将主机号清0了,所以“按位与”后的结果就是该网络对应的网络号.
- 子网掩码一般在路由器内部给我们写好的,因为组建局域网的时候需要路由器参与,所以路由器在组网的时候就把子网掩码记下来了, 当一个报文到路由器里面,路由器就拿着要去的目的ip和路由器内部的子网掩码做按位与操作 确定网络号
例子:
10 11 1001 1010 1111 0001 1011 X XXX XXXX // IP地址
11 11 1111 1111 1111 1111 1111 `0`000 0000 & // 子网掩码
---------------------------------------------------------
10 11 1001 1010 1111 0001 1011 `0`000 0000 // 网络号
此时一个网络就被更细粒度的划分成了一个个更小的子网,通过不断的子网划分,子网中IP地址对应的主机号就越来越短,因此子网当中可用IP地址的个数也就越来越少,这也就避免了IP地址被大量浪费的情况
- 将子网掩码中主机号对应位置的首位设为1,将此位看作网络号的内容,就能弥补网络位太少的不足.
10 11 1001 1010 1111 0001 1011 X XXX XXXX // IP地址
11 11 1111 1111 1111 1111 1111 `1`000 0000 & // 子网掩码
---------------------------------------------
10 11 1001 1010 1111 0001 1011 `1`000 0000 // 网络号1
10 11 1001 1010 1111 0001 1011 `0`000 0000 // 网络号2
例子3:
网络号 = IP地址 & 子网掩码
140 252 20 68
140 252 20 01000100 //IP地址的二进制 前面的和全1& 都不变,所以写成整数形式
11111111 11111111 11111111 11110000 &
-------------------------------------------
140 252 20 01000000 // 网络号 即:140.252.20.64
子网划分不是只能进行一次,我们可以在划分出来的子网的基础上继续进行子网划分.因此一个数据在路由的时候,随着数据不断路由进入更小的子网
- 其网络号的位数是在不断增加的
- 也就意味着IP地址当中的主机号的位数在不断减少
- 最终当数据路由到达目标主机所在的网络时,就可以在该网络当中找到对应的目标主机并将数据交给该主机,此时该数据的路由也就结束了
DHCP协议
手动管理IP地址是一个非常麻烦的事情,当子网中新增主机时需要给其分配一个IP地址,当子网当中有主机断开网络时又需要将其IP地址进行回收,便于分配给后续新增的主机使用. 一般路由器中都有DHCP功能,可以自动为子网内新增的节点分配IP地址
- 因此对于IP地址的分配和回收一般不会手动进行,而是采用DHCP-动态主机配置协议技术.
- DHCP通常被应用在大型的局域网环境中,其主要作用就是集中地址管理、分配IP地址,使网络环境中的主机动态获得IP地址、Gateway地址、DNS服务器地址等信息,并能够提升地址的使用率.
- DHCP是一个基于UDP的应用层协议,一般的路由器都带有DHCP功能,因此路由器也可以看作一个DHCP服务器.
例如:
当我们连接WiFi时需要输入密码,本质就是因为路由器需要验证你的账号和密码,如果验证通过,那么路由器就会给你动态分配了一个IP地址,然后你就可以基于这个IP地址进行各种上网动作了.
特殊的IP地址
并不是所有的IP地址都能够作为主机的IP地址,有些IP地址本身就是具有特殊用途的.
- 将IP地址中的主机地址全部设为0,就成为了网络号,代表这个局域网.
- 将IP地址中的主机地址全部设为1,就成为了广播地址,用于给同一个链路中相互连接的所有主机发送数据包.
- 127.*的IP地址用于本机环回(loop back)测试,通常是127.0.0.1.
我们之前写的127.0.0.1 本地环回这样的报文不会经过网络,实际上是把协议栈走了一圈,从上到下交付一下,然后到了网络层,查自己的路由表,发现这个报文是给当前主机的, 然后又向上交付了,其实是没有转发的->体现在数据没有交给数据链路层,
IP地址中主机号为全0的代表的是当前局域网的网络号,IP地址中主机号为全1的代表的是广播地址,这两个IP地址都是不能作为主机的IP地址的.因此在某个局域网中最多能存在的主机个数是 2^主机号的位数 − 2
IP地址的数量限制的问题
IP地址数量不足问题
我们知道,IP地址(IPv4)是一个4字节32位的正整数,因此一共有2 32 2^{32}232个IP地址,也就是将近43亿个IP地址.但TCP/IP协议规定,每个主机都需要有一个IP地址.
- 现在全世界人口已经有70多亿了,就算有一半的人没有智能手机,算下来也有30多亿台智能手机需要IP地址.
- 随着科技的发展,我们使用的电脑、智能手表、智能冰箱、智能洗衣机等设备如果要入网也是需要IP地址的.
- 另外,IP地址并不是按照主机台数来配置的,因此一个主机可能需要多个IP地址,更别谈还有很多组网的路由设备也需要IP地址,以及一些特殊的IP地址不能使用的问题.
所以43亿个IP地址其实早就不够用了,因此才提出了CIDR的方案对已经划分好的五类网络继续进行子网划分,其目的就是为了减少IP地址的浪费,根本原因就是IP地址本来就不够了,所以不能够再浪费了.
CIDR虽然在一定程度上缓解了IP地址不够用的问题,因为CIDR提高了IP地址的利用率,减少了浪费,但IP地址的绝对上限并没有增加.
如何解决IP地址不足问题
解决IP地址不足有以下几种方式:
- 动态分配IP地址:只给接入网络的设备分配IP地址,因此同一个MAC地址的设备,每次接入互联网中,得到的IP地址不一定是相同的,避免了IP地址强绑定于某一台设备.
- NAT技术:能够让不同局域网当中同时存在两个相同的IP地址,NAT技术不仅能解决IP地址不足的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机.
- IPv6:IPv6用16字节128位来表示一个IP地址,能够大大缓解IP地址不足的问题.但IPv6并不是IPv4的简单升级版,它们是互不相干的两个协议,彼此并不兼容,因此目前IPv6还没有普及. IPV4和IPV6是不兼容的
私网IP和公网IP
局域网分配的IP地址只用于局域网内的通信,而不能直接连到公网上.理论上使用任意的IP地址都可以,但是RFC 1918规定了用于组建局域网的私有IP地址
IP | 网络号 | 地址数 |
---|---|---|
10.* | 前8位是网络号 | 共16,777,216个地址 |
172.16.* ~ 172.31.* | 前12位是网络号 | 共1,048,576个地址 |
192.168.* | 前16位是网络号 | 共65,536个地址 |
包含在这个范围中的,都称为私网IP,其余的则称为公网IP(或全局IP).
我们的服务器用的是什么IP
我们连接云服务器时,连接的这个IP地址就是云服务器的公网IP地址
我们可以通过ifconfig
命令来查看我们这台机器的私网IP,其中网络接口lo(loop)代表的是本地环回,而eth0代表的就是我这台机器的网络接口
我们为什么要给运营商交钱?
我们享受的是互联网公司提供服务,但为什么需要向运营商交钱呢?
- 实际网络通信的基础设施都是运营商搭建的,我们访问服务器的数据并不是直接发送到了对应的服务器,而是需要经过运营商建设的各种基站以及各种路由器,最终数据才能到达对应的服务器.
- 因为运营商为我们提供了通信的基础设施,所以我们交网费实际就相当于购买入网许可一样.
- 没有运营商提供的这些基础设施,就不会诞生所谓的互联网公司,因为互联网公司是诞生在网络通信基础之上的.
也就是说,用户上网的数据首先必须经过运营商的相关网络设备,然后才能发送到互联网公司对应的服务器.因此所谓的网段划分、子网划分等工作实际都是运营商做的.
所以说:有些互联网公司说:是我们创造了这个时代,这其实就是个笑话, 因为这些互联网公司本质就是互联网下的产物罢了
数据是如何发送到服务器的
路由器是连接两个或多个网络的硬件设备,在路由器上有两种网络接口,分别是LAN口和WAN口
- LAN口:表示连接本地网络的端口,主要与家庭网络中的交换机、集线器或PC相连.
- WAN口:表示连接广域网的端口,一般指互联网.
我们将LAN口的IP地址叫做LAN口IP,也叫做子网IP,将WAN口的IP地址叫做WAN口IPO,也叫做外网IP.
我们使用的电脑、家用路由器、运营商路由器、广域网以及我们要访问的服务器之间的关系大致如下:
- 所有的客户是在家用路由器当中以局域网的形式接入网络
- 每个路由器都有自己的LAN口(路由器对内的子网IP) 和WAN口(公网IP)
- 路由器可以构建子网(局域网)
公网IP在整个网络世界是不可以出现重复的,但私网IP在不同的网段内是可以重复的.所有接入公网的人都需要用路由器建立私网.每个家用路由器,都作为运营商路由器子网中的一个节点.
- 不同的路由器,子网IP其实都是一样的(通常都是192.168.1.1),子网内的主机IP地址不能重复,但是子网之间的IP地址就可以重复了.
- 每一个家用路由器,其实又作为运营商路由器的子网中的一个节点,这样的运营商路由器可能会有很多级,最外层的运营商路由器的WAN口IP就是一个公网IP了
由于私网IP不能出现在公网当中,因此子网内的主机在和外网进行通信时,路由器会不断将数据包IP首部中的源IP地址替换成路由器的WAN口IP,这样逐级替换,最终数据包中的源IP地址成为一个公网IP,这种技术成为NAT-网络地址转换技术
为什么私网IP不能出现在公网当中?
私有IP不能出现在公网当中,也不能出现在公网当中,所以公网里面跑的所有的报文的目的源IP绝对不是私有IP
- 不同的局域网中主机的IP地址可能是相同的,所以私网IP无法唯一标识一台主机,因此不能让私网IP出现在公网上,因为IP地址要能唯一标识公网上的一台主机.
- 但由于IP地址不足的原因,我们不能让主机直接使用公网IP,而是让主机使用私网IP,因为私网IP可以重复也就意味着我们可以在不同的局域网使用相同的IP地址,缓解了IP的不足.
- 此外,我们不能直接使用公网IP还有一个原因就是,因为我们的数据包必须要经过运营商的路由器,如果我们发送的数据直接到了公网,那也就意味着我们再也不用交网费了,这是不现实的.
两个局域网当中的主机不能不跨公网进行通信
- 两个局域网当中的主机,理论上是不能不跨公网进行通信的,因为一个主机要将数据发送给另一台主机的前提是得先知道另一台主机的IP地址.
- 即便现在这个主机知道了另一台主机的IP地址,但有可能这两台主机的IP地址是一样的,因为它们的IP地址都是私网IP地址.
- 当这一台主机发送数据时将目的IP地址填成和自己相同的IP地址,操作系统就会认为这个数据就是要发给自己的,而不会向外进行发送了.
所以数据要从一个局域网发送到另一个局域网,如果不经过公网是基本上不可能的.我们在和别人聊天的时候,也不是直接将数据从一个局域网直接发送到了另一个局域网,而是先将数据经过公网发送到了服务器,然后再由服务器将数据经过公网转发到了另一个局域网.
但实际确实存在一些技术能够使数据包在发送过程中不进行公网IP的替换,而将数据正确送到目标主机,这种技术叫做内网穿透,也叫做NAT穿透.
路由
路由的过程
数据在路由的过程中,实际就是一跳一跳“问路”的过程.
-
所谓“一跳”就是数据从一个节点到另一个节点到过程.
-
所谓“一跳”就是数据链路层中的一个区间,具体在以太网中指从源MAC地址到目的MAC地址之间的帧传输区间
IP数据包的传输过程中会遇到很多路由器,这些路由器会帮助数据包进行路由转发,每当数据包遇到一个路由器后,对应路由器都会查看该数据的目的IP地址,并告知该数据下一跳应该往哪跳.
路由器的查找结果可能有以下三种:
- 路由器经过路由表查询后,得知该数据下一跳应该跳到哪一个子网.
- 路由器经过路由表查询后,没有发现匹配的子网,此时路由器会将该数据转发给默认路由.
- 路由器经过路由表查询后,得知该数据的目标网络就是当前所在的网络,此时路由器就会将该数据转给当前网络中对应的主机
IP数据包的传输过程和问路类似:
- 当IP数据包到达路由器时,路由会先查看报文中的目的IP;
- 对比目的IP和路由表,如果对应得上就说明目标主机就在自身子网内,就直接发送到目标主机;
- 如果对应不上就将报文转发到下一个路由器,就是所在子网的路由器.
依此反复,直到到达目标主机.
查看路由表的命令:
route
所以数据包传输的过程:
1)当IP数据包到达路由器时,路由器就会用该数据的目的IP地址,依次与路由表中的子网掩码 Genmask
进行&操作,然后将结果与对应的目的网络地址Destination
进行比对
- 如果匹配则说明该数据包下一跳就应该跳去这个子网,此时就会将该数据包通过对应的发送接口
Iface
发出
2)没有找到匹配的目的网络地址,此时路由器就会将这个数据包发送到默认路由,也就是路由表中目标网络地址中的default
.可以看到默认路由对应的Flags
是UG
,实际就是将该数据转给了另一台路由器,让该数据在另一台路由器继续进行路由
3)数据包不断经过路由器路由后,最终就能到达目标主机所在的目标网络,此时就不再根据该数据包目的IP地址当中的网络号进行路由了,而是根据目的IP地址当中的主机号进行路由,最终根据该数据包对应的主机号就能将数据发送给目标主机了
路由表生成算法
路由可分为静态路由和动态路由:
- 静态路由:是指由网络管理员手工配置路由信息.
- 动态路由:是指路由器能够通过算法自动建立自己的路由表,并且能够根据实际情况进行调整.
路由表相关生成算法:距离向量算法、LS算法、Dijkstra算法等.