Linux网络编程:网络层协议|IP

前言:

网络层协议解决什么问题

当我们在上层实现了应用层协议将字符流数据转化为结构体数据,在传输层完成了通信的实现,数据的传输,但是数据最终还是得从本主机输出,从网络中进入到远端的另外一台主机。

  • 网络层协议IP解决的就是两台主机间数据的转移,也就是提供了一种将数据包从一台主机送到一台主机的能力。
  • 传输层协议TCP/UDP解决的是网络传输的可靠性,将数据传输到相应的应用程序
  • 应用层协议HTTP/HTTPS解决的是将字节流数据转化为结构体数据

如图:主机B发送数据包经由若干个路由器最终到达主机C


基本概念

  • 主机: 配有IP地址, 但是不进行路由控制的设备;
  • 路由器: 即配有IP地址, 又能进行路由控制;
  • 节点: 主机和路由器的统称
  • 路由控制是路由器根据路由控制表转发数据包的过程。

1.IP协议

1.1.IP协议格式


32位源IP和目的IP

表示发送端的ip地址,和数据报文的目的主机地址。


4位版本

对应着IP协议版本

  • IPV4(表示4个字节表示IP地址)
  • IPV6(16个字节表示地址)

4位首部长度

和TCP协议的4位首部长度一致,单位也是4字节,大小最大为40字节


8位协议

用来识别数据报文是TCP协议还是UDP协议,防止不同类型的协议之间进行通信


16位总长度

表示整个报文的长度,并且已知报头长度固定为20字节,那么通过报文长度 = 报头长度 + 有效载荷长度,即可分离报文和报头


8位服务类型

用于数据包发送质量、效果优先级的选择。

  • 3位优先权字段(已经弃用),4位TOS字段,和1位保留字段(必须置为0)。
  • 4位TOS分别表示:最小延时,最大吞吐量,最高可靠性,最小成本。
  • 这四者相互冲突,只能选择一个。对于ssh/telnet这样的应用程序,最小延时比较重要; 对于ftp这样的程序, 最大吞吐量比较重要。

8位生存时间(TTL)

报文在路径转发过程中,经由路由器的跳数上限(不允许报文在路径节点进行无意义的节点转换,造成资源的浪费)


16位标识、3位标志、13位片偏移

因为在网络层的下一层数据链路层,最大允许发出报文长度为MTU=1500,也就是网络层形成报文之后可能大小会大于MTU,这时候就需要进行分片、多次发送。

  • 16位标识(id):唯一的标识主机发送的报文。如果IP报文在数据链路层被分片了,那么每一个片里面的这个id都是相同的(用来区分不同的报文)
  • 3位标志字段:第一位保留(默认填0,暂时没有意义)。第二位置为1表示禁止分片,这时候如果报文长度超过MTU,IP模块就会丢弃报文。 第三位表示"更多分片", 如果分片了的话,最后一个分片置为1,其他是0。类似于一个结束标记.
  • 13位分片偏移:是分片相对于原始IP报文开始处的偏移。其实就是在表示当前分片在原报文中处在哪个位置。实际偏移的字节数是这个值 * 8 得到的。因此,除了最后一个报文之外,其他报文的长度必须是8的整数倍(否则报文就不连续了)

1.2.网段划分

1.2.1.知识引入

IP地址分为两个部分, 网络号和主机号

  • 网络号: 保证相互连接的两个网段具有不同的标识;
  • 主机号: 同一网段内,主机之间具有相同的网络号,但是必须有不同的主机号;

例如本机ip地址为127.0.0.1,其中127.0.0.为网络号,1为主机号

如图: 路由器连接了两个局域网,而在同一个局域网,我们发现网络号是相同的,不同的是主机号,所以我们就能够通过IP地址定位到网络号和主机号,进而找到唯一的主机!!!

路由器的作用:

  • 连接不同的网络
  • 构建子网
  • 进行数据包的路由和转发

1.2.2.IP地址划分和子网掩码

IP地址划分

在1.2.1.中我们知道IP地址划分成了网络号和主机号,保证在互相连接的网络中每一台主机的ip地址都不同,那么这时就会出现一个问题:网络号和主机号怎么设计,IP地址如何进行划分?

如图所示,为过去的一种IP地址划分的方案,为了应对不同的场景下(对应局域网的主机数量)的网络方案。

随着Internet的飞速发展,这种划分方案的局限性很快显现出来,大多数组织都申请B类网络地址,导致B类地址很快就分配完了,而A类却浪费了大量地址;

  • 例如,申请了一个B类地址,理论上一个子网内能允许6万5千多个主机。A类地址的子网内的主机数更多。
  • 然而实际网络架设中,不会存在一个子网内有这么多主句的情况。因此大量的IP地址都被浪费掉了。

我们也可以从网络的拓扑结构(两层)看出来,上述方案的IP地址利用率较低。

因此这样的固定长度划分方案,漏洞过大,并且当前国际上主流的IP地址版本为IPV4,即用4个字节(32位)来表示全球上的所有主机,理论上最多表示42亿台主机,所以IP地址也是一种资源,那么我们就需要采取其他方案在实现IP划分的同时,又保证IP地址利用率。


子网掩码

针对这种情况提出了新的划分方案,称为CIDR(Classless Interdomain Routing):

  • 引入一个额外的子网掩码(subnet mask)来区分网络号和主机号;
  • 子网掩码也是一个32位的正整数。通常用一串 "0" 来结尾;
  • 将IP地址和子网掩码进行 "按位与" 操作,得到的结果就是网络号;
  • 网络号和主机号的划分与这个IP地址是A类、B类还是C类无关;

值得一提的是:子网掩码以一串"1"开头,以一串"0"结尾。

如图:添加了子网掩码这个模块后,我们发现同一个IP地址转化后表示的子网内的地址范围存在不同,那为什么会导致这种不同呢?接下来我们通过下图来理解一下:

看到这里大家可能理解了子网掩码的作用,但是应该没有理解到它的魅力,那我们通过A类方案的子网掩码化来体会一下(实际上A类方案不进行子网构建,一般为B为方案):

对比原先的IP地址划分方案,我们发现网络拓扑:

  • 原先的IP地址划分结构为:大网络->主机
  • 添加子网掩码后:大网络->小网络->主机

所以我们实现了将没有利用到的主机号,转化成了"子网的网络号",进而提升了IP地址的利用率。

1.3.IP地址分类

1.3.1.特殊IP地址

  • 将IP地址中的主机地址全部设为0,就成为了网络号,代表这个局域网
  • 将IP地址中的主机地址全部设为1,就成为了广播地址,用于给同一个链路中相互连接的所有主机发送数据包
  • 127.*的IP地址用于本机环回(loop back)测试,通常是127.0.0.1

1.3.2.私有IP和公网IP

如果一个组织内部组建局域网,IP地址只用于局域网内的通信,而不直接连到Internet上,理论上使用任意的IP地址都可以,但是RFC 1918规定了用于组建局域网的私有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的主机,最终实现从广域网到局域网,从公有ip到私有ip。将子网划分提高了定位和查找主机的效率。

  • 一个路由器可以配置两个IP地址,一个是WAN口IP,一个是LAN口IP(子网IP)
  • 路由器LAN口连接的主机,都从属于当前这个路由器的子网中
  • 不同的路由器,子网IP其实都是一样的(通常都是192.168.1.1),子网内的主机IP地址不能重复。但是子网之间的IP地址就可以重复了**(只要WAN口IP不同即可区分)**
  • 每一个家用路由器,其实又作为运营商路由器的子网中的一个节点。这样的运营商路由器可能会有很多级,最外层的运营商路由器,WAN口IP就是一个公网IP了。

1.3.3.NAT技术

子网内的主机需要和外网进行通信时,路由器将IP首部中的IP地址进行替换(替换成WAN口IP),这样逐级替换,最终数据包中的IP地址成为一个公网IP。这种技术称为NAT (Network Address Translation,网络地址转换).

那么我们实际上进行网络通信时,数据报文在网络中是如何传送到服务器的呢?

首先内网IP不会出现在公网通信中,所以内网如果需要实现通信,就得一层一层通过路由器向上转发到运营商路由器中公网ip发送。并且向上转发时,需要将源IP替换成路由器的WAN口IP!!!这又是为什么呢?

因为路由器对应的子网IP不是表示当前路由器的标识,而是当前路由器的下一层子网的标识,而WAN口IP才是路由器网络中区分唯一路由器的标识!!!

这时我们就可以理解了NAT技术:逐级将当前的IP地址替换为当前对应的WAN口IP,直到转换为公网IP,也就是最上层的运营商路由器的WAN口IP。

1.4.路由

1.4.1.什么是路由

我们知道数据报文离开发送端后,并不是直接发送到接收端主机的,需要不断的通过中间节点(路由器)转发,不断路由直到到达接收端

  • 路由的过程,就是这样一跳一跳 "问路" 的过程。
  • "一跳"就是数据链路层中的一个区间。具体在以太网中指从源MAC地址到目的MAC地址之间的帧传输区间

在智能手机未普及的时代,人们去一个很远的地方,是需要进行不断的问路,一步一步的从一个终点走到一个新的起点,再继续问路,直到最后走到目的地。

IP数据包的传输过程也和问路一样:

  1. 当IP数据包,到达路由器时,路由器会先查看目的IP
  2. 路由器决定这个数据包是能直接发送给目标主机,还是需要发送给下一个路由器
  3. 依次反复,一直到达目标IP地址

1.4.2.路由表

既然在网络中,数据报文是通过路由器在主机之间进行转发的,那么就会出现一个问题:数据报文怎么知道自己要去往哪一个路由器进行路由转发呢?这时IP协议就规定了每一个路由器节点中必须维护一张路由表,当数据报文到达一个节点时,通过查询路由表来获取下一个路由器的IP地址。

如图:为某一个路由器主机的路由表。

  • Destination:目的地址
  • Gateway:下一条地址
  • Genmask:子网掩码
  • Use Iface:发送接口

数据报文在路由器转发的过程:

数据报文在网络中转发的过程本质是:通过不同主机的路由表,和子网掩码按位与接着从相应地址的接口传到下一个主机,再继续这个循环

我们可以借助:route命令来查看当前主机的路由表

1.5.网络层数据切片和组装

我们在1.1.中提及:16位标识、3位标志、13位片偏移这三个字段,并且我们提及了MTU,即Maximum Transmission Unit,最大传输单元,它是计算机网络中用于描述数据帧的最大长度的一个参数。所以数据切片是为了解决数据帧过大的问题!!!

那么网络通信的过程中是如何实现数据的切片和组装的呢?

切片规则:

  • IP数据报文总大小为1500字节(包括20字节的固定报头)
  • 当数据报文超过1500字节时,切片的第一片3位标志为001,并且13位片偏移量为0
  • 切片的最后一片的3位标志为0,并且13位偏移量等于其他

如图:我们首先进行切片,因为MTU=1500,所以我们需要切成三片,接着将这三片数据传入数据链路层,最终发送到对端主机

而当我们在对端主机接收到许多数据帧,然后将帧头去掉后传到网络层后,我们如何找到这些切片后的数据报文,并把他们组装到一起?

组装规则:

  • 必须按照顺序、并且组装完整成分片前的完整的IP数据报文
  • 第一个切片的3位标志位001,并且13位偏移量为0
  • 根据相同的16位标识来对分片的报文进行组装

通过图上这种方式,我们就可以分别找到若干个段切片后的IP报文,接着我们保留第一个切片的报头,把其他切片报头除去,按照顺序将切片的所有IP报文恢复成切片前的完整的IP报文,传到传输层中。这就是数据的组装。


从传输层数据大小设置来替代数据切片

然而我们不得不承认:切片并不是一个好的传输策略,首先切成若干份,可能会增加丢包的风险,这时就会导致数据的传输需要消耗更加多的资源,那么我们如何解决这个问题呢?

我们知道IP报文需要进行切片的根本原因就是:

  • 数据链路层存在最大传输单元MTU

即IP层向下转发的数据报文过大,也可以说是传输层的TCP/UDP报文过大,那么我们就认为规定单次向网络层传输的字节数不超过 MSS = MTU - IP协议报头大小 - 以太网数据报头大小

这样我们就尽量避免了数据的切片和组装,但是实际应用上我们还是运行这种情况的出现......

相关推荐
Anna_Tong3 分钟前
全局流量管理:提升用户体验与保障服务稳定性
运维·服务器·网络·数据库·安全·负载均衡
番茄电脑全能王23 分钟前
《鸣潮》运行时提示找不到emp.dll是什么原因?缺失emp.dll文件要怎么解决?
网络·数据库·经验分享·游戏·电脑
浙江洲际检测认证1 小时前
一分钟快速解读LEED绿色建筑认证
网络
千天夜2 小时前
深度学习中的残差网络、加权残差连接(WRC)与跨阶段部分连接(CSP)详解
网络·人工智能·深度学习·神经网络·yolo·机器学习
茶颜悦色vv2 小时前
Wireshark(1)
网络·web安全·网络安全·wireshark
ascarl20102 小时前
【Nginx系列】---Nginx配置tcp转发
运维·tcp/ip·nginx
我叫czc3 小时前
【Python高级353】python实现多线程版本的TCP服务器
服务器·python·tcp/ip
xianwu5433 小时前
反向代理模块。开发
linux·开发语言·网络·c++·git
brhhh_sehe3 小时前
重生之我在异世界学编程之C语言:深入文件操作篇(下)
android·c语言·网络
忆源3 小时前
Linux高级--2.4.2 linux TCP 系列操作函数 -- 深层理解
linux·网络·tcp/ip