橘生淮南

记录学习中的点点滴滴

0%

序言

别怕路长梦远,总有星河照耀(●’◡’●),当初那张稚嫩青涩的脸庞早已不见了模样,但是我们依然要记得来时的路。复习之余整理了一些关于计算机网络的基础知识以便查漏补缺,巩固加强(主要还是方便查阅)。在此还要感谢各位老师在我困惑中指点迷津。卑微小生,才疏学浅,还望各位同仁批评指正!时间紧迫,我们快开始吧!🤍知足,上进,不负野心!
祝我上岸成功

(一)计算机网络基础

1.1 计算机通信网的组成(●’◡’●)

  计算机网络是将不同地理位置的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

网络的主要类型
一、网络的地理覆盖范围分:

1
2
3
4
5
6
7
8
9
10
A、局域网:
  1、属于一个组织,一个单位或一个部门所有。
  2、网络一般不对外提供公共服务,管理方便,安全保密性高。
  3、组建方便,投资少,见效快,使用灵活应用最普遍的计算机网络。
B、城域网:
  1、一般是一个地区或一个城市。
C、广域网
  1、小到一个地区,一个城市,大到一个国家,几个国家乃至全世界。
  2、提供大范围的公共服务。因特网就是典型的广域网。
  3、与局域网相比,广域网投资大,安全保密性差,传输速率慢。

二、按计算机网络的线路结构分:

1
2
3
4
5
6
7
8
9
A、总线型
  1、该结构采用一条公共总线作为传输介质,每台计算机通过相应的硬件接口入网,信号沿总线进行广播式传送
  2、是典型的共享传输介质的网络。从信源所发的信息会传送到介质长度所及之处,被其他所有站点看到。如果有两个以上的节点同时发送数据,可能会造成冲突,就像公路上的两车相撞一样。

  优点:布线容易,增删容易,节约电缆;
  缺点:
  1、任何两个站点传送信息都要经过总线,总线称为传输瓶颈;当计算机站点多时,容易造成信息阻塞,传递不畅。
  2、一台计算机接入总线的接口发生故障,会造成整个网络瘫痪。
  3、当网络发生故障时,故障诊断和隔离困难。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
B、星型
  由一台中央节点和周围的从节点组成。
  中央节点和从节点可以直接通信,而从节点必须经过中央节点转接才能通信。

  优点:
  1、可靠性高。每台计算机及其接口的故障不会影响其他计算机,不会影响整个网络,也不会造成网络瘫痪。
  2、故障诊断和隔离容易,网络容易管理和维护。
  3、可扩性好,配置灵活。增删改一个站点容易实现,和其他计算机没有关系
  4、传输速率高。每个节点独占一条线路,消除了信息阻塞的情况。而总线和环形网络的瓶颈都在线路上。
     
  缺点:
  1、线缆使用量大。
  2、布线、安装工作量大。
  3、网络可靠性依赖于中央节点。如交换机或集线器选择不当,发生故障会造成全网瘫痪。
1
2
3
4
5
6
7
C、环型
  计算机通过硬件接口入网,这些接口首尾相连成一条链路。信息传送也是广播式的,沿着一个方向(如逆时针方向)单向逐点传送。
  优点:
  点到点且沿一个方向单向传输,非常适合用光纤作为传输介质。
  1、传输距离远,适合做主干网。
  2、故障诊断容易定位。
  3、初始安装容易,线缆用量少。环形线路也是一条总线,只是首尾封闭。

三、按传输介质分:

1
2
3
4
5
6
7
8
9
A、有线网络
  1、同轴电缆:成本低,安装方便,但传输率低,抗干扰能力一般,传输距离短
  2、双绞线:组建局域网时常用,优缺点类似于同轴电缆。
  3、光纤:主要用于网络的主干部分,其特点是成本高,安装技术要求高,传输距离长,传输率高,抗干扰能力强,且不会受到电子监听设备的监听等,是组建高安全性网络的理想选择。

B、无线网络
  1、红外线
  2、微波
  3、无线电

  计算机网络由通信子网和资源子网组成。
• 通信子网负责数据的无差错和有序传递,其处理功能包括差错控制、流量控制、路由选择、网络互连等。
• 资源子网是计算机通信的本地系统环境,包括主机、终端和应用程序等,资源子网的主要功能是用户资源配置、数据的处理和管理、软件和硬件共享以及负载均衡等。
• 总的来说,计算机通信网就是一个由通信子网承载的、传输和共享资源子网的各类信息的系统。(✿◕‿◕✿)

1.2 通信协议(●’◡’●)

为了完成计算机之间有序的信息交换,提出了通信协议的概念,其定义是相互通信的双方(或者多方)对如何进行信息交换所必须遵守的一整套规则。

协议涉及到三个要素,分别为:

1. 语法:语法是用户数据与控制信息的结构与格式,以及数据出现顺序的意义
2. 语义:用于解释比特流的每一部分的意义
3. 时序:事件实现顺序的详细说明

1.3 OSI参考模型(Open System Interconnection)

OSI参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系,一般称为OSI参考模型或七层模型。

OSI定义了网络互连的七层框架:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层

每一层实现各自的功能和协议,并完成与相邻层的接口通信,OSI的服务定义详细说明了各层所提供的服务。某一层的服务就是该层及其下各层的一种能力,它通过接口提供给更高一层。

OSI参考模型
物理层

1. 提供建立、维护和释放物理链路所需的机械、电气功能和规程等特性
2. 通过传输介质进行数据流(比特流)的物理传输、故障监测和物理层管理
3. 从数据链路层接收帧,将比特流转换成底层物理介质上的信号
4. 物理层并不是物理媒体本身,它只是开放系统中利用物理媒体实现物理连接的功能描述和执行连接的规程
5. 物理层提供用于建立、保持和断开物理连接的机械的、电气的、功能的和过程的条件
6. 简而言之,物理层提供有关同步和比特流在物理媒体上的传输手段

数据链路层

1. 在物理链路的两端之间传输数据
2. 在网络层实体间提供数据传输功能和控制
3. 提供数据的流量控制
4. 检测和纠正物理链路产生的差错
5. 格式化的消息称为帧
6. 数据链路层用于建立、维持和拆除链路连接,实现无差错传输的功能
7. 在点到点或点到多点的链路上,保证信息的可靠传递
8. 该层对连接相邻的通路进行差错控制、数据成帧、同步等控制

网络层

1. 负责端到端的数据的路由或交换,为透明地传输数据建立连接
2. 寻址并解决与数据在异构网络间传输相关的所有问题
3. 使用上面的传输层和下面的数据链路层的功能
4. 格式化的消息称为分组
5. 网络层规定了网络连接的建立、维持和拆除的协议
6. 它的主要功能是利用数据链路层所提供的相邻节点间的无差错数据传输功能,通过路由选择和中继功能,实现两个系统之间的连接

传输层

1. 提供无差错的数据传输
2. 接收来自会话层的数据,如果需要,将数据分割成更小的分组,向网络层传送分组并确保分组完整和正确到达它们的目的地
3. 在系统之间提供可靠的透明的数据传输,提供端到端的错误恢复和流量控制
4. 传输层完成开放系统之间的数据传送控制
5. 主要功能是开放系统之间的数据的收发确认
6. 同时还用于弥补各种通信网络的质量差异,对经过下三层之后仍然存在的传输差错进行恢复,进一步提高可靠性
7. 另外,还通过复用、分段和组合、连接和分离、分流和合流等技术措施,提高吞吐量和服务质量

会话层

1. 提供节点之间通信过程的协调
2. 负责执行会话规则(如:连接是否允许半双工或全双工通信)、同步数据流以及当故障发生时重新建立连接
3. 使用上面的表示层和下面的传输层的功能
4. 会话层依靠传输层以下的通信功能使数据传送功能在开放系统间有效地进行
5. 其主要功能是按照在应用进程之间的约定,按照正确的顺序收发数据,进行各种形式的对话
6. 控制方式可以归纳为以下两类:
7. 一是为了在会话应用中易于实现接受处理和发送处理的逐次交替变换,设置某一时刻只有一端发送数据。因此需要有交替改变发信端的传送控制。
8. 二是在类似文件传送等单方向传送大量数据的情况下,为了防备应用处理中出现意外,在传送数据的过程中需要给数据记上标记,当出现意外时,可以由记标记处重发。

表示层

1. 提供数据格式、变换和编码转换
2. 涉及正在传输数据的语法和语义
3. 将消息以合适电子传输的格式编码
4. 执行该层的数据压缩和加密
5. 从应用层接收消息,转换格式,并传送到会话层,该层常合并在应用层中
6. 表示层的主要功能是把应用层提供的信息变换为能够共同理解的形式,提供字符代码、数据格式、控制信息格式、加密等的统一表示
7. 表示层仅对应用层信息内容的形式进行变换,而不改变其内容本身

应用层

1. 包括各种协议,它们定义了具体的面向用户的应用:如电子邮件、文件传输等
2. 应用层是OSI参考模型的最高层。其功能是实现应用进程(如用户程序、终端操作员等)之间的信息交换,还具有一系列业务处理所需要的服务功能。

1.4 OSI模型的优点(●’◡’●)

(1) 分工合作,责任明确
性质相似的工作划分在同一层,性质不同的工作则划分到不同层,这样每一层的功能都是明确的,每一层都有其负责的工作范围,一旦出现问题,很容易找到问题所在的层,仅对此层加以改善即可。
(2) 对等交谈
计算机通过网络进行通信时,按照对等交谈的原则,即同一层找同层,通过各对等层的协议来进行通信,比如,两个对等的网络层使用网络协议通信。
对等层的协议
(3) 逐层处理,层层负责。
在OSI中,两个实体通信必须涉及下一层,只有相邻层之间可以通信,下层向上层提供服务,上层通过接口调用下层的服务,层间不能有越级调用关系,每层功能的实现都是在下层提供服务的基础上完成的。即每一层都是利用下层提供的服务来完成本层功能,并在此基础上为上层提供进一步的服务。

1
2
3
4
5
✈低三层模型属于通信子网,涉及为用户间提供透明连接,操作主要以每条链路为基础,在节点间的各条数据链路上进行通信。由网络层来控制各条链路上的通信,但要依赖于其他节点的协调操作。

✈高三层属于资源子网,主要涉及保证信息以正确可理解形式传送。

✈传输层是高三层和低三层之间的接口,它是第一个端到端的层次,保证透明的端到端连接,满足用户的服务质量(QoS)要求,并向高三层提供合适的信息形式。

1.5 TCP / IP协议簇(●’◡’●)

  由于ISO指定的OSI参考模型过于庞大和复杂,在实现的时候造成了很大困难,所以招到了很多批评。在实际中,TCP/IP协议栈获得了更加广泛的应用。主流的操作系统基本上都采用TCP/IP协议栈。
TCP/IP协议簇
五层协议体系结构的各层功能:

  应用层:是体系结构中的最高直接为用户的应用进程提供服务,在因特网中的应用层协议很多,如支持万维网应用的HTTP协议,支持电子邮件的SMTP协议,支持文件传送的FTP协议等。

  运输层:负责向两个主机中进程之间的通信提供服务由于一个主机可同时运行多个进程,因此运输层有复用和分用的功能:

1. 复用,是多个应用层进程可同时使用下面运输层的服务
2. 分用,是把收到的信息分别交付给上面应用层中相应的进程
  1. 运输层主要使用以下两种协议:
  2. 传输控制协议TCP:面向连接的,数据传输的单位是报文段,能够提供可靠的交付
  3. 用户数据包协议UDP:面向无连接的,数据传输的单位是用户数据报,不保证提供可靠的交付,只能提供尽最大努力交付

  网络层:主要包括以下两个任务:

  1. 负责为分组交换网上的不同主机提供通信服务在发送数据时,网络层把运输层残生的报文段或用户数据报封装成分组或包进行传送。在TCP/IP体系中,由于网络层使用IP协议,因此分组也叫做IP数据报,或简称为数据报。
  2. 选中合适的路由,使源主机运输层所传下来的分组,能够通过网络中的路由器找到目的主机。

  数据链路层:常简称为链路层,两个主机之间的数据传输,总是在一段一段的链路上传送的,也就是说,在两个相邻结点之间传送数据是直接传送的(点对点),这时就需要使用专门的链路层的协议。

在两个相邻结点之间传送数据时,数据链路层将网络层交下来的IP数据报组装成帧,在两个相邻结点之间的链路上“透明”地传送帧中的数据,每一帧包括数据和必要的控制信息(如同步信息、地址信息、差错控制等)。

注:透明是一个很重要的术语::它表示某一个实际存在的事物看起来却好像不存在一样。”在数据链路层透明传送数据”表示无论什么样的比特组合的数据都能够通过这个数据链路层。因此,对所传送的数据来说,这些数据就“看不见”数据链路层。或者说,数据链路层对这些数据来说是透明的。

   (1)在接收数据时,控制信息使接收端能知道一个帧从哪个比特开始和到哪个比特结束。这样数据链路层在收到一个帧后,就可从中提取出数据部分,上交给网络层。
   (2)控制信息还使接收端能检测到所收到的帧中有无差错。如发现有差错,数据链路层就简单地丢弃这个出了差错的帧,以免继续传送下去白白浪费网络资源。如需改正错误,就由运输层的TCP协议来完成。

  物理层:在物理层上所传数据的单位是比特,物理层的任务就是透明地传送比特流。

1.6 数据在各层之间的传递过程(●’◡’●)

数据在各层之间的传递过程
OSI参考模型各层主要功能、传输数据单位:

物理层 原始比特流的传输,基本单位:比特
数据链路层 建立相邻节点数据链路传输,基本单位:帧
网络层 基于IP地址的路由选路传输数据,基本单位: 数据包
传输层 常规数据传递,面向连接或者无连接,基本单位:数据段
会话层 建立会话关系
表示层 统一数据传输格式
应用层 为用户应用程序提供服务接口
1
2
3
4
5
物理层 --> 比特流 --> bit
数据链路层 --> 数据帧 --> frame
网络层 --> 数据包 --> packet
传输层 --> 数据段 --> segment
应用层 --> 消息 --> message

(二)MAC地址

  MAC地址也叫物理地址、硬件地址,由网络设备制造商生产时烧录在网卡的EPROM。IP地址与MAC地址在计算机里都是以二进制表示的,IP地址是32位的,而MAC地址则是48位的。

  MAC地址的长度为48位(6个字节),通常表示为12个16进制数,如:00-16-EA-AE-3C-40就是一个MAC地址,其中前3个字节,16进制数00-16-EA代表网络硬件制造商的编号,它由IEEE(电气与电子工程师协会)分配,而后3个字节,16进制数AE-3C-40代表该制造商所制造的某个网络产品(如网卡)的系列号。

  只要不更改自己的MAC地址,MAC地址在世界是唯一的。形象地说,MAC地址就如同身份证上的身份证号码,具有唯一性。

2.1 MAC地址组成(●’◡’●)

MAC地址组成
MAC帧格式
MAC帧格式
单播:

指从单一的源端发送到单一的目的端。每个主机接口由一个MAC地址唯一标识,MAC地址的OUI中,第一字节第8个比特表示地址类型。对于主机MAC地址,这个比特固定为0,表示目的MAC地址为此MAC地址的帧都是发送到某个唯一的目的端。在冲突域中,所有主机都能收到源主机发送的单播帧,但是其他主机发现目的地址与本地MAC地址不一致后会丢弃收到的帧,只有真正的目的主机才会接收并处理收到的帧。

广播:

表示帧从单一的源发送到共享以太网上的所有主机。广播帧的目的MAC地址为十六进制的FFFFFFFFFFFF,所有收到该广播帧的主机都要接收并处理这个帧。
广播方式会产生大量流量,导致带宽利用率降低,进而影响整个网络的性能,当需要网络中的所有主机都能接收到相同的信息并进行处理的情况下,通常会使用广播方式。

组播:

组播比广播更加高效,组播转发可以理解为选择性的广播,主机侦听特定组播地址,接收并处理目的MAC地址为该组播MAC地址的帧。
组播MAC地址和单播MAC地址是通过第一字节中的第8个比特区分的。
组播MAC地址的第8个比特为1,而单播MAC地址的第8个比特为0。
当需要网络上的一组主机(而不是全部主机)接收相同信息,并且其他主机不受影响的情况下通常会使用组播方式。

(三)IP地址

  IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。

IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。

3.1 IP地址的组成(●’◡’●)

(1)长度:32bit(4字节=4B=32bit),一共有2^32个IPv4地址。

(2)写法:4组点分十进制数,意思将一个32位的IP地址分为4段,每段由二进制转换为十进制的数来进行表示,每段数字范围为0-255。

(3)组成:一个IP地址由网络号和主机号组成。

3.2 IP地址类型(●’◡’●)

1. 公有地址

公有地址(Public address)由Inter NIC(Internet Network Information Center 因特网信息中心)负责,这些IP地址分配给注册并向Inter NIC提出申请的组织机构,通过它直接访问因特网。

2. 私有地址

私有地址(Private address)属于非注册地址,专门为组织机构内部使用。

1
2
3
A类 10.0.0.0--10.255.255.255
B类 172.16.0.0--172.31.255.255
C类 192.168.0.0--192.168.255.255

3.3 IP地址编址方式(●’◡’●)

最初设计互联网时,为了便于寻址以及层次化构造网络,每个IP地址包括两个标识码ID,即网络ID和主机ID。同一个物理网络上的所有主机都使用同一个网络ID,网络上的一个主机有一个主机ID与其对应。
在这里插入图片描述

A类IP地址

一个A类IP地址是指, 在IP地址的四段号码中,第一段号码为网络号码,剩下的三段号码为本地计算机的号码。如果用二进制表示IP地址的话,A类IP地址就由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是”0”。A类IP地址中网络的标识长度为8位,主机标识的长度为24位,A类网络地址数量较少,有126个网络,每个网络可以容纳主机数达1600多万台。

A类IP地址的地址范围1.0.0.1到127.255.255.254

1
(二进制表示为:00000001 00000000 00000000 00000001----01111111 11111111 11111111 11111110)
B类IP地址

一个B类IP地址是指,在IP地址的四段号码中,前两段号码为网络号码。如果用二进制表示IP地址的话,B类IP地址就由2字节的网络地址和2字节主机地址组成,网络地址的最高位必须是”10”。B类IP地址中网络的标识长度为16位,主机标识的长度为14位,B类网络地址适用于中等规模的网络,有16384个网络,每个网络所能容纳的计算机数为6万多台。

B类IP地址地址范围128.0.0.1到191.255.255.254

1
(二进制表示为:10000000 00000000 00000000 00000001----10111111 11111111 11111111 11111110)
C类IP地址

一个C类IP地址是指,在IP地址的四段号码中,前三段号码为网络号码,剩下的一段号码为本地计算机的号码。如果用二进制表示IP地址的话,C类IP地址就由3字节的网络地址和1字节主机地址组成,网络地址的最高位必须是”110”。C类IP地址中网络的标识长度为24位,主机标识的长度为8位,C类网络地址数量较多,有209万余个网络。适用于小规模的局域网络,每个网络最多只能包含254台计算机。

C类IP地址范围192.0.0.1到223.255.255.254

1
(二进制表示为: 11000000 00000000 00000000 00000001----11011111 11111111 11111111 11111110)
1
注意:IP地址中不能以十进制127作为开头,该类地址中数字127.0.0.1到127.255.255.255用于回路测试

3.4 子网(●’◡’●)

  引入子网掩码,从逻辑上把一个大网络划分成一些小网络。子网掩码是由一系列的1和0构成,通过将其同IP地址做与运算来指出一个IP地址的网络号是什么。
  对于传统IP地址分类来说,A类地址的子网掩码是255.0.0.0;B类地址的子网掩码是255.255.0.0;C类地址的子网掩码是255.255.255.0。

例如,如果要将一个B类网络166.111.0.0划分为多个C类子网来用的话,只要将其子网掩码设置为255.255.255.0即可,这样166.111.1.1和166.111.2.1就分属于不同的网络了。像这样,通过较长的子网掩码将一个网络划分为多个网络的方法就叫做划分子网。

3.5 超网(●’◡’●)

  超网是同子网类似的概念,它通过较短的子网掩码将多个小网络合成一个大网络。

例如,一个单位分到了8个C类地址:202.120.224.0到202.120.231.0,只要将其子网掩码设置为255.255.248.0,就能使这些C类网络相通。

(四)ARP / RARP协议

  地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址。收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。

4.1 ARP欺骗(●ˇ∀ˇ●)

地址解析协议是建立在网络中各个主机互相信任的基础上的,网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记入本机ARP缓存;由此攻击者就可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个ARP欺骗。

4.2 ARP工作流程举例(●ˇ∀ˇ●)

主机A的IP地址为192.168.1.1,MAC地址为0A-11-22-33-44-01;
主机B的IP地址为192.168.1.2,MAC地址为0A-11-22-33-44-02;
  
当主机A要与主机B通信时,地址解析协议可以将主机B的IP地址192.168.1.2解析成主机B的MAC地址,以下为工作流程:

  1. 根据主机A上的路由表内容,IP确定用于访问主机B的转发IP地址是192.168.1.2,然后A主机在自己的本地ARP缓存中检查主机B的匹配MAC地址。
  2. 如果主机A在ARP缓存中没有找到映射,它将询问192.168.1.2的硬件地址,从而将ARP请求帧广播到本地网络上的所有主机。源主机A的IP地址和MAC地址都包括在ARP请求中。本地网络上的每台主机都接收到ARP请求并且检查是否与自己的IP地址匹配。
  3. 如果主机发现请求的IP地址与自己的IP地址不匹配,它将丢弃ARP请求。
  4. 主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射添加到本地ARP缓存中。
  5. 主机B将包含其MAC地址的ARP回复消息直接发送回主机A。
  6. 当主机A收到从主机B发来的ARP回复消息时,会用主机B的IP和MAC地址映射更新ARP缓存。本机缓存是有生存期的,生存期结束后,将再次重复上面的过程。主机B的MAC地址一旦确定,主机A就能向主机B发送IP通信了。

4.3 逆地址解析协议(●ˇ∀ˇ●)

  RARP,功能和ARP协议相对,其将局域网中某个主机的物理地址转换为IP地址,比如局域网中有一台主机只知道物理地址而不知道IP地址,那么可以通过RARP协议发出征求自身IP地址的广播请求,然后由RARP服务器负责回答。

RARP协议工作流程:
  (1)给主机发送一个本地的RARP广播,在此广播包中,声明自己的MAC地址并且请求任何收到此请求的RARP服务器分配一个IP地址;
  (2)本地网段上的RARP服务器收到此请求后,检查其RARP列表,查找该MAC地址对应的IP地址;
  (3)如果存在,RARP服务器就给源主机发送一个响应数据包并将此IP地址提供给对方主机使用;
  (4)如果不存在,RARP服务器对此不做任何的响应;
  (5)源主机收到从RARP服务器的响应信息,就利用得到的IP地址进行通讯;
  (6)如果一直没有收到RARP服务器的响应信息,表示初始化失败。

(五)IP协议

  IP协议是TCP/IP协议族中最为核心的协议。所有的TCP、IP、ICMP、IGMP数据都以IP协议数据报格式发送。IP协议提供无连接、不可靠的数据报传送服务。

  1. 不可靠:它不保证数据能成功地发送到目的地,IP协议仅提供最好地传输服务。如果发生某种错误,如路由器暂时用完缓冲区,IP协议的处理算法是:丢弃该数据报,然后发送ICMP消息给发送源(告诉发送源为什么丢弃),任何要求的可靠性都需要在上层实现。
  2. 无连接:IP协议并不维护后序数据报的状态信息,每个数据报的处理是独立的。举例:A向B依次发送1、2两个数据报,这两个数据报在网络上选择的路由线路可能不同,因此很有可能先收到报文2,后收到报文1。对于此种情况,IP协议并没有做处理。

5.1 IP报头(●ˇ∀ˇ●)

IP报头
中文版:
IP报头

  1. Version(版本信息)(占4bit,指IP协议的版本)
  2. Header Length(头部长度)
    1. 头部长度是指IP报头的总长度,因为有Option可选部分,通常为20字节,在20–60字节;
    2. 该字段单位为32位字(1个32位字为4字节),因此当IP报头长度为1111时是最大60字节;
    3. IP报头长度不是4字节的整数倍是,就需要对填充域进行填充;
  3. Differentiated Services Field(服务类型)(占8bit)
    TOS,服务类型:用来指定特殊的报文处理方式
  4. Total Length(总长度)(占16bit)
    1. 标示此IP报头和数据的之和的总长度
    2. 总长度16位,一个数据最大长度65535字节,链路只允许1500字节,超过的话需要进行MTU分片
    3. 一个数据包由IP报头和数据两部分组成,而IP报头为20—60字节,所以不会有一个数据包里纯数据超过1480字节的
  5. Identification(ID标识符)(占16bit)与标记字段和偏移字段用于IP报文分片
    1. 原始报文大小超过MTU(<1480字节)就必须将原始数据进行分片,每个分片小于MTU;
    2. 对同一原始文件被分片的报文打上相同的标记,也用来判断流量是否来于同一主机;
    3. IP软件在存储器中维持一个计数器没生产一个数据包,计数器就加1,并赋予标识字段;
    4. 数据报文进行分片处理后每个分片的标识值都与原数据包的标识值相同,接收端具有同标识值的分片就能最终正确重组为原数据。
  6. Flag(标记)(占3bit),目前只有2位具有意义:
    1. 第一位没有被使用
    2. 第二位不分片为(DF),当DF位置为1时表示路由器不能对报文进行分片处理
    3. 第三位(多分片)(MF)
    4. 当路由器对报文进行分片时,除了最后一个分片的MF位设置为0外,其他所有分片MF位置为1,以便接收者直到收到MF位为0的分片为止
  7. Fragmentation Offset(分片偏移)(13bit)
    1. 标识分片在分组中的位置
    2. 片偏移以8个字节为偏移单位,分片的长度为8字节的整数倍

以太网最大帧长为1518B,IP报文1500B(三层)+ 帧头18B(二层)
注意:MTU不是固定1500,这要取决现场物理环境,MTU不包含帧头帧尾。

  1. Time To Live(TTL生存时间)(8bit)
    1. 跳数大小,即数据包能传多少跳
    2. 不同操作系统TTL的默认最大值会有所不同(Linux—255;Win98—225;Win7/8/10—64)
    3. 表示数据包在网络中的寿命(最初以秒为单位,现在以跳数为单位,最大225)
    4. 分组寿命,防止路由成环时IP数据被无限转发
    5. 源和目的路由器之间的路由数量(跳数)
    6. 路由设备每此转发将TTL值减1,TTL为0时丢弃该分组
  2. Protocol(协议)(8bit)
    标识数据携带的数据是何种协议,标识传输层地址或协议号,如1代表ICMP,6代表TCP,17代表UDP
  3. Header Checksum(报头校验和)(16bit)检查报文头部的完整性
    1. 用于校验检查IP报头是否有出入
    2. 只校验IP报头部,数据部分由高层协议校验(TCP头的校验字段包含IP头和数据的校验)
    3. 无需重复校验数据部分,缩短路由器转发分组时的处理时间,数据部分由终端用高层协议校验
  1. 发送方先把校验和字段置为0,对首部中每个16bit(切割多个16bit)进行二进制反码求和,结果存在校验和字段中

  2. 收到一份IP数据包后同样对首部中每个16bit进行二进制码反求和,接收方计算中包含了发送方存在的首部校验和

  3. 如果传输过程无错误,接收方结算结果全为1,传输中出现错误或数据丢失校验和结果为非全1,接受者丢弃校验未通过数据

  4. 不生成错误报文,由上层发现丢失数据进行重传

  1. Source Ip address(源IP地址)(32bit)
    数据发起者的IP地址

  2. Destination Ip address(目的IP地址)(32bit)
    数据的接收者IP地址

  3. Option(可选字段)(24bit)
    24bit用于提供一些数据报可选的服务,时间戳,记录路由等

  4. Padding(填充项)
    Options一般为0或者是32bit的倍数,如果不够32bit或者32bit的倍数则由Padding补齐。

(六)TCP / IP协议

&emsp;&emsp;TCP/IP协议是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地。

&emsp;&emsp;IP层接收由更低层(网络接口层)发来的数据包,并把该数据包发送到更高层TCP或UDP层;相反,IP层也把从TCP或UDP层接收来的数据包传送到更低层。IP数据包是不可靠的,因为IP并没有做任何事情来确认数据包是否按顺序发送的或者有没有被破坏,IP数据包中含有发送它的主机的地址(源地址)和接收它的主机的地址(目的地址)。

6.1 UDP(●’◡’●)

UDP:用户数据报文协议
用户数据报文协议

1
2
3
4
16位源端口
16位目标端口
16位的UDP包长度(UDP头部和UDP数据的总长度字节)
16位头部校验和

6.2 UDP的特点(●’◡’●)

1.协议开销小、效率高
2.UDP是无连接的,即发送数据之前不需要建立连接
3.UDP使用尽最大努力交付,即不保证可靠交付
4.UDP没有拥塞控制
5.UDP支持一对一、一对多、多对一和多对多交互通信
6.UDP的首部开销小,只有8个字节

6.3 TCP(●’◡’●)

&emsp;&emsp;TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由RFC793定义。

&emsp;&emsp;TCP是面向连接的通信协议,通过三次握手建立连接,通讯完成时要拆除连接,由于TCP是面向连接的所以只能用于端到端的通讯。

&emsp;&emsp;TCP提供的是一种可靠的数据流服务,采用”带重传的肯定确认”技术来实现传输的可靠性。TCP还采用一种称为”滑动窗口”的方式进行流量控制,所谓窗口实际表示接收能力,用以限制发送方的发送速度。
TCP
TCP

  1. 16位源端口,16位目的端口
  2. 32位序列编号:用来解决网络包乱序(reordering)问题。
  3. 32位确认序号ACK:用于确认收到数据包,用来解决不丢包的问题。
  4. 4位首部长度:表示该TCP报头有多少个4字节(32个bit)
  5. 6位保留位
  6. 6位标志位
    1. URG: 标识紧急指针是否有效
    2. ACK: 标识确认序号是否有效
    3. PSH: 用来提示接收端应用程序立刻将数据从TCP缓冲区读走
    4. RST: 要求重新建立连接. 我们把含有RST标识的报文称为复位报文段
    5. SYN: 请求建立连接. 我们把含有SYN标识的报文称为同步报文段
    6. FIN: 通知对端,本端即将关闭。我们把含有FIN标识的报文称为结束报文段
  7. 16位窗口大小:用来数据传输时的流量控制避免拥塞
  8. 16位校验和:发送端填充,检验形式有CRC校验等,如果接收端校验不通过,则认为数据有问题。此处的校验和不光包含TCP首部,也包含TCP数据部分
  9. 16位紧急指针:用来标识哪部分数据是紧急数据

6.4 TCP的特点(●’◡’●)

一、面向连接的传输,传输数据前需要先建立连接,数据传输完毕要释放连接
二、端到端通信,不支持广播通信
三、高可靠性,确保传输数据的正确性,不出现丢失或乱序
四、全双工方式传输
五、采用字节流方式 ,即以字节为单位传输字节序列。如果字节流太长,将其分段。
六、提供紧急数据传送功能,即当有紧急数据要发送时,发送进程会立即发送,接收方收到后会暂停当前工作,读取紧急数据并做相应处理。

6.5 TCP的状态(●’◡’●)

TCP的状态

6.6 三次握手与四次挥手问题(●’◡’●)

三次握手与四次挥手过程图:
三次握手与四次挥手过程图
&emsp;&emsp;三次握手(Three-Way Handshake)是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。

  1. 第一次握手:客户端将标志位SYN置为1,随机产生一个值 seq=s,并将该数据包发送给服务端,客户端进入SYN_SENT状态,等待服务端确认。
  2. 第二次握手:服务端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务端将标志位SYN和ACK都置为1,ack=s+1,随机产生一个值seq=k,并将该数据包发送给客户端以确认连接请求,服务端进入SYN_RCVD状态。
  3. 第三次握手:客户端收到确认后,检查ack值是否为s+1,ACK标志位是否为1,如果正确则将标志位ACK置为1,ack=k+1,并将该数据包发送给服务端。服务端检查ack值是否为k+1,ACK标志位是否为1,如果正确则连接建立成功,客户端和服务端进入ESTABLISHED 状态,完成三次握手。
6.6.1 为什么要三次握手?

&emsp;&emsp;在只有两次握手的情形下,假设Client想跟Server建立连接,但是却因为中途连接请求的数据报丢失了,故Client端不得不重新发送一遍。这个时候Server端仅收到一个连接请求,因此可以正常的建立连接。但是,有时候Client端重新发送请求不是因为数据报丢失了,而是有可能数据传输过程因为网络并发量很大在某结点被阻塞了,这种情形下Server端将先后收到2次请求,并持续等待两个Client请求向它发送数据。
&emsp;&emsp;问题就在这里,Cient端实际上只有一次请求,而Server端却有2个响应,极端的情况可能由于Client端多次重新发送请求数据而导致Server端最后建立了N多个响应在等待,因而造成极大的资源浪费!所以,三次握手很有必要!

&emsp;&emsp;四次挥手(Four-Way Wavehand)指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。

  1. 第一次挥手客户端发送一个FIN,用来关闭客户端到服务端的数据传送,客户端进入FIN_WAIT_1状态。
  2. 第二次挥手服务端收到FIN后,发送一个 ACK 给客户端,确认序号为收到序号+1,服务端进入CLOSE_WAIT状态。
  3. 第三次挥手服务端发送一个FIN,用来关闭服务端到客户端的数据传送,服务端进入LAST_ACK 状态。
  4. 第四次挥手客户端收到FIN 后,客户端进入TIME_WAIT 状态,接着发送一个ACK 给服务端,确认序号为收到序号+1,服务端进入 CLOSED状态,完成四次挥手。
6.6.2 为什么要四次挥手?

&emsp;&emsp;试想一下,假如现在你是客户端,你想断开跟Server的所有连接该怎么做?
第一步,你自己先停止向Server端发送数据,并等待Server的回复。但事情还没有完,虽然你自身不往Server发送数据了,但是因为你们之前已经建立好平等的连接了,所以此时Server也有主动权向你发送数据。故Server端还得终止主动向你发送数据,并等待你的确认。其实,说白了就是保证双方的一个合约的完整执行!

6.7 三次握手详解(●’◡’●)

三次握手过程图:
三次握手过程图

  1. 握手之前主动打开连接的客户端结束CLOSED阶段,被动打开的服务器端也结束CLOSED阶段,并进入LISTEN阶段。
  2. 随后开始三次握手:
    1. 首先客户端向服务器端发送一段TCP报文,其中:标记位为SYN,表示“请求建立新连接”,序号为Seq=x(x一般为1),随后客户端进入SYN-SENT阶段。
    2. 服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文,其中:标志位为SYN和ACK,表示“确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接”(即告诉客户端,服务器收到了你的数据);
    3. 服务器端想从y开始,序号为Seq=y;确认号为Ack=x+1,表示收到客户端的序号Seq并将其值加1作为自己确认号Ack的值。随后服务器端进入SYN-RCVD阶段。
  3. 客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文。其中:标志位为ACK,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了);序号为Seq=x+1,表示收到服务器端的确认号Ack,并将其值作为自己的序号值;确认号为Ack=y+1,表示收到服务器端序号Seq,并将其值加1作为自己的确认号Ack的值;随后客户端进入ESTABLISHED阶段。
  4. 服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。
  5. 在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续握手,以此确保了三次握手的顺利完成,此后客户端和服务器端进行正常的数据传输。
6.7.1 为什么要进行第三次握手?

&emsp;&emsp;1.进行第三次握手是为了防止服务器端开启一些无用的连接增加服务器开销以及防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
&emsp;&emsp;2.由于网络传输是有延时的(要通过网络光纤和各种中间代理服务器),在传输的过程中,比如客户端发起了SYN=1创建连接的请求(第一次握手)。
&emsp;&emsp;3.如果服务器端直接创建了这个连接并返回包含SYN、ACK和Seq等内容的数据包给客户端,这个数据包因为网络传输的原因丢失了,丢失之后客户端就一直没有接收到服务器返回的数据包。客户端可能设置了一个超时时间,时间到了就关闭了连接创建的请求。再重新发出创建连接的请求,而服务器端是不知道的,如果没有第三次握手告诉服务器端:客户端收的到服务器端传输的数据的话,服务器端是不知道客户端有没有接收到服务器端返回的信息的。

这个过程可理解为:
第三次握手
&emsp;&emsp;4.这样没有给服务器端一个创建还是关闭连接端口的请求,服务器端的端口就一直开着,等到客户端因超时重新发出请求时,服务器就会重新开启一个端口连接。那么服务器端上没有接收到请求数据的上一个端口就一直开着,长此以往,这样的端口多了,就会造成服务器端开销的严重浪费。
&emsp;&emsp;5.还有一种情况是已经失效的客户端发出的请求信息,由于某种原因传输到了服务器端,服务器端以为是客户端发出的有效请求,接收后产生错误。
&emsp;&emsp;6.所以我们需要第三次握手来确认这个过程,让客户端和服务器端能够及时地察觉到因为网络等一些问题导致的连接创建失败,这样服务器端的端口就可以关闭了不用一直等待。

也可以这样理解:第三次握手是客户端向服务器端发送数据,这个数据就是要告诉服务器,客户端有没有收到服务器第二次握手时传过去的数据。若发送的这个数据是”收到了”的信息,接收后服务器就正常建立TCP连接,否则建立TCP连接失败,服务器关闭连接端口,由此减少服务器开销和接收到失效请求发生的错误。

6.8 四次挥手详解:(●’◡’●)

四次挥手过程图:
四次挥手过程图

  1. 客户端打算关闭连接,此时会发送一个TCP首部FIN标志位被置为1的报文,也即FIN报文,之后客户端进入FIN_WAIT_1状态。
  2. 服务端收到该报文后,就向客户端发送ACK应答报文,接着服务端进入CLOSED_WAIT状态。
  3. 客户端收到服务端的ACK应答报文后,之后进入FIN_WAIT_2状态。等待服务端处理完数据后,也向客户端发送FIN报文,之后服务端进入LAST_ACK状态。客户端收到服务端的FIN报文后,回一个ACK应答报文,之后进入TIME_WAIT状态。
  4. 服务器收到了ACK应答报文后,就进入了CLOSE状态,至此服务端已经完成连接的关闭。
  5. 客户端在经过2MSL一段时间后,自动进入CLOSE状态,至此客户端也完成连接的关闭。
    四次挥手过程图

四次挥手详解:

  1. 挥手之前主动释放连接的客户端结束ESTABLISHED阶段。
  2. 随后开始四次挥手:
    1. 首先客户端想要释放连接,向服务器端发送一段TCP报文,其中:标记位为FIN,表示“请求释放连接“;序号为Seq=u;随后客户端进入FIN-WAIT-1阶段,即半关闭阶段。并且停止在客户端到服务器端方向上发送数据,但是客户端仍然能接收从服务器端传输过来的数据。注意:这里不发送的是正常连接时传输的数据(非确认报文),而不是一切数据,所以客户端仍然能发送ACK确认报文。
    2. 服务器端接收到从客户端发出的TCP报文之后,确认了客户端想要释放连接,随后服务器端结束ESTABLISHED阶段,进入CLOSE-WAIT阶段(半关闭状态)并返回一段TCP报文,其中:标记位为ACK,表示“接收到客户端发送的释放连接的请求”;序号为Seq=v;确认号为Ack=u+1,表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值。
    3. 随后服务器端开始准备释放服务器端到客户端方向上的连接。
    4. 客户端收到从服务器端发出的TCP报文之后,确认了服务器收到了客户端发出的释放连接请求,随后客户端结束FIN-WAIT-1阶段,进入FIN-WAIT-2阶段,前”两次挥手”既让服务器端知道了客户端想要释放连接,也让客户端知道了服务器端了解了自己想要释放连接的请求。于是,可以确认关闭客户端到服务器端方向上的连接了。
  3. 服务器端自从发出ACK确认报文之后,经过CLOSED-WAIT阶段,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段TCP报文,其中:标记位为FIN,ACK,表示“已经准备好释放连接了”。注意:这里的ACK并不是确认收到服务器端报文的确认报文。序号为Seq=w;确认号为Ack=u+1;表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值。随后服务器端结束CLOSE-WAIT阶段,进入LAST-ACK阶段。并且停止在服务器端到客户端的方向上发送数据,但是服务器端仍然能够接收从客户端传输过来的数据。
  4. 客户端收到从服务器端发出的TCP报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器端发送一段报文,其中:标记位为ACK,表示“接收到服务器准备好释放连接的信号”。序号为Seq=u+1;表示是在收到了服务器端报文的基础上,将其确认号Ack值作为本段报文序号的值。确认号为Ack=w+1;表示是在收到了服务器端报文的基础上,将其序号Seq值作为本段报文确认号的值。
  5. 随后客户端开始在TIME-WAIT阶段等待2MSL,服务器端收到从客户端发出的TCP报文之后结束LAST-ACK阶段,进入CLOSED阶段。由此正式确认关闭服务器端到客户端方向上的连接。
  6. 客户端等待完2MSL之后,结束TIME-WAIT阶段,进入CLOSED阶段,由此完成四次挥手。后两次挥手既让客户端知道了服务器端准备好释放连接了,也让服务器端知道了客户端了解了自己准备好释放连接了。于是,可以确认关闭服务器端到客户端方向上的连接了,由此完成四次挥手。
  7. 与三次挥手一样,在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性,一旦出现某一方发出的TCP报文丢失,便无法继续挥手,以此确保了四次挥手的顺利完成。
6.8.1 为什么”握手”是三次,”挥手”却要四次?

&emsp;&emsp;TCP建立连接时之所以只需要”三次握手”,是因为在第二次握手过程中,服务器端发送给客户端的TCP报文是以SYN与ACK作为标志位的。SYN是请求连接标志,表示服务器端同意建立连接;ACK是确认报文,表示告诉客户端,服务器端收到了它的请求报文。即SYN建立连接报文与ACK确认接收报文是在同一次握手当中传输的,所以三次握手不多也不少,正好让双方明确彼此信息互通。
&emsp;&emsp;TCP释放连接时之所以需要四次挥手,是因为FIN释放连接报文与ACK确认接收报文是分别由第二次和第三次握手传输的。

6.8.2 为何建立连接时一起传输,释放连接时却要分开传输?

&emsp;&emsp;建立连接时,被动方服务器端结束CLOSED阶段进入握手阶段并不需要任何准备,可以直接返回SYN和ACK报文,开始建立连接。
&emsp;&emsp;释放连接时,被动方服务器,突然收到主动方客户端释放连接的请求时并不能立即释放连接,因为还有必要的数据需要处理,所以服务器先返回ACK确认收到报文,经过CLOSE-WAIT阶段准备好释放连接之后,才能返回FIN释放连接报文。

6.8.3 为什么客户端在TIME-WAIT阶段要等2MSL?

&emsp;&emsp;为的是确认服务器端是否收到客户端发出的ACK确认报文

&emsp;&emsp;当客户端发出最后的ACK确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。

&emsp;&emsp;MSL指的是Maximum Segment Lifetime:一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长。

&emsp;&emsp;服务器端在1MSL内没有收到客户端发出的ACK确认报文,就会再次向客户端发出FIN报文。如果客户端在2MSL内,再次收到了来自服务器端的FIN报文,说明服务器端由于各种原因没有接收到客户端发出的ACK确认报文。
&emsp;&emsp;客户端再次向服务器端发出ACK确认报文,计时器重置,重新开始2MSL的计时;否则客户端在2MSL内没有再次收到来自服务器端的FIN报文,说明服务器端正常接收了ACK确认报文,客户端可以进入CLOSED阶段,完成“四次挥手”。
&emsp;&emsp;所以,客户端要经历时长为2SML的TIME-WAIT阶段,这也是为什么客户端比服务器端晚进入CLOSED阶段的原因。

(七)DNS协议

&emsp;&emsp;DNS是域名系统(Domain Name System)的缩写,该系统用于命名组织到域层次结构中的计算机和网络服务,可以简单地理解为将URL转换为IP地址。
&emsp;&emsp;域名是由圆点分开一串单词或缩写组成的,每一个域名都对应一个唯一的IP地址,在Internet上域名与IP地址之间是一一对应的,DNS就是进行域名解析的服务器。
&emsp;&emsp;因特网采用了层次树状结构的命名方法。任何一个连接在因特网上的主机或路由器,都有一个唯一的层次结构的名字,即域名。域名空间有根域,顶层域,二级域,子域,区域之分。

7.1 域名结构示意图(●’◡’●)

域名结构示意图
每一个域名都是一个标号序列,用字母、数字和连接符组成,标号序列总长度不能超过255个字符,它由点号分割成一个个的标号,每个标号应该在63个字符之内,每个标号都可以看成一个层次的域名。

级别最低的域名写在左边,级别最高的域名写在右边

域名服务主要是基于UDP实现的,服务器的端口号为53

7.2 域名解析方式(●’◡’●)

  1. 递归查询法
    1. 如果DNS服务器无法解析出DNS客户机所要求查询的域名所对应的IP地址时,DNS服务器代表DNS客户机来查询或联系其他DNS服务器,以完全解析该名称,并将应答返回给客户机。
    2. 本机向本地域名服务器发出一次查询请求,就静待最终的结果。如果本地域名服务器无法解析,自己会以DNS客户机的身份向其它域名服务器查询,直到得到最终的IP地址告诉本机。
  2. 迭代查询法
    1. 客户机送出查询请求后,若该DNS服务器中不包含所需数据,它会告诉客户机另外一台DNS服务器的IP地址,使客户机自动转向另一台DNS服务器查询,以此类推,直到查询数据,否则由最后一台DNS服务器通知客户机查询失败。
    2. 本地域名服务器向根域名服务器查询,根域名服务器告诉它下一步到哪里去查询,然后它再去查,每次它都是以客户机的身份去各个服务器查询。
  3. 反向查询
    客户机利用IP地址查询其主机完整域名。

7.3 域名解析过程(●’◡’●)

域名解析总体可分为两大步骤:
第一个步骤是本机向本地域名服务器发出一个DNS请求报文,报文里携带需要查询的域名。
第二个步骤是本地域名服务器向本机回应一个DNS响应报文,里面包含域名对应的IP地址。

1
2
3
4
5
6
7
8
9
10
从下面对jocent.me进行域名解析的报文中可明显看出这两大步骤:
具体的流程可描述如下:
1. 主机10.74.36.90先向本地域名服务器10.74.1.11进行递归查询
2. 本地域名服务器采用迭代查询,向一个根域名服务器进行查询
3. 根域名服务器告诉本地域名服务器,下一次应该查询的顶级域名服务器dns.me的IP地址
4. 本地域名服务器向顶级域名服务器dns.me进行查询
5. 顶级域名服务器me告诉本地域名服务器,下一步查询权限服务器dns.jocent.me 的IP地址
6. 本地域名服务器向权限服务器dns.jocent.me进行查询
7. 权限服务器dns.jocent.me告诉本地域名服务器所查询的主机的IP地址
8. 本地域名服务器最后把查询结果告诉10.74.36.90

7.4 DNS协议报文格式(●’◡’●)

DNS协议报文格式
整个 DNS 格式主要分为 3 部分内容,即基础结构部分、问题部分、资源记录部分:

1. 基础结构部分
基础结构部分

1. 事务 ID:DNS 报文的 ID 标识,对于请求报文和其对应的应答报文,该字段的值是相同的,通过它可以区分 DNS 应答报文是对哪个请求进行响应的
2. 标志:DNS 报文中的标志字段
3. 问题计数:DNS 查询请求的数目
4. 回答资源记录数:DNS 响应的数目
5. 权威名称服务器计数:权威名称服务器的数目
6. 附加资源记录数:额外的记录数目(权威名称服务器对应 IP 地址的数目)

基础结构部分中的标志字段又分为若干个字段:
标志字段

1. QR(Response):查询请求/响应的标志信息。查询请求时,值为0;响应时,值为 1。
2. Opcode:操作码。其中,0 表示标准查询;1 表示反向查询;2 表示服务器状态请求。
3. AA(Authoritative):授权应答,该字段在响应报文中有效。值为 1 时,表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。
4. TC(Truncated):表示是否被截断。值为 1 时,表示响应已超过 512 字节并已被截断,只返回前 512 个字节。
5. RD(Recursion Desired):期望递归。该字段能在一个查询中设置,并在响应中返回。该标志告诉名称服务器必须处理这个查询,这种方式被称为一个递归查询。如果该位为 0,且被请求的名称服务器没有一个授权回答,它将返回一个能解答该查询的其他名称服务器列表。这种方式被称为迭代查询。
6. RA(Recursion Available):可用递归。该字段只出现在响应报文中。当值为 1 时,表示服务器支持递归查询。
7. Z:保留字段,在所有的请求和应答报文中,它的值必须为 0。
8. rcode(Reply code):返回码字段,表示响应的差错状态。
      1. 当值为 0 时,表示没有错误;
      2. 当值为 1 时,表示报文格式错误,服务器不能理解请求的报文;
      3. 当值为 2 时,表示域名服务器失败,因为服务器的原因导致没办法处理这个请求;
      4. 当值为 3 时,表示名字错误,只有对授权域名解析服务器有意义,指出解析的域名不存在;
      5. 当值为 4 时,表示查询类型不支持,即域名服务器不支持查询类型;
      6. 当值为 5 时,表示拒绝,一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。

2. 问题部分
问题部分指的是报文格式中查询问题区域(Queries)部分。该部分是用来显示 DNS 查询请求的问题,通常只有一个问题。该部分包含正在进行的查询信息,包含查询名(被查询主机名字)、查询类型、查询类。
问题部分

1. 查询名:一般为要查询的域名,有时也会是 IP 地址,用于反向查询
2. 查询类型:DNS 查询请求的资源类型,通常查询类型为 A 类型,表示由域名获取对应的 IP 地址
3. 查询类:地址类型,通常为互联网地址,值为 1

3. 资源记录部分
资源记录部分是指DNS报文格式中的最后三个字段,包括回答问题区域字段、权威名称服务器区域字段、附加信息区域字段。
资源记录部分

1. 域名:DNS 请求的域名
2. 类型:资源记录的类型,与问题部分中的查询类型值是一样的
3. 类:地址类型,与问题部分中的查询类值是一样的
4. 生存时间:以秒为单位,表示资源记录的生命周期,一般用于当地址解析程序取出资源记录后决定保存及使用缓存数据的时间,它同时也可以表明该资源记录的稳定程度,稳定的信息会被分配一个很大的值
5. 资源数据长度:资源数据的长度
6. 资源数据:表示按查询段要求返回的相关资源记录的数据

(八)NAT协议

&emsp;&emsp;NAT 网络地址转换(Network Address Translation)属接入广域网(WAN)技术,是一种将私有(保留)地址转化为合法IP地址的转换技术,它被广泛应用于各种类型Internet接入方式和各种类型的网络中。NAT不仅完美地解决了lP地址不足的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。

&emsp;&emsp;借助于NAT,私有(保留)地址的”内部”网络通过路由器发送数据包时,私有地址被转换成合法的IP地址,一个局域网只需使用少量IP地址(甚至是1个)即可实现私有地址网络内所有计算机与Internet的通信需求。

&emsp;&emsp;NAT将自动修改IP报文的源IP地址和目的IP地址,IP地址校验则在NAT处理过程中自动完成。有些应用程序将源IP地址嵌入到IP报文的数据部分中,所以还需要同时对报文的数据部分进行修改,以匹配IP头中已经修改过的源IP地址。否则,在报文数据部分嵌入IP地址的应用程序就不能正常工作。

8.1 NAT工作过程(✿◠‿◠)

①如图这个Client的网关设定为NAT主机,所以当要连上Internet的时候,该封包就会被送到NAT主机,这个时候的封包头部的源IP为192.168.1.100。

②透过这个NAT主机,它会将Client的对外联机封包的源IP伪装成PPP0(拨号),这个接口具有公共IP。因为是公共IP了,所以这个封包就可以连上Internet,同时NAT主机并且会记忆这个联机的封包是由哪一个Client端传送来的。
③由Internet传送回来的封包,当然由NAT主机来接收,NAT主机会去查询原本记录的路由信息,并将目标IP由 PPP0上面的公共IP改回原来的192.168.1.100。
④最后则由NAT主机将该封包传送给原先发送封包的Client。

8.2 配置内部源地址静态NAT(●’◡’●)

在这里插入图片描述

(九)DHCP协议

&emsp;&emsp;DHCP动态主机配置协议(Dynamic Host Configuration Protocol)是一个局域网的网络协议,使用UDP协议工作,主要有两个用途:1.给内部网络或网络服务供应商自动分配IP地址,2.给用户或者内部网络管理员作为对所有计算机作中央管理的手段。

9.1 DHCP报文种类(●’◡’●)

  1. Discover
    DHCP客户端在请求IP地址时并不知道DHCP服务器的位置,因此DHCP客户端会在本地网络内以广播方式发送Discover请求报文,以发现网络中的DHCP服务器。
    所有收到Discover报文的DHCP服务器都会发送应答报文,DHCP客户端据此可以知道网络中存在的DHCP服务器的位置。
  2. Offer
    DHCP服务器收到Discover报文后,就会在所配置的地址池中查找一个合适的IP地址,加上相应的租约期限和其他配置信息(如网关、DNS服务器等),构造一个Offer报文,发送给DHCP客户端,告知用户本服务器可以为其提供IP地址。
    但这个报文只是告诉DHCP客户端可以提供IP地址,最终还需要客户端通过ARP来检测该IP地址是否重复。
  3. Request
    DHCP客户端可能会收到很多Offer请求报文,所以必须在这些应答中选择一个。通常是选择第一个Offer应答报文的服务器作为自己的目标服务器,并向该服务器发送一个广播的Request请求报文,通告选择的服务器,希望获得所分配的IP地址。
    另外,DHCP客户端在成功获取IP地址后,在地址使用租期达到50%时,会向DHCP服务器发送单播Request请求报文请求续延租约,如果没有收到ACK报文,在租期达到87.5%时,会再次发送广播的Request请求报文以请求续延租约。
  4. ACK
    DHCP服务器收到Request请求报文后,根据Request报文中携带的用户MAC来查找有没有相应的租约记录,如果有则发送ACK应答报文,通知用户可以使用分配的IP地址。
  5. NAK
    如果DHCP服务器收到Request请求报文后,没有发现有相应的租约记录或者由于某些原因无法正常分配IP地址,则向DHCP客户端发送NAK应答报文,通知用户无法分配合适的IP地址。
  6. Release
    当DHCP客户端不再需要使用分配IP地址时(一般出现在客户端关机、下线等状况)就会主动向DHCP服务器发送RELEASE请求报文,告知服务器用户不再需要分配IP地址,请求DHCP服务器释放对应的IP地址。
  7. Decline
    DHCP客户端收到DHCP服务器ACK应答报文后,通过地址冲突检测发现服务器分配的地址冲突或者由于其他原因导致不能使用,则会向DHCP服务器发送Decline请求报文,通知服务器所分配的IP地址不可用,以期获得新的IP地址。
  8. Inform
    DHCP客户端如果需要从DHCP服务器端获取更为详细的配置信息,则向DHCP服务器发送Inform请求报文。DHCP服务器在收到该报文后,将根据租约进行查找到相应的配置信息后,向DHCP客户端发送ACK应答报文。

9.2 DHCP报文格式(●’◡’●)

DHCP报文格式

  1. OP:报文的操作类型。分为请求报文和响应报文,1:请求报文,2:应答报文
  2. Htype:DHCP客户端的MAC地址类型。MAC地址类型其实是指明网络类型。Htype值为1时表示为最常见的以太网MAC地址类型。
  3. Hlen:DHCP客户端的MAC地址长度。以太网MAC地址长度为6个字节,即以太网时Hlen值为6。
  4. Hops:DHCP报文经过的DHCP中继的数目,默认为0。DHCP请求报文每经过一个DHCP中继,该字段就会增加1。没有经过DHCP中继时值为0。(若数据包需经过router传送,每站加1,若在同一网内,为0)
  5. Xid:客户端通过DHCP Discover报文发起一次IP地址请求时选择的随机数,相当于请求标识。用来标识一次IP地址请求过程,在一次请求中所有报文的Xid都是一样的。
  6. Secs:DHCP客户端从获取到IP地址或者续约过程开始到现在所消耗的时间,以秒为单位。在没有获得IP地址前该字段始终为0。(DHCP客户端开始DHCP请求后所经过的时间)
  7. Flags:标志位,只使用第0比特位,是广播应答标识位,用来标识DHCP服务器应答报文是采用单播还是广播发送,0表示采用单播发送方式,1表示采用广播发送方式。注意:在客户端正式分配了IP地址之前的第一次IP地址请求过程中,所有DHCP报文都是以广播方式发送的,包括客户端发送的DHCP Discover和DHCP Request报文,以及DHCP服务器发送的DHCP Offer、DHCP ACK和DHCP NAK报文。当然,如果是由DHCP中继器转的报文,则都是以单播方式发送的。另外,IP地址续约、IP地址释放的相关报文都是采用单播方式进行发送的。
  8. Ciaddr:DHCP客户端的IP地址。仅在DHCP服务器发送的ACK报文中显示,因为在得到DHCP服务器确认前,DHCP客户端是还没有分配到IP地址的。在其他报文中均显示,只有客户端是Bound、Renew、Rebinding状态,并且能响应ARP请求时,才能被填充。
  9. Yiaddr:DHCP服务器分配给客户端的IP地址。仅在DHCP服务器发送的Offer和ACK报文中显示,其他报文中显示为0。
  10. Siaddr:下一个为DHCP客户端分配IP地址等信息的DHCP服务器IP地址。仅在DHCP Offer、DHCP ACK报文中显示,其他报文中显示为0。
  11. Giaddr:DHCP客户端发出请求报文后经过的第一个DHCP中继的IP地址。如果没有经过DHCP中继,则显示为0。
  12. Chaddr:DHCP客户端的MAC地址。在每个报文中都会显示对应DHCP客户端的MAC地址。
  13. Sname:为DHCP客户端分配IP地址的DHCP服务器名称(DNS域名格式)。在Offer和ACK报文中显示发送报文的DHCP服务器名称,其他报文显示为0。
  14. File:DHCP服务器为DHCP客户端指定的启动配置文件名称及路径信息。仅在DHCP Offer报文中显示,其他报文中显示为空。
  15. Options:可选项字段,长度可变,格式为”代码+长度+数据”。

9.3 DHCP协议的工作过程(●’◡’●)

  1. 发现阶段:即DHCP客户机寻找DHCP服务器的阶段。DHCP客户机以广播的方式发送DHCP Discover 发现信息来寻找DHCP服务器(因为DHCP服务器的IP地址对客户机来说是未知的),即向255.255.255.255发送特定的广播信息,网络上每一台安装了TCP/IP协议的主机都会接收到这种广播信息,但只有DHCP服务器才会作出响应。
  2. 提供阶段:即DHCP服务器提供IP地址的阶段。在网络中收到DHCP Discover发现信息的DHCP服务器都会作出响应,它从尚未出租的IP地址中挑选一个分配给DHCP客户机,向DHCP客户机发送一个包含出租的IP地址和其他设置额DHCP Offer提供信息。
  3. 选择阶段:DHCP客户机选择某台DHCP服务器提供的IP地址的阶段。如果有多台DHCP服务器向DHCP客户机发来的DHCP Offer,客户机只接收第一个收到的DHCP Offer,然后它以广播的方式回答一个DHCP Request请求信息。该信息中包含它所选定的DHCP服务器请求IP地址的内容。之所以要以广播的方式回答,是为了通知所有的DHCP服务器,它将选择某台DHCP服务器所提供的IP地址。
  4. 确认阶段:即DHCP服务器确认所提供的IP地址的阶段。当DHCP服务器收到DHCP客户机回答的DHCP Resquest请求后,它便向DHCP客户机发送一个包含它提供的IP地址和其他设置的DHCP ACK确认信息,告诉DHCP客户机可以使用它所提供的IP地址。然后DHCP客户机便将其TCP/IP协议与网卡绑定,除了DHCP客户机所选择的服务器IP外,其他的DHCP服务器都将收回曾提供的IP地址。
  5. 重新登陆:以后DHCP客户机每次登陆网络时,就不需要再发送DHCP Discover发现信息了。而是直接发送包含前一次所分配IP地址的DHCP Resquest请求。当DHCP服务器收到这一信息后,它会尝试让客户机继续使用原来的IP并回答一个DHCP ACK确认信息,如果此IP地址无法分配个原来的DHCP客户机时(比如IP分配给其他DHCP客户机使用),则DHCP服务器给DHCP客户机回答一个DHCP NACK否认消息,当原来的DHCP客户机收到此消息后,它就必须重新发送DHCP Discover发现信息重新请求新的IP地址。
  6. 更新租约:DHCP服务器向DHCP客户机出租的IP地址一般都由一个租借期限,期满后DHCP服务器会收回出租的IP地址。如果DHCP客户机要延长其IP租约,则必须更新其租约。DHCP客户机启动时和IP租约期限过一半时,DHCP客户机都会自动向DHCP服务器发送其更新租约的信息。

(十)HTTP协议

&emsp;&emsp;超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。
超文本传输协议

10.1 HTTP的主要特点( •̀ ω •́ )

  1. 简单快速
    客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

  2. 灵活
    HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。

  3. 无连接
    无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

  4. 无状态
    HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

10.2 URL统一资源定位符( •̀ ω •́ )

HTTP使用统一资源标识符URI来传输数据和建立连接。URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息。

URL,全称是Uniform Resource Locator,中文“统一资源定位符”,是互联网上用来标识某一处资源的地址。

✈以下面这个URL为例,介绍下普通URL的各部分组成:

1
http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name

从上面的URL可以看出,一个完整的URL包括以下几部分:

  1. 协议部分:该URL的协议部分为“http:”,这代表网页使用的是HTTP协议。在Internet中可以使用多种协议,如HTTP,FTP等本例中使用的是HTTP协议。在”HTTP”后面的“//”为分隔符。
  2. 域名部分:该URL的域名部分为“www.aspxfans.com”。一个URL中,也可以使用IP地址作为域名使用。
  3. 端口部分:跟在域名后面的是端口,域名和端口之间使用“:”作为分隔符。端口不是一个URL必须的部分,如果省略端口部分,将采用默认端口。
  4. 虚拟目录部分:从域名后的第一个“/”开始到最后一个“/”为止,是虚拟目录部分。虚拟目录也不是一个URL必须的部分。本例中的虚拟目录是“/news/”
  5. 文件名部分:从域名后的最后一个“/”开始到“?”为止,是文件名部分,如果没有“?”,则是从域名后的最后一个“/”开始到“#”为止,是文件部分。如果没有“?”和“#”,那么从域名后的最后一个“/”开始到结束,都是文件名部分。本例中的文件名是“index.asp”。文件名部分也不是一个URL必须的部分,如果省略该部分,则使用默认的文件名。
  6. 锚部分:从“#”开始到最后,都是锚部分。本例中的锚部分是“name”。锚部分也不是一个URL必须的部分。
  7. 参数部分:从“?”开始到“#”为止之间的部分为参数部分,又称搜索部分、查询部分。本例中的参数部分为“boardID=5&ID=24618&page=1”。参数可以允许有多个参数,参数与参数之间用“&”作为分隔符。

10.3 HTTP请求报文(❁´◡`❁)

HTTP请求报文
一个HTTP请求报文由四个部分组成:请求行、请求头部、空行、请求数据。

  1. 请求行
    请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。请求方法比较多:GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT,最常用的是GET和POST。

    1. GET
      传递参数长度受限制,因为传递的参数是直接表示在地址栏中,而特定浏览器和服务器对url的长度是有限制的。因此,GET不适合用来传递私密数据,也不适合拿来传递大量数据。
    2. POST
      POST把传递的数据封装在HTTP请求数据中,以名称/值的形式出现,可以传输大量数据,对数据量没有限制,也不会显示在URL中。表单的提交用的是POST。
    3. HEAD
      HEAD跟GET相似,不过服务端接收到HEAD请求时只返回响应头,不发送响应内容。所以,如果只需要查看某个页面的状态时,用HEAD更高效,因为省去了传输页面内容的时间。
    4. DELETE
      删除某一个资源
    5. OPTIONS
      用于获取当前URL所支持的方法
    6. PUT
      把一个资源存放在指定的位置上,本质上来讲, PUT和POST极为相似,都是向服务器发送数据,但它们之间有一个重要区别,PUT通常指定了资源的存放位置,而POST则没有,POST的数据存放位置由服务器自己决定。
    7. TRACE
      回显服务器收到的请求,主要用于测试或诊断。
    8. CONNECT
      CONNECT方法是HTTP/1.1协议预留的,能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接与非加密的HTTP代理服务器的通信。
  2. 请求头部
    请求头部

  3. 空行
    通过一个空行,告诉服务器请求头部到此为止

  4. 请求数据

10.4 HTTP响应报文(❁´◡`❁)

HTTP响应由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

  1. 状态行,由HTTP协议版本号, 状态码, 状态消息三部分组成。

状态码:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 消息报头,用来说明客户端要使用的一些附加信息
    在这里插入图片描述

  2. 空行

  3. 响应正文,服务器返回给客户端的文本信息

10.5 HTTP工作原理(❁´◡`❁)

&emsp;&emsp;HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。
&emsp;&emsp;HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。

以下是 HTTP 请求/响应的步骤:

  1. 客户端连接到Web服务器
    一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。例如,http://www.oakcms.cn。
  2. 发送HTTP请求
    通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。
  3. 服务器接受请求并返回HTTP响应
    Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
  4. 释放连接TCP连接
    若connection模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求。
  5. 客户端浏览器解析HTML内容
    客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

例如:在浏览器地址栏键入URL,按下回车之后会经历以下流程:

  1. 浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址
  2. 解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接
  3. 浏览器发出读取文件(URL中域名后面部分对应的文件)的HTTP 请求,该请求报文作为 TCP 三次握手的第三个报文的数据发送给服务器
  4. 服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器
  5. 释放 TCP连接
  6. 浏览器将该 html 文本并显示内容

如果我们在客户端(客户端)浏览器输入 http://www.baidu.com,而baidu.com为要访问的服务器(服务器),下面详细分析客户端为了访问服务器而执行的一系列关于协议的操作:

  1. 客户端浏览器通过DNS解析到www.baidu.com的IP地址220.181.27.48,通过这个IP地址找到客户端到服务器的路径。客户端浏览器发起一个HTTP会话到220.161.27.48,然后通过TCP进行封装数据包,输入到网络层。
  2. 在客户端的传输层,把HTTP会话请求分成报文段,添加源和目的端口,如服务器使用80端口监听客户端的请求,客户端由系统随机选择一个端口,如5000,与服务器进行交换,服务器把相应的请求返回给客户端的5000端口。然后使用IP层的IP地址查找目的端。
  3. 客户端的网络层不用关系应用层或者传输层,主要做的是通过查找路由表确定如何到达服务器,期间可能经过多个路由器,这些都是由路由器来完成的工作,不作过多的描述,无非就是通过查找路由表决定通过那个路径到达服务器。
  4. 客户端的链路层,包通过链路层发送到路由器,通过邻居协议查找给定IP地址的MAC地址,然后发送ARP请求查找目的地址,如果得到回应后就可以使用ARP的请求应答交换的IP数据包。现在就可以传输了,然后发送IP数据包到达服务器的地址。

(十一)交换机与路由器

11.1 交换机的工作原理(❁´◡`❁)

&emsp;&emsp;在计算机网络系统中,交换概念的提出改进了共享工作模式。我们以前介绍过的Hub集线器就是一种共享设备,Hub本身不能识别目的地址,当同一局域网内的A主机给B主机传输数据时,数据包在以Hub为架构的网络上是以广播方式传输的,由每台终端通过验证数据包头的地址信息来确定是否接收,也就是说,在这种工作方式下,同一时刻网络上只能传输一组数据帧的通信, 如果发生碰撞还得重试。这种方式就是共享网络带宽。
&emsp;&emsp;如前所述,交换机拥有条很高带宽的背部总线和内部交换矩阵。交换机上所有的端口都挂接在这条背部总线上。控制电路收到数据包以后,处理端口会查找内存中的地址对照表以确定目的MAC(网卡的硬件地址)的NIC(网卡)挂接在哪个端口上,并通过内部交换矩阵迅速将数据包传送到目的端口,目的MAC若不存在,则广播到所有的端口,接收端口回应后交换机会“学习”到新的地址,并把它添加到内部MAC地址表中。
&emsp;&emsp;使用交换机也可以把网络“分段”,通过对照MAC地址表,交换机只允许必要的网络流量通过交换机。通过交换机的过滤和转发,可以有效地隔离广播风暴,减少误包和错包的出现,避免共享冲突。

&emsp;&emsp;交换机的主要功能有两部分,建立虚连接和转发信息
当两个站点间有信息包要传输时,在这两站点间临时建立条点对点的连接,信息包发送完后,就立即拆除这个连接。在交换机内部保存了一张地址表,用来存储它所连接的各站点所在的交换机端口号和MAC地址之间的对应关系。交换机从一个端口收到信息包,识别出包中的目的站MAC地址,查地址表, 得到目的站所在的端口,就可在两个端口间建立一条虚连接,再将信息包从源端口转发到目的端口,包发送完后, 这条虚连接就不存在了。
&emsp;&emsp;总之,交换机是一种基于MAC地址识别,能完成封装转发数据包功能的网络设备。交换机可以学习MAC地址,并将其存放在内部地址表中,通过在数据帧的始发者和目标接收者之间建立临时的交换路径,使数据帧直接由源地址到达目的地址。

11.2 生成树协议(✿◕‿◕✿)

11.2.1 冗余和交换环路

&emsp;&emsp;为了保证企业网络中关键应用的可持续性,在进行网络拓扑结构设计和规划时,冗余常常是我们考虑的重要因素之一。 在如今的网络工程设计中,冗余设计是考验一个网络是否稳定的关键环节,链路冗余使网络具有了容错功能,具有冗余性的设计对于网络的可靠性是极其重要的。

  1. 产生广播风暴
    广播风暴也叫网络广播风暴,即一个数据包或帧被传送到本地网段(由广播域定义)上的每个节点就是广播,网络上的广播帧由于被转发,数量急剧增加而出现正常网络通信的反常现象。
    广播风暴会占用相当可观的网络带宽,导致正常数据包无法正常运行。当广播数据充斥网络无法处理并占用大量网络带宽,导致正常业务不能运行,这就发生了广播风暴,造成局域网局部或整个网络瘫痪。
    冗余和交换环路
    如图所示就是一个存在环路的局域网络,假设B1、B2和B3都还没有学习到A的MAC地址,因为A还没有发送过任何数据。当A发送了一个数据帧,最初三个交换机都接收了这个数据帧,记录A的地址在LAN1上,并排队等待将这个数据帧转发到LAN2上。其中一个交换机首先成功地将数据帧发送到LAN2上,假设这个网桥是B1,那么B2和B3将再次收到这个数据帧,因为BI对B2和B3来说是透明的。这个数据帧就好像是A在LAN2上发送的一样。于是B2和B3记录A在LAN2上,排队等待将这个新接收到的数据帧转发到LAN1上,假设这时B2成功地将最初的数据帧转发到LAN2上,那么B1和B3都接收到这个数据帧,并排队等待转发这个数据帧到LAN1上。 如此反复,数据帧就在环路中不断循环,更糟糕的是每次成功的转发都会导致网络中出现两个新的数据帧,从而形成严重的广播风暴。

  2. 出现帧的复制现象
    广播风暴不断旋转时,同一个广播帧被反复在网段上传递,交换机就要拿出更多的时间处理这个不断被复制的广播帧,从而使整个网络的性能急剧下降,甚至瘫痪,而主机也正忙于处理这些相同的广播帧,因为它们在不断地被发送到主机的网络接口卡上,这就影响了主机的正常工作,在严重时甚至会使主机死机。

  3. MAC地址表不稳定
    交换机之所以比集线器(Hub)速率快,就是因为交换机的内存里有一个MAC地址表和端口映射的缓存——MAC地址表, 但是在发生广播风暴的时候,由于广播风暴是在两个相反的方向上旋转的,所以会影响到MAC地址表的正常工作。
    如果网络不存在物理上的环路,就不会有上述问题存在,然而当网络结构复杂时,要保证网络中没有任何环路是很困难的,并且在许多对可靠性要求高的网络中,为了保证不向断的网络服务,需要采用物理环路的冗余备份,因此,保证网络不存在环路是不现实的。

IEEE提出了个很好的解决办法,
那就是802.1D协议标准规定的STP(生成树协议的英文缩写),该协议可应用于环路网络,通过一定的算法实现路径冗余,同时将环路网络修剪成无环路的树形网络,从而避免报文在环路网络中的增生和无限循环。

11.2.2 STP 生成树协议简介

&emsp;&emsp;STP (Spanning Tree Protocol,生成树协议)就是用来解决网络的环路。STP通过一定的算法,使得任意两个节点间有且只有一条路径连接, 而其他的冗余链路则被自动阻塞, 作为备份链路。只有当活动链路失败时,备份链路才会被激活,从而恢复设备之间的连接,保证网络的畅通。
&emsp;&emsp;STP操作对于终端来说是透明的,而不管终端连在LAN的某一部分还是多个部分。当创建网络时,网络中所有节点存在多条路径。生成树中的算法计算出最佳路径,因为每个VLAN是一个逻辑LAN部分,所以网管员能使STP一次工作在最多64个VLAN中。如果要配置超过64个VLAN,网管员需要将其他VLAN的STP禁止,因为默认的STP只能支持1–64个VLAN。

&emsp;&emsp;运行生成树算法的网桥/交换机在规定的问隔(默认2秒)内通过网桥协议数据单元(BPDU)的组播帧与其他交换机交换配置信息,其工作的过程如下:

  1. 通过比较交换机优先级选取根交换机 (给定广播域内只有一个根交换机)
  2. 其余的非根交换机只有一一个通向根交换机的端口称为根端口
  3. 每个网段只有一个转发端口
  4. 根交换机所有的连接端口均为转发端口
  5. 注:生成树协议在交换机上一般是默认开启的,不经人工干预即可正常工作,但这种自动生成的方案可能导致数据传输的路径并非最优化。因此,可以通过人工设置交换机优先级的方法影响生成树的生成结果。

启用生成树协议的交换机,其接口处于以下几种状态:

  1. Blocking(阻塞):不参与帧的转发
  2. Listening(侦听):当确定该接口将参与帧转发时,在阻寒状态后的第一个过度状态
  3. Learning(学习):准备参与帧转发
  4. Forwarding(转发):转发帧
  5. Disabled(禁用):端口处于Shutdown状态、没有连接,或者没有启用Spanning–Tree,从而不参与Spanning-Tree

&emsp;&emsp;STP中定义了根桥(Root Bridge)、根端口(Root Port)、指定端口(Designated Port)、路径开销(Path Cost)等,目的就在于通过构造一棵自然树的方法达到裁剪冗余环路的目的,同时实现链路备份和路径最优化

&emsp;&emsp;生成树算法会特意阻塞可能导致环路的冗余路径,以确保网络中所有目的地之间只有一条逻辑路径,但要实现这些功能,网桥之间必须要进行一些信息的交流,这些信息交流单元就称为网桥协议数据单元BPDU帧。

  • BPDU是一种二层报文,目的MAC地址是多播地址:01-80-C2-00-00-00,所有支持STP的网桥都会接收并处理收到的BPDU报文,该报文的数据区里携带了用于生成树计算的所有有用信息。
  • 阻塞的路径不再接收和转发用户数据包,但是会接收用来防止环路的网桥协议数据单元BPDU帧。如果网络拓扑发生变化,需要启用阻塞路径来抵消网络电缆或交换机故障的影响,STP就会重新计算路径,将必要的端口解除阻塞,使冗余路径进入活动状态。
1
2
3
4
5
每个BPDU都包含4个重要字段:
1.根桥的BID
2.转发根桥BPDU 的网桥的BID
3.到达根桥的开销
4.转发根桥BPDU 的网桥的端口ID
1
2
3
4
5
6
7
8
1.根端口:最靠近根桥的交换机端口
2.指定端口:网络中获准转发流量的、除根端口之外的所有端口
3.非指定端口:为防止环路而被置于阻塞状态的所有端口
4.禁用端口:是处于管理性关闭状态的交换机端口
5.端口开销:端口开销值与给定路径上的每个交换机端口的端口速度相关联
6.路径开销:路径开销是到根桥的路径上所有端口开销的总和
7.到根桥的最佳路径:交换机到根桥的所有路径中最短的一条路径
8.BPDU(网桥协议数据单元):是运行STP交换机之间交换的消息帧

交换机完成启动后,生成树便立即确定。如果交换机端口直接从阻塞转换到转发状态,但交换机此时并不了解所有拓扑信息时,该端口可能会暂时造成数据环路。为此,STP引入了5种端口状态:
5种端口状态
端口处于各种端口状态的时间长短取决于BPDU计时器,BPDU计时器状态:

  1. Hello时间
    Hello时间是端口发送BPDU帧的间隔时间,此值默认为2s,不过可调整为1~10s之间的值
  2. 转发延迟
    转发延迟是处于侦听和学习状态的时间,默认情况下,每转换一个状态要等待15s,不过此时间可调整为4~30s之间的值
  3. 最大老化时间
    最大老化时间计时器控制着交换机端口保存配置BPDU信息的最长时间,此值默认为20s,不过可调整为6~40s之间的值
11.2.3 STP端口状态转换过程

STP端口状态的转换过程有以下5种:

  1. 如果一个被阻塞的接口(非指定端口)在收到一个BPDU后,20s的时间内没有收到BPDU,则开始进入侦听状态。
  2. 交换机端口初始化后直接进入侦听状态。
  3. 在侦听状态中,交换机通过相互间的BPDU交换选出根桥、根端口、指定端口。在一个转发延迟(默认15s)之后进入下个状态。如果端口类型是以上三种之一则进入侦听状态,否则进入阻塞状态。
  4. 在侦听状态中,BPDU 交换就绪。开始学习新的MAC 地址。在一个转发延迟之后(默认15s)进入下个状态。
  5. 在转发状态中,端口可以接收和发送BPDU,可以接收和发送数据帧。端口现在就是生成树拓扑中的一个具有全部功能的交换机端口。
  6. 如下图展示:
    STP端口状态转换过程
11.2.4 STP收敛的步骤
  1. 选举根桥
    ●根桥的选举在交换机完成启动时或网络中检测到路径故障时触发。一开始,所有交换机端口都配置为阻塞状态,此状态默认情况下会持续20s。
    ●由于生成树技术允许网络的端与端之间最多有7台交换机,因此整个根桥的选举过程能够在14s内完成,此时间短于交换机端口处于阻塞状态的时间。
    ●交换BPDU,每2s发送一次,选取BID最小的网桥为根桥。

  2. 选举根端口
    ●除根桥外的其余每台交换机都需具有一个根端口(到根桥的路径开销最低的端口)。
    ●如果当同一交换机上有两个以上的端口到根桥的路径开销相同时,做出如下选择:

    1. 到根路径的上一级网桥BID不同时:选择其BID小的路径。
    2. 到根路径的上一级网桥BID相同时:选择其BPDU中的端口ID小的路径。
    3. 当交换机从具有等价路径的多个端口中选择一个作为根端口时,落选的端口会被配置为非指定端口(阻塞状态)以避免环路。
  3. 选举指定端口和非指定端口
    ●交换网络中的每个网段只能有一个指定端口。
    ●当两台交换机交换BPDU帧时,它们会检查收到的BPDU帧内的发送方BID,BID较小的交换机会将其端口配置为指定角色,BID较大的交换机将其交换机端口配置为非指定角色(阻塞状态)。

11.2.5 STP生成树协议的缺陷

&emsp;&emsp;STP给透明网桥带来了新生。但是,随着应用的深入和网络技术的发展,它的缺点在应用中也被暴露了出来,STP的缺陷主要表现在收敛速度上:当拓扑发生变化,新的配置消息要经过一定的时延才能传播到整个网络,这个时延称为转发延迟,协议默认值是15s。在所有网桥收到这个变化的消息之前,若旧拓扑结构中处于转发的端口还没有发现自己应该在新的拓扑中停止转发,则可能存在临时环路。

&emsp;&emsp;为了解决临时环路的问题,生成树使用了一种定时器策略,即在端口从阻塞状态到转发状态中间加上一个只学习MAC地址但不参与转发的中间状态,两次状态切换的时间长度都是转发延迟,这样就可以保证在拓扑变化的时候不会产生临时环路。但是,这个看似良好的解决方案实际上带来的却是至少两倍转发延迟的收敛时间。

11.2.6 RSTP快速生成树协议

&emsp;&emsp;RSTP在STP基础上做了三点重要改进,使得收敛速度快得多(最快1s以内)。

  1. 第一点改进:为根端口和指定端口设置了快速切换用的替换端口(Alternate Port)和备份端口(Backup Port)两种角色,当根端口/指定端口失效的情况下,替换端口/备份端口就会无时延地进入转发状态。
  2. 第二点改进:在只连接了两个交换端口的点对点链路中,指定端口只需与下游网桥进行一次握手就可以无时延地进人转发状态。如果是连接了三个以上网桥的共享链路,下游网桥是不会响应上游指定端口发出的握手请求的,只能等待两倍转发延迟时间进入转发状态。
  3. 第三点改进:直接与终端相连而不是把其他网桥相连的端口定义为边缘端口(Edge Port)。边缘端口可以直接进入转发状态,不需要任何延时,但由于网桥无法知道端口是否是直接与终端相连,所以需要人工配置。

11.3 路由器工作原理(✿◠‿◠)

&emsp;&emsp;路由器(Router)用于连接多个逻辑上分开的网络。所谓逻辑网络是代表一个单独的网络或者一个子网。当数据从一个子网传输到另个子网时,可通过路由器来完成,因此,路由器具有判断网络地址和选择路径的功能,它能在多网络互联环境中,建立灵活的连接,可用完全不同的数据分组和介质访问方法连接各种子网。路由器只接受源站或其他路由器的信息,属于网络层的一种互联设备, 它不关心各子网使用的硬件设备,但要求运行与网络层协议相一致的软件。一般说来,异种网络互联与多个子网互联都应采用路由器来完成。
&emsp;&emsp;路由器的主要工作就是为经过路由器的每个数据帧寻找一条最佳传输路径,并将该数据有效地传送到目的站点。由此可见,选择最佳路径的策略即路由算法是路由器的关键所在。为了完成这项工作,在路由器中保存着各种传输路径的相关数据路由表 (Routing Table),供路由选择时使用。路由表中保存着子网的标志信息、网上路由器的个数和下一个路由器的名字等内容。路由表可以是由系统管理员固定设置好的、也可以由系统动态修改,可以由路由器自动调整、也可以由主机控制。
&emsp;&emsp;当IP子网中的一台主机发送IP分组给同一IP子网的另一台主机时,它直接把IP分组送到网络上对方就能收到,而要送给不同IP子网上的主机时,它要选择一个使到达目的子网上的路由器,把IP分组送给该路由器,由路由器负责把IP分组送到目的地。如果没有找到这样的路由器,主机就把IP分组送给一个称为默认网关(default gateway)的路由器上。默认网关是每台主机上的一个配置参数,它是接在同一个网络上的某个路由器端口的IP地址。
&emsp;&emsp;路由器转发IP分组时,只根据IP分组目的IP地址的网络号部分,选择合适的端口,把IP分组送出去。同主机一样,路由器也要判定端口所接的是否是目的子网。如果是,就直接把分组通过端口送到网络上,否则就要选择下一个路由器来传送分组。路由器也有它的默认网关,用来传送不知道往哪儿送的IP分组。这样,通过路由器把知道如何传送的IP分组正确转发出去,不知道的IP分组送给默认网关路由器,这样一级一级地传送,IP分组最终将送到目的地,送不到目的地的IP分组则被网络丢弃了。

路由动作包括两项基本内容:寻行和转发

  1. 寻径即判定到达目的地的最佳路径,由路由选择算法来实现。
  2. 由于涉及不同的路由选择协议和路由选择算法,要相对复杂一些。 为了判定最佳路径,路由选择算法必须启动并维护包含路由信息的路由表,其中路由信息依赖于所选的路由选择算法。路由选择算法将收集到的不同信息填入路由表中,根据路由表可将目的网络与下一站的关系告诉路由器。路由器间互通信息进行路由更新,更新维护路由表使之正确反映网络的拓扑变化,并由路由器根据量度来决定最佳路径。这就是路由选择协议,例如路由信息协议(RIP)、开放式最短路径优先协议(OSPF)和边界网关协议(BGP) 等。
  3. 转发即沿寻径好的最佳路径传送信息分组。
  4. 路由器首先在路由表中查找,判明是否知道如何将分组发送到下一个站点 (路由器或主机),如果路由器不知道如何发送分组, 通常将该分组丢弃,否则就根据路由表的相应表项将分组发送到下一个站点:如果目的网络直接与路由器相连,路由器就把分组直接送到相应的端口上。这就是路由转发协议(Routed Protocol)。

路由转发协议和路由选择协议是相互配合又相互独立的概念, 前者使用后者维护的路由表,同时后者要利用前者提供的功能来发布路由协议数据分组。

典型的路由选择方式有两种:静态路由和动态路由

11.3.1 静态路由

&emsp;&emsp;静态路由是在路由器中设置的固定的路由表。除非网络管理员干预,否则静态路由不会发生变化。由于静态路由不能对网络的改变做出反映,因而一般用于网络规模不大、拓扑结构固定的网络中。

11.3.2 静态路由与动态路由

&emsp;&emsp;静态路由的优点是简单、高效、可靠,在所有的路由中,静态路由的优先级最高。当动态路由与政由发生冲突时,以静态路由为准。
&emsp;&emsp;动态路由是网络中的路由器之间相互通信、传递路由信息、利用收到的路由信息更新路由器表的过程,它能实时地适应网络结构的变化。如果路由更新信息表明发生了网络变化路由这择软件就会重新计算路由,并发出新的路由更新信息,这些信息通过各个网络,引起各路由器重新启动其路由算法并更新各自的路由表以动态地反映网络网络拓扑变化。
&emsp;&emsp;动态路由适用于网络规模大、网络拓扑复杂的网络,当然,各种动态路由协议会不同程度地占用网络带宽和CPU资源静志路由和动态路由有各自的特点和适用范围,因此在网络中动态路由通常作为静态路由的补充。
&emsp;&emsp;当一个分组在路由器中进行寻径时,路由器首先查找静态路由,如果查到则根据相应的静态路由转发分组,否则即再查找动态路由。

11.3.3 RIP路由协议
  1. RIP路由协议
    RIP协议是由施乐在20世纪70年代设计开发的,是Internet中常用的路由协议。RIP采用距离向量算法,即路由器根据距离选择路由,所以也称为距离向量协议。路由器收集所有可到达目的地的不同路径,并且保存有关到达每个目的地的最少站点数的路经信息,除了到达目的地的最佳路径之外,任何其他信息均予以丢弃,同时路由器也把所收集的路由信息用RIP协议通知相邻的其他路由器,这样,正确的路由信息就逐渐扩散到了全网。

  2. RIP使用非常广泛,它简单、可靠、便于配置,但是RIP只适用于小型的同构网络,因为它允许的最大站点数为15,任何超过15个站点的目的地均被标记为不可达,而且RIP每隔30s一次的路由信息广播也是造成网络的广播风暴的重要原因之一。

11.3.4 RIPv1 和 RIPv2 的主要区别
  1. RIPv1是有类路由协议,RIPv2是无类路由协议
  2. RIPv1不能支持VLSM,RIPv2 可以支持VLSM
  3. RIPv1没有认证的功能,RIPv2可以支持认证,并且有明文和MD5两种认证
  4. RIPv1没有手工汇总的功能,RIPv2 可以在关闭自动汇总的前提下,进行手工汇总
  5. RIPv1是广播更新(255.255. 255. 255),RIPv2是组播更新(224. 0.0.9)
  6. RIPv1对路由没有标记的功能,RIPv2可以对路由打标记(tag),用于过滤和做策略
  7. RIPv1发送的更新最多可以携带25条路由条目,RIPv2在有认证的情况下最多只能携带24条路由

RIPv1特点:
(1)有类别路由协议,不支持VLSM和CIDR
(2)广播更新路由信息
(3)不支持认证

  • RIPv1的报文:包括 Request 和 Response ,Request 报文在RIP路由器接入网络时发送,用于请求对端的所有RIP路由信息,Response 报文周期30s发送,携带本路由器所有的有效的RIP路由。
  • 在支持触发更新时也会发送 Response 报文更新或删除路由。
  • RIPv1的报文最大有512byte = 8byte(UDP) + 4byte(RIP header) + 20byte(一条RIP路由) * 25,所以一个RIPv1报文最大只能携带25条路由。

RIPv2特点:
(1)无类别路由协议,在更新路由时可以发送路由的掩码,支持VLSM和CIDR
(2)支持组播和广播更新,组播地址为224.0.0.9
(3)支持明文和MD5密文认证
(4)路由信息更加丰富,支持携带掩码和下一跳,支持携带路由标记

  • RIPv2的报文:组播224.0.0.9发送,使用组播更安全,效率更高,只有运行RIPv2的路由器接受并处理。包括 Request 和 Response,Request 报文在RIP路由器接入网络时发送,用于请求对端的所有RIP路由信息,Response 报文周期30s发送,携带本路由器所有的有效的RIP路由。
  • 在支持触发更新时也会发送 Response 报文更新或删除路由。
  • RIPv1的报文最大有512byte = 8byte(UDP) + 4byte(RIP header) + 20byte(一条RIP路由) * 25,所以一个RIPv1报文最大只能携带25条路由。
  • RIPv2将认证字段放在了一条路由中,使用第一条路由携带认证字段,所以如果开启了认证,则一个RIPv2报文最大只能携带24条路由。
11.3.5 OSPF路由协议
  1. OSPF路由协议
    20世纪80年代中期,RIP已不能适应大规模异构网络的互联,OSPF随之产生,它是网间工程任务组织(IETF) 的内部网关协议工作组为IP网络而开发的一种路由协议。
  2. OSPF是一种基于链路状态的路由协议,需要每个路由器向其同一管理域的所有其他路由器发送链路状态广播信息。在OSPF的链路状态广播中包括所有接口信息、所有的量度和其他一些变量。
  3. 利用OSPF的路由器必须首先收集有关的链路状态信息,并根据一定的算法计算出到每个节点的最短路径,而基于距离矢量的路由协议仅向其邻接路由器发送有关路由更新信息。
11.3.6 路由器的功能
  1. 在网络间截获发送到远地网段的报文,起转发的作用。
  2. 选择最合理的路由,引导通信。
    为了实现这功能, 路由器要按照某种路由通信协议,查找路由表。路由表中列出整个互联网络中包含的各个节点,以及节点间的路径情况和与它们相联系的传输费用。如果到特定的节点有一条以上的路径,则基于预先确定的准则选择最优(最经济)的路径。由于各种网络段和其相互连接情况可能发生变化,因此路由情况的信息需要及时更新,这是由所使用的路由信息协议规定的定时更新或者按变化情况更新来完成的。网络中的每个路由器均按照这一规则动态地更新它所保持的路由表,以便保持有效的路由信息。
  3. 路由器在转发报文的过程中,为了便于在网络间传送报文,应按照预定的规则把大的数据包分解成适当大小的数据包,到达目的地后再把分解的数据包包装成原有形式。
  4. 多协议的路由器可以连接使用不同通信协议的网络段,作为不同通信协议网络段通信连接的平台。
  5. 路由器的主要任务是把通信引导到目的地网络,然后到达特定的节点站地址。后一个功能是通过网络地址分解完成的。
11.3.7 RIP协议详解
  1. RIP的主要特征

    1. RIP(Routing Information Protocol,路由信息协议),是早期应用比较普遍的内部网关协议,是典型的距离矢量路由协议,适用于小型网络,最大的缺点是无法在具有冗余链路的网络中有效的运用。
    2. RIP协议的默认管理距离是120,RIP所接收的路由信息都被封装在UDP协议的数据报中,在UDP的520端口接收来自远程路由的信息。
      RIP使用跳数作为路径选择的度量值。最大跳数是15,如果最大跳数大于15,则认为该网络失效。
    3. RIPv1采用广播式更新,RIPv2采用组播更新方式,RIP默认每隔30秒周期性的发送整个路由表给邻路由。
  2. RIP路由互相学习过程解析
    RIP协议运行前R1、R2、R3的路由表中只有直连路由的信息

    1. 运行RIP路由协议后,R1、R2、R3宣告各自直连网络。
    2. 假设R1先发送路由更新,R1将自己直连网络10.1.0.0和10.2.0.0以1跳的度量值告诉R2。
    3. R2收到R1的路由表后,将自己的路由与R1传过来的路由进行比较,R2发现自己的路由表中没有10.1.0.0,R2记下这条路由以及路由对应的接口和跳数1,并且R2发现自己的路由表中已经有10.2.0.0这个条目,而且是直连条目,直连路由的管理距离是0,学到的RIP路由的管理距离是120,所以R2忽略R1传过来的10.2.0.0这个条目。
    4. R2把自己路由表中的直连网络10.2.0.0和10.3.0.0以1跳的度量值告诉R3,并且将从R1那里学到的10.1.0.0网络以2跳的度量值告诉R3。
    5. R3收到R2发过来的路由条目,将自己的路由表和R2发过来的条目进行比较,R3发现自己路由表中没有10.1.0.0,R3记录下这条路由以及对应端口和跳数2;R3发现自己路由表中没有10.2.0.0,R3记录下这条路由和对应端口以及跳数1;R3发现自己的路由表中已经存在10.3.0.0,并且是直连,比R2发过来的RIP更新有更好的度量值,R3忽略R2发来的10.3.0.0。这样R3学到了完整的路由条目。
    6. 同理,R3也会将路由发给R2,R2再发给R1,最后所有路由都可以学到所有条目。
      在这里插入图片描述
11.3.8 RIP路由环路形成解析

路由环路

  1. R1将网络1的路由信息发给R2,R2学到了网络1并将度量值标记为1跳,即经过一台路由可以到达,下一跳是R1。
  2. 路由R2将网络1的路由信息发给R3和R5,R3和R5都学到了网络1,并将度量值标记为2跳,即经过两台路由可以到达,下一跳是路由R2;R3和R5都将网络1发给R4,R4也学到网络1,并将度量值标记为3跳,下一跳路由是R3或R5,即从两台路由都能到达,R4去往网络1的数据将负载均衡。此时所有的路由都拥有一致的认识和正确的路由表,这时网络被称作已收敛。
  3. 此时,当网络1断开,R1将网络1不可达的信息发送给R2,R2将网络1不可达的信息发送给R3和R5,R4还不知道网络1不可达的信息,就在这个时候R4发送了一个更新给R5,认为通过R3可以到达网络1;当然也可能是发送给R3,告诉它通过R5可以到达网络1。
  4. 这里假设是第一种情况,R5收到通过R4可以到达网络1的更新后,更新自己的路由表,并将网络1可达的信息发送给了R2,R2更新自己的路由表并发送给R3和R1。R3更新自己的路由表并发送给R4,至此,路由环路形成。
11.3.9 距离矢量路由环路的解决方法

距离矢量路由环路的解决方法有如下五种:

  1. 最大跳计数
    RIP允许跳计数最大可以达到15,任何需要经过16跳才能到达的网络都被认为是不可达的。
  2. 水平分隔
    限制路由器不能按照接收信息的方向去将接收到的信息再发回去。比如路由R3和R5有关网络1的信息是从R2学习到的,它们不会将网络1的信息再从与R2相连的接口发回去,这样R4最终会学习到网络1不可达的信息。
  3. 路由毒化
    路由中毒通过将故障网络设置成最大跳计数加1来暗示网络不可达,毒性反转是避免环路的另一种方法,比如R2学习到R1发送过来的网络1不可达的信息,首先它将网络1的跳计数更改成16跳,并且根据毒性反转,它将向R1送回一条网络1不可达的更新。
  4. 触发更新
    周期性发送更新,RIP是默认每隔30秒。
  5. 抑制定时器
    比如R2收到了R1发来的网络1不可达的信息后,R2首先标记此网络不可达,同时R2的抑制定时器启动,在RIP中抑制定时器是180秒倒计时,如果在抑制定时器到期前,又从R1收到网络1可达的信息了,那么删除这个抑制定时器,并且标记网络1可达。
    如果在抑制定时器期满前,收到一个来自其他路由的(R3或者R4)关于网络1的更新,并且这个更新具有更好的度量值(假设以前学到的是3跳,这里有其他路由告诉它只需要1跳就能到达网络1),那么R2删除抑制定时器,并且标记网络1可达。
    如果在抑制定时器到期前,R2收到另外路由器发来的关于网络1的更新,并且具有更差的度量值,那么忽略此更新;在抑制定时器期满后,R2删除抑制定时器,接收来自任何源路由的关于网络1的更新。
11.3.10 OSPF协议详解
  1. OSPF(Open Shortest Path First 开放式最短路径优先)是一个内部网关协议(简称IGP),用于在单一自治系统内决策路由。
  2. OSPF的简单说就是两个相邻的路由器通过发报文的形式成为邻居关系,邻居再相互发送链路状态信息形成邻接关系,之后各自根据最短路径算法算出路由,放在OSPF路由表,OSPF路由与其他路由比较后,优的加入全局路由表。
  3. OSPF协议的优点
    1. 开销减少。OSPF减去了通过广播进行路由更新的网络开销。OSPF只在检测到拓扑变化时发送路由更新信息,而不是定时发送整个路由表,只在有必要进行更新时给路由表发送改变路由的信息,而不是整个路由表。
    2. 支持VLSM和CIDR。OSPF路由更新中包括子网掩码。
    3. 支持不连续网络。
    4. 支持手工路由汇总。
    5. 收敛时间短。在一个设计合理的OSPF网络中,一条链路发生故障之后可以很快达到收敛, 因为OSPF具有一个包含OSPF域中所有路径的完整的拓扑数据库。
    6. 无环拓扑生产。
    7. 跳数只受路由器资源使用和IP TTL的限制。
      在这里插入图片描述
1
OSPF协议使用最短路径优先算法SPF来计算生成路由表,其参考值为链路成本(Cost)

链路成本的定义是基于链路带宽的,带宽越高则链路成本越低,带宽越低则链路成本越高。

11.3.11 OSPF区域

&emsp;&emsp;OSPF引入分层路由的概念,将网络分割成一个“主干”连接的一组相互独立的部分,这些相互独立的部分被称为“区域”,主干的部分称为“主干区域”。多区域的OSPF必须存在一个主干区域,主干区域负责收集非主干区域发出的汇总路由信息,并将这些信息返回给到各区域。

  • 分区域的好处:
    (1)减少路由更新
    (2)加速收敛
    (3)限制不稳定到一个区域
    (4)提高网络性能
11.3.12 OSPF路由器的类型
  1. 区域内路由器:所有端口都在同一区域的路由器,它们都维护着一个相同的链路状态数据库。
  2. 区域边界路由器:具有连接多区域端口的路由器,一般作为一个区域的出口。
  3. 主干路由器:至少有一个连接主干区域端口的路由器。
  4. 自治系统边界路由器:至少拥有一个连接外部自治域网络端口的路由器,负责将非OSPF网络信息传入OSPF网络。

OSPF路由器
指定路由器
指定路由器

11.3.13 OSPF路由计算

OSPF路由计算
OSPF路由计算

11.3.14 OSPF数据包类型
  1. 问候(Hello) 数据包:周期性发送,用来发现和维持OSPF邻居关系以及指定路由器或备份路由器的选举。
  2. 数据库描述(Database Description, DD)数据包:描述了本地LSDB中每一条LSA的摘要信息,用于两台路由器进行数据库同步。
  3. 链路状态请求(Link State Request, LSR) 数据包:向对方请求所需的LSA,内容包括所需要的LSA的摘要。
  4. 链路状态更新(Link State Update, LSU)数据包:向对方发送其所需要的LSA。
  5. 链路状态确认(Link State Acknowledgment, LSAck) 数据包:用来对收到的LSA进行确认。
  • OSPF首部
    在这里插入图片描述
  • 问候(Hello) 数据包格式
    在这里插入图片描述
  • 链路状态请求(Link State Request, LSR) 数据包格式
    在这里插入图片描述
  • 链路状态更新(Link State Update, LSU)数据包格式
    在这里插入图片描述
  • 链路状态确认(Link State Acknowledgment, LSAck) 数据包格式
    在这里插入图片描述
  • 链路状态通告 LSA
    在这里插入图片描述
  • LSA首部格式:
    在这里插入图片描述
11.3.15 OSPF工作原理
1. 建立路由器的邻居关系
2. 进行必要的DR/BDR选举
3. 链路状态数据库的同步
4. 产生路由表
5. 维护路由信息

OSPF工作原理

11.3.16 OSPF的邻居状态机

在这里插入图片描述
在这里插入图片描述

11.3.17 OSPF工作原理展开
  1. OSPF协议通过Hello协议建立路由器的邻居关系
    每个Hello数据包都包含以下信息:
    在这里插入图片描述
  • 邻居关系的建立要经过三个状态
    在这里插入图片描述
  1. 选举DR和BDR

    1. 当路由器同一个或多个路由器建立双向的通信后,检查每个邻居Hello包里的优先级、DR和BDR域。列出所有符合DR和BDR选举的路由器(优先级大于0,为0时不参加选举),列出所有的DR和BDR。
    2. 从这些合格的路由器中建立一个没有宜称自己为DR的子集(因为宣称为DR的路由器不能选举成为BDR)。
    3. 如果在这个子集里有一个或多个邻居(包括它自己的接口)在BDR域宣称自己为BDR,则选举具有最高优先级的路由器,如果优先级相同,则选择具有最高Router ID的那个路由器为BDR。
    4. 如果在这个子集里没有路由器宣称自己为BDR,则在它的邻居里选择具有最高优先级的路由器为BDR,如果优先级相同,则选择具有最大Router ID的路由器为BDR。
    5. 在宣称自己为DR的路由器列表中,如果有一个或多个路由器宣称自己为DR,则选择具有最高优先级的路由器为DR,如果优先级相同,则选择具有最大Router ID的路由器为DR。
    6. 如果没有路由器宣称为DR,则将最新选举的BDR作为DR。
    7. 如果是第一次选举某个路由器为DR/ BDR或没有DR/BDR被选举,则要重复2到6步,然后是第8步。
    8. 将选举出来的路由器的端口状态作相应的改变,DR的端口状态为DR, BDR的端口状态为BDR,否则的话为DR Other。
    9. 在点到点的网络中,不选举DR和BDR,两台路由器之间建立主从关系,路由器ID高的作为主路由器,另一台作为从路由器,进入准启动Exstart状态。
  2. 链路状态数据库的同步
    在这里插入图片描述
    在这里插入图片描述

  3. 路由表的产生
    在这里插入图片描述

  4. 维护路由信息
    在这里插入图片描述

  5. 总结:

    1. 每台路由器学习激活的直接相连的网络。
    2. 每台路由器和直接相连的路由器互交,发送Hello报文,建立邻居关系。
    3. 每台路由器构建包含直接相连的链路状态的LSA(链路状态通告)链路状态通告中记录了所有相关的路由器,包括邻路由器的标识、链路类型、带宽等。
    4. 每台路由器泛洪链路状态通告(LSA)给所有的邻路由器,并且自己也在本地储存邻路由发过来的LSA,然后再将收到的LSA泛洪给自己的所有邻居,直到在同一区域中的所有路由器收到了所有的LSA。每台路由器在本地数据库中保存所有收到的LSA副本,这个数据库被称作链路状态数据库(LSDB)。
    5. 每台路由器基于本地的链路状态数据库执行最短路径优先(SPF)算法,并以本路由器为根,生成一个SPF树,基于这个SPF树计算去往每个网络的最短路径,也就得到了最终的路由表。
  • 点到点网络
    点到点网络是连接单独的一对路由器的网络,点到点网络上的有效邻居总是可以形成邻接关系的,在这种网络上,OSPF包的目标地址使用的是224.0.0.5。
  • 广播型网络
    广播型网络上会选举一个DR和BDR,DR/BDR发送的OSPF包的目标地址为224.0.0.5,而除了DR/BDR以外的OSPF包的目标地址为224.0.0.6。
  • 在广播型网络中,所有路由器都以224.0.0.5的地址发送Hello包,用来维持邻居关系,DR/BDR会使用224.0.0.5的地址来发送LSA更新,非DR/BDR路由都以224.0.0.6的地址发送LSA更新。
  • OSPF区域内当有链路发生变化时,如变化路由器为DR Other时,它不会将LSU泛洪给所有区域内其它路由器,而是通过组播地址224.0.0.6先发送给DR/BDR,然后DR/BDR再将此更新通过224.0.0.5发送给其它区域内路由器。换句话说,DR/BDR监听224.0.0.6,而DR Other监听224.0.0.5。

越姐护体


(●’◡’●)****🤍❀★内容持续更新中(●’◡’●)****🤍❀★

小萌新打算写一篇关于C语言中指针方面的学习心得,因为自己是一位在校大学生,专业课也涉及到C语言,在学习C语言指针的时候,相信也和大家一样,在这块知识面感觉晦涩难懂,学不好。但是小萌新并没有放弃,指针是C语言的灵魂,热心的小萌新博主想帮助大家,前车之覆,后车之鉴,我们一起来学习指针。

有不足的地方,还请大家多多批评指正!若写的好了,大家多多支持一下。谢谢啦~么么哒!

废话不多说!C指针学习之路让我们开始吧!


1. 什么是变量

在介绍指针之前,我们先必须知道什么是变量,就这一点萌新认为在理解C语言指针非常重要!

变量,从字面上理解,就是计算机中存储的数据,并且这个数据是可以变化的,电脑使用内存来记忆计算时所使用的数据

1. 那么内存是如何存储数据的呢?

存储数据之前,首先我们必须申请内存空间,就好比我们去住酒店开房间一样,只有当我们开好房间后,我们才能入住,对不对!换句话说,内存也是一样的,内存就像旅馆,数据各式各样,要先根据数据的需求(即类型)为它申请一块合适的空间。

同样数据在内存中占用的字节大小也不一样 ,所以申请的空间也就不一样。

2. 那一次要申请多大的内存空间呢?
我们是根据数据类型来决定内存空间的大小的:

  1. short int(短整数类型)占用2个字节空间
  2. int(整数类型)占用2个字节空间
  3. long int(长整数类型)占用4个字节空间
  4. long long int(超长整数类型)占用4个字节空间

同时,我们可以使用sizeof()函数来输出不同数据类型的字节长度,在不同的环境下,相同数据占用的字节长度可能不同。

1
2
3
4
5
6
7
8
int main(void)
{
printf("sizeof(int)=%d\n",sizeof(int)); //占4个字节空间
printf("sizeof(float)=%d\n",sizeof(float)); //占4字节空间
printf("sizeof(double)=%d\n",sizeof(double));//占8字节空间
printf("sizeof(char)=%d\n",sizeof(char)); //占1字节空间
printf("sizeof(long int)=%d\n",sizeof(long int)); //占8字节空间
}

根据数据类型,决定空间大小

3. 那么由谁去申请内存空间呢?
实际上当我们在写声明变量的时候,就已经在申请内存空间了,声明变量后,程序执行时由系统去自动分配内存空间,根据数据类型的字节长度去分配合适大小的内存空间,这个过程是由编译器完成的。这就好比有些软件占用的内存空间小,而有些软件占用的内存的大,占用内存空间大的程序运行时调用的变量就多,而内存小的,调用的变量就少,一个道理。

4. 那么已经将数据存入内存,如何找到它呢?
我们先要知道数据存入内存中,不是为了玩的,而是要让CPU去调用,去运算的。

  1. 不同的数据存入内存中有不同的地址空间,一块地址空间只能存放一个数据,而这个地址空间就是变量
  2. 每申请出的一块空间,就是一个变量,变量就是一块地址空间。

还是和开房间一样,当我们退房的时候,下一次的顾客依然可以入住上一次已经退过房的房间,而我们常说的变量,就是一块可以存放数据的内存空间。

5. 那我们如何找到这个地址空间呢?
假如张三住401,李四住307,我们在找张三的时候,只需要知道401这个房间号就可以了。内存空间也是一样,也有它的房间号。内存空间由32位二进制数表示,常用标记方法是:0xFFFFFFFF,是由0x八段十六进制整数表示的。

  1. 0x0001ab2e————存储值4
  2. 0x0001ab2a————存储值5

这就是内存空间的房间号了,当我们在找存储在这个空间里面的数据(存储值)的时候,用这个地址就可以找到了。但是萌新相信没有哪个小伙伴会这么记忆内存地址的吧,这么一长串的数字,不过那不排除记忆力好的小伙伴。

6. 那么内存地址不好记,怎么办?
计算机可以通过内存地址找到它所存储的值,我们通过引入变量名来方便人的记忆,变量名就是给内存空间起的别名。

变量名:给内存地址起的名字

对于直接输出变量值,我们无需知道变量具体存储在哪块内存空间,只需要知道变量名即可

1
2
3
int num1=1;
int num2=2;
int num3=3;
内存地址 别名 存储的数据
0x0001ab10 num1 1
0x0001ab14 num2 2
0x0000ab18 num3 3

7. 那我们知道了内存地址,如何去操作它?

2. 什么是指针

在这里插入图片描述

我们使用指针去保存并操作变量的地址(注意是保存和操作内存空间的地址)

8. 那我们为什么要用指针?

  • 原因如下:
  • 提高程序执行效率
  • 实现函数与指针的双向通信
  • 利用指针实现动态内存分配
  • 利用指针操作内存

我们通过指针去访问变量(内存空间)这个过程,叫做:间接访问

  • 即通过访问变量a,b的地址,然后找到变量a,b的值的过程,就是间接访问

我们先声明两个指针变量

1
2
int *p1=&a;  指针p1保存a的地址
int *p2=&b; 指针p2保存b的地址
1
2
用指针p1保存变量a的地址,然后就可以找到变量a
用指针p2保存变量b的地址,然后就可以找到变量b

9. 那么使用&地址符也能取出变量地址,干嘛还需要指针呢?
因为在程序运行中,变量的地址是变化的!不是静态的。

3. 指针的含义

指针:是一种数据类型,用于保存其他变量的地址
指针又分为:整数指针,浮点数指针,字符指针,函数指针,数组指针,指针的指针等。

  • 指针变量保存其他变量的地址,通过对变量地址的操作来间接的操作变量,实现更多的功能。
  • 声明的变量是指针类型,这个变量就是指针变量,指针变量保存其它变量的地址,来间接访问变量。(切记!)

10. 我们如何来定义一个指针变量?

1
数据类型名  *指针变量名;

*必须存在,且最好靠近指针变量名

int * 指向整数类型的指针
float * 指向浮点数类型的指针

来个例子吧:

  1. 先声明int num1=6
    我们有一个变量,叫num1,它是一个整数类型,在这个变量里存放了6这个整型数据;
  2. 我们再声明一个指针p,去保存num1的地址(叫作指针p指向num1),所以我们声明的指针为int *p

11. 与指针相关的操作符
指针声明好后,我们就要给它赋值了,但是我们不能随便赋值。注意:指针保存的是地址,这个地址是以32位二进制整数的形式表示的,当我们赋一个随便的整数,那么指针就指向了一块未知的地址。那么问题来了,在这个连你都不知道的地址里怎么能找到有你想要的数据呢?答案当然是不!

12. 那么我们如何给指针赋值呢?

  1. 先得到地址空间的地址(即取地址)
  2. 然后把这个地址交给指针来保存(指针就是一种用来保存地址的数据类型,上面已经讲过了)

我们这样给指针赋值:

1
p=&a;

记作:指针 p 指向变量 a,实际上就是指针 p 保存了变量 a 的地址

既然我们已经得到了地址,就可以取在地址里的数据了,但是问题又来了!

13. 那我们怎么取这个地址里的数据呢?
我们声明一个指针:int *p,然后用指针p来取值
这时有小伙伴要用指针p去接收变量a的地址,于是写成*p=&a

这是错误的!

我们在操作指针p的时候是不能带星号的,应该写成p=&a,这才叫用指针p去接收变量a的地址

我们在声明完int *p后,以后出现的星号就代表取地址里的值了,这一点大家要好好理解。

  • p:表示保存变量的地址
  • *p:表示取出已保存地址中的值

所以注意!
声明一定要带星号,赋值不能带星号

1
2
int *p;  声明指针
p=&a; 指针p保存了变量a的地址

我们再来个例子吧!

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main(void)
{
int a=3; //声明变量a,把数值3赋给a
int *p=NULL;//声明指针p,指针初始化赋空值,否则成为野指针
p=&a; //让指针p保存a的地址
printf{"var a add=%p\n",&a};//输出变量a的地址
printf{"p=%p\n",p}; //输出指针p的值,也就是保存了a的地址空间
printf{"date a=%d\n",a}; //输出a的值
printf{"*p=%d\n",*p}; //对指针p取值,也就是在a的地址空间里取值
return 0;
}

运行结果:

1
2
3
4
var a add=0x7fff2c4dc274 
p=0x7fff2c4dc274
date a=3
*p=3

在这里插入图片描述

14. 指针变量关联的四个属性

1
int *p1=&a;
  • 指针变量的地址——指针自身的内存地址
  • 指针变量的值——变量a的地址
  • 指针指向的地址——指针p保存a的地址
  • 指针指向的——地址空间中a的值
1
2
int i=1; //假设i的地址为100,实际地址不是这样的
int *p=&i; //假设指针p的地址为200

指针变量的值也就是指针的值,也就是变量i的地址,也就是说p=100
而指针指向的值,指的是指针所指向的地址100这个内存位置,所对应的值,也就是变量i的值,为1

所以【指针变量的值】与【指针指向的地址】是两个相同的概念,小伙伴们切记!

15. 什么叫指向?
用指针保存某个变量的地址,就叫做指向

16.指针的值和变量的地址之间的联系
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main(void)
{
int num1=3;
int num2=2;
int *p=NULL;
p=&num1;
printf("p date=%p\n",p);
p=&num2;
printf("p date2=%p\n",p);
printf("address num1=%p\n",&num1);
printf("address num2=%p\n",&num2);
return 0;
}

结果:

1
2
3
4
p date=0x7fff7670e214 
p date2=0x7fff7670e210
address num1=0x7fff7670e214
address num2=0x7fff7670e210

我们可以看出,我们可以通过指针去保存某个变量的值,但是我们不能去更改变量的地址。好比我们只能记录张三退房后又搬到哪里去了,但是我们不能去更改张三住过的房间,因为房间是固定的,同样地址空间也是固定的。

17. 指针变量的赋值

  • 指针变量初始化必须赋值,而且只能赋值已经声明后的变量地址(先有变量,再有指针)
  • 给指针变量直接就赋值一个整数,这是十分危险的
  • 对指针变量赋值,实际就是让指针指向一个新的内存空间

再举个例子

1
2
3
int a=0;
int *p1=&a;
int *p2=23;

如果我们直接用int *p=23声明一个整数,这么做,指针直接指向23这个内存空间,你能保证这个地址里一定有你要的值吗?

18. 指针变量的字节长度与所指向的内容无关

指针既然是变量,也会有它的字节长度,但是指针变量的字节长度与所指向的内容无关

在32位的操作系统中,指针变量的字节长度是4。
在64位的操作系统中,指针变量的字节长度是8。

1
2
3
4
5
6
7
8
9
int main()
{
printf("sizeof(int *)=%d\n",sizeof(int *)); //8个字节
printf("sizeof(float *)=%d\n",sizeof(float *)); //8个字节
printf("sizeof(double *)=%d\n",sizeof(double *)); //8个字节
printf("sizeof(char *)=%d\n",sizeof(char *)); //8个字节
printf("sizeof(long int *)=%d\n",sizeof(long int *));//8个字节
return 0;
}

19. 比较讨厌的星号,因为它有双重意义
星号在声明的时候和在取值的时候,意义不同:

  • 在声明指针的时候,加星号只是表示:为声明的是一个指针;
  • 在取值的时候,加星号表示:我要从该地址取值出来;

最后注意

  1. 指针的类型和它所指向的变量类型要匹配
    1
    2
    3
    char c='e';
    int *p=&c;
    //指针的类型为int型,但是指针保存了一个char型的内存空间
  2. 声明指针的时候,同时给指针赋值,容易受到惯性思维的影响,所以不要一开始就给指针一个整数常量
1
int *p=0xfff20321;

推荐

1
2
int *p=NULL; //声明指针的时候先给指针p赋空值
p=&a; //再让指针保存其它变量的地址
  1. 不经过初始化的指针,是不安全的,一般可以给它赋上空值。
1
2
3
int a=0;
int *p;
*p=&a;
  • 声明的时候,星号的作用只是为了区别普通变量与指针
  • 操作的时侯,星p代表的是取变量(内存地址)的值,所以声明以后再用星p就只能表示:把&a(a的地址)中的值赋给指针p,即a地址中存储的值被指针p取出

4. 指针的运算规则

  1. 两个指针永远不能做加法运算
    张三住301房间,李四住407房间,我们把301和407加起来,是没有意义的。

  2. 同类型的指针相减:两个指针所指的两个地址之间有多少个该类型的变量
    通过407减301,我们就能得出在407到301之间有多少房间了,对不对!

  3. 不同类型的指针不能相减

  4. 指针加n
    表示指针在原来的值+n*sizeof(指针所指变量类型)

  5. 指针减n
    表示指针在原来的值-n*sizeof(指针所指变量类型)

1
2
3
4
5
6
7
8
9
int a=0;
int *p=NULL;
p=&a;
&a=0x00001000;
p=0x00001000;
*p=0;
p+1=0x00001000 + sizeof(int);
p+2=0x00001000 + 2×sizeof(int);
p+3=0x00001000 + 3×sizeof(int);
  1. 只有相同类型的指针,才能相互赋值,否则必须强制类型转换一个变量
1
2
3
4
5
int a=3;
int b=4;
int *pa=&a;
int *pa=&b;
pa=pb;

a=b或pa=pb是等效操作,但不是等价的,因为虽然a的值变化了,但是原来那个a和b的地址没有变化。

指针的自加运算
C语言中*p++(*p)++*++p++*p的区别:
在这里插入图片描述

1
1.*p++

*++ 两个处于同一优先级,结合方向是自右向左,但是前提是当++在变量前面的时候才处理同一优先级,当++在变量之后时,你可以将++的优先级看成最低级的。

因为程序自左向右运行,先取出*p(保存的地址空间中的值),再使指针p保存的地址空间下移1个空间。

相当于取出存储的值之后,指针p移到下一个地址空间,存储的值并没有改变。可以这么理解:p是个指针,给p加1,相当于让p指向了x所在地址的下一个位置。

1
2.*++p

*++ 两个处于同一优先级,结合方向是自右向左。先进行++p,即先将指针p自增1, 操作再取出该值*(++p),即取出下一地址空间的值。

1
3.++*p

*++ 两个处于同一优先级,结合方向是自右向左。先进行*p,即先取指针p指向的值,再将该值自增1,即(*p)+1;

1
4.(*p)++

括号优先级最高,先取出p指向的变量(地址空间)中的值,再对存储的值进行自加,与++*p相同。
*p 是指针p所指向的地址中对应的值。若为3,当输出*p之后,再给*p自加1,所以空间x中存储的值由3就变为4,而此时地址空间x并没有移动。


最后,C指针还有许许多多的知识内容,萌新这里仅仅介绍了C指针的初识,尽力而为,最后谢谢大家了,真心希望可以通过自己的博客帮到大家。

开篇声明,小萌新创作这篇文章的初衷只是想介绍一些关于黑客方面的基础知识,从读者的角度去满足一些计算机爱好者的好奇心,那我们就开始吧!

在这里插入图片描述
在此引用前辈的语句作为文章的开篇,成为黑客的第一步,开始阅读吧!

安全问题的本质是信任的问题

Web安全简史
  • 起初,研究计算机系统和网络的人,被称为Hacker,他们对计算机系统有着深入的理解,因此往往能够发现其中的问题。Hacker在中国按照音译,被称为黑客。在计算机安全领域,黑客是一群破坏规则、不喜欢被拘束的人,因此总想着能够找到系统的漏洞,以获得一些规则之外的权力。对于现代计算机系统来说,在用户态的最高权限是Root,也是黑客们最渴望能够获取的系统最高权限。漏洞利用代码能够帮助黑客们达成这一目标。在黑客的世界里,有的黑客,精通计算机技术,能自己挖掘漏洞,而有的黑客,则只对攻击本身感兴趣,对计算机原理和各种编程技术的了解比较粗浅,因此只懂得编译别人的代码,自己并没有动手能力,这种黑客被称为脚本小子。在现实世界里,真正造成破坏的,往往并非那些挖掘并研究漏洞的黑客们,而是这些脚本小子。

黑客简史

  • 中国黑客的发展分为启蒙时代,黄金时代,黑暗时代:
  • 首先是启蒙时代,这个时期大概处在 20 世纪 90 年代,此时中国的互联网也刚刚处于起步阶段,一些热爱新兴技术的青年受到国外黑客技术的影响,开始研究安全漏洞。启蒙时代的黑客们大多是由于个人爱好而走上这条道路,好奇心与求知欲是驱使他们前进的动力,没有任何利益的瓜葛。这个时期的中国黑客们通过互联网,看到了世界,因此与西方发达国家同期诞生的黑客精神是一脉相传的,他们崇尚分享、自由、免费的互联网精神,并热衷于分享自己的最新研究成果。
  • 接下来是黄金时代,这个时期以中美黑客大战为标志。在这个历史背景下,黑客这个特殊的群体一下子几乎吸引了社会的所有眼球,而此时黑客圈子所宣扬的黑客文化以及黑客技术的独特魅力也吸引了无数的青少年走上这条道路。自此事件后,各种黑客组织如雨后春笋般冒出。此阶段的中国黑客,其普遍的特点是年轻,有活力,充满激情,但在技术上也许还不够成熟。此时期黑客圈子里贩卖漏洞、恶意软件的现象开始升温,同时因为黑客群体的良莠不齐,也开始出现以赢利为目的的攻击行为,黑色产业链逐渐成型。
  • 最后是黑暗时代,这个阶段从几年前开始一直延续到今天,也许还将继续下去。在这个时期黑客组织也遵循着社会发展规律,优胜劣汰,大多数的黑客组织没有坚持下来。在上一个时期非常流行的黑客技术论坛越来越缺乏人气,最终走向没落。所有门户型的漏洞披露站点,也不再公布任何漏洞相关的技术细节。伴随着安全产业的发展,黑客的功利性越来越强。黑色产业链开始成熟,这个地下产业每年都会给互联网造成数十亿的损失。而在上一个时期技术还不成熟的黑客们,凡是坚持下来的,都已经成长为安全领域的高级人才,有的在安全公司贡献着自己的专业技能,有的则带着非常强的技术进入了黑色产业。此时期的黑客群体因为互相之间缺失信任已经不再具有开放和分享的精神,最为纯粹的黑客精神实质上已经死亡。

黑帽白帽在这里插入图片描述

  • 正如一个硬币有两面一样,黑客也有好坏之分。在黑客的世界中,往往用帽子的颜色来比喻黑客的好坏。
  • 白帽子,则是指那些精通安全技术,但是工作在反黑客领域的专家们
  • 黑帽子,则是指利用黑客技术造成破坏,甚至进行网络犯罪的群体
  • 同样是研究安全,白帽子和黑帽子在工作时的心态是完全不同的。对于黑帽子来说,只要能够找到系统的一个弱点,就可以达到入侵系统的目的;而对于白帽子来说,必须找到系统的所有弱点,不能有遗漏,才能保证系统不会出现问题。这种差异是由于工作环境与工作目标的不同所导致的。白帽子一般为企业或安全公司服务,工作的出发点就是要解决所有的安全问题,因此所看所想必然要求更加的全面、宏观;黑帽子的主要目的是要入侵系统,找到对他们有价值的数据,因此黑帽子只需要以点突破,找到对他们最有用的一点,以此渗透,因此思考问题的出发点必然是有选择性的、微观的。
  • 从对待问题的角度来看,黑帽子为了完成一次入侵,需要利用各种不同漏洞的组合来达到目的,是在不断地组合问题;而白帽子在设计解决方案时,如果只看到各种问题组合后产生的效果,就会把事情变复杂,难以细致入微地解决根本问题,所以白帽子必然是在不断地分解问题,再对分解后的问题逐个予以解决。这种定位的不对称,也导致了白帽子的安全工作比较难做。破坏永远比建设容易,但凡事都不是绝对的。要如何扭转这种局面,一般来说,白帽子选择的方法,是克服某种攻击方法,而并非抵御单次的攻击。
  • 在安全领域也普遍认为:“最大的漏洞就是人”。写得再好的程序,在有人参与的情况下,就可能会出现各种各样不可预知的情况,比如管理员的密码有可能泄露,程序员有可能关掉了安全的配置参数等等。安全问题往往发生在一些意想不到的地方。另一方面,防御技术在不断完善的同时,攻击技术也在不断地发展。这就像一场军备竞赛,看谁跑在前面。白帽子们刚把某一种漏洞全部堵上,黑帽子们转眼又会玩出新花样。谁能在技术上领先,谁就能占据主动。互联网技术日新月异,在新技术领域的发展中,也存在着同样的博弈过程。可现状是,如果新技术不在一开始就考虑安全设计的话,防御技术就必然会落后于攻击技术,导致历史不断地重复。
计算机漏洞
  • 计算机漏洞是系统的一组特性,恶意的主体(攻击者或者攻击程序)能够利用这组特性,通过手段和方式获取对资源的未授权访问,或者对系统造成损害。
  • 漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。
  • 漏洞会影响到很大范围的软硬件设备,包括作系统本身及其支撑软件,网络客户和服务器软件,网络路由器和安全防火墙等。换言之,在这些不同的软硬件设备中都可能存在不同的安全漏洞问题。在不同种类的软、硬件设备,同种设备的不同版本之间,由不同设备构成的不同系统之间,以及同种系统在不同的设置条件下,都会存在各自不同的安全漏洞问题。

漏洞问题是与时间紧密相关的

  • 一个系统从发布的那一天起,随着用户的深入使用,系统中存在的漏洞会被不断暴露出来,这些早先被发现的漏洞也会不断被系统供应商发布的补丁软件修补,或在以后发布的新版系统中得以纠正。而在新版系统纠正了旧版本中具有漏洞的同时,也会引入一些新的漏洞和错误。因而随着时间的推移,旧的漏洞会不断消失,新的漏洞会不断出现。漏洞问题也会长期存在。因而脱离具体的时间和具体的系统环境来讨论漏洞问题是毫无意义的。只能针对目标系统的作系统版本、其上运行的软件版本以及服务运行设置等实际环境来具体谈论其中可能存在的漏洞及其可行的解决办法。同时应该看到,对漏洞问题的研究必须要跟踪当前最新的计算机系统及其安全问题的最新发展动态。这一点如同对计算机病毒发展问题的研究相似。如果在工作中不能保持对新技术的跟踪,就没有谈论系统安全漏洞问题的发言权,既使是以前所作的工作也会逐渐失去价值。
漏洞问题与不同安全级别计算机系统之间的关系
  • 橘皮书正式名称是“受信任计算机系统评量基准”。橘皮书中对可信任系统的定义是这样的:一个由完整的硬件及软件所组成的系统,在不违反访问权限的情况下,它能同时服务于不限定个数的用户,并处理从一般机密到最高机密等不同范围的信息。

  • 橘皮书将一个计算机系统可接受的信任程度加以分级,凡符合某些安全条件、基准规则的系统即可归类为某种安全等级。橘皮书将计算机系统的安全性能由高而低划分为A、B、C、D四大等级。其中:

**D级——最低保护(Minimal Protection)**,凡没有通过其他安全等级测试项目的系统即属于该级,如Dos,Windows个人计算机系统

**C级——自主访问控制(Discretionary Protection)**,该等级的安全特点在于系统的客体(如文件、目录)可由该系统主体(如系统管理员、用户、应用程序)自主定义访问权。例如:管理员可以决定系统中任意文件的权限

**B级——强制访问控制(Mandatory Protection)**,该等级的安全特点在于由系统强制对客体进行安全保护,在该级安全系统中,每个系统客体(如文件、目录等资源)及主体(如系统管理员、用户、应用程序)都有自己的安全标签Security Label,系统依据用户的安全等级赋予其对各个对象的访问权限

**A级——可验证访问控制(Verified Protection)**,而其特点在于该等级的系统拥有正式的分析及数学式方法可完全证明该系统的安全策略及安全规格的完整性与一致性

  • 可见,根据定义,系统的安全级别越高,理论上该系统也越安全。可以说,系统安全级别是一种理论上的安全保证机制。是指在正常情况下,在某个系统根据理论得以正确实现时,系统应该可以达到的安全程度。系统安全漏洞是指可以用来对系统安全造成危害,系统本身具有的,或设置上存在的缺陷。总之,漏洞是系统在具体实现中的错误。比如在建立安全机制中规划考虑上的缺陷,作系统和其他软件编程中的错误,以及在使用该系统提供的安全机制时人为的配置错误等。

  • 安全漏洞的出现,是因为人们在对安全机制理论的具体实现中发生了错误,是意外出现的非正常情况。而在一切由人类实现的系统中都会不同程度的存在实现和设置上的各种潜在错误。因而在所有系统中必定存在某些安全漏洞,无论这些漏洞是否已被发现,也无论该系统的理论安全级别如何。所以可以认为,在一定程度上,安全漏洞问题是独立于作系统本身的理论安全级别而存在的。并不是说,系统所属的安全级别越高,该系统中存在的安全漏洞就越少。可以这么理解,当系统中存在的某些漏洞被入侵者利用,使入侵者得以绕过系统中的一部分安全机制并获得对系统一定程度的访问权限后,在安全性较高的系统当中,入侵者如果希望进一步获得特权或对系统造成较大的破坏,必须要克服更大的障碍。

安全漏洞与系统攻击之间的关系
  • 系统安全漏洞是在系统具体实现和具体使用中产生的错误,但并不是系统中存在的错误都是安全漏洞。只有能威胁到系统安全的错误才是漏洞。许多错误在通常情况下并不会对系统安全造成危害,只有被人在某些条件下故意使用时才会影响系统安全。
  • 漏洞虽然可能最初就存在于系统当中,但一个漏洞并不是自己出现的,必须要有人发现。在实际使用中,用户会发现系统中存在错误,而入侵者会有意利用其中的某些错误并使其成为威胁系统安全的工具,这时人们会认识到这个错误是一个系统安全漏洞。系统供应商会尽快发布针对这个漏洞的补丁程序,纠正这个错误。这就是系统安全漏洞从被发现到被纠正的一般过程。
  • 系统攻击者往往是安全漏洞的发现者和使用者,要对于一个系统进行攻击,如果不能发现和使用系统中存在的安全漏洞是不可能成功的。对于安全级别较高的系统尤其如此。
    系统安全漏洞与系统攻击活动之间有紧密的关系。因而不该脱离系统攻击活动来谈论安全漏洞问题。了解常见的系统攻击方法,对于有针对性的理解系统漏洞问题,以及找到相应的补救方法是十分必要的。
IP地址

IP是Internet Protocol(网际互连协议)的缩写,是TCP/IP体系中的网络层协议。

  • 设计IP的目的是提高网络的可扩展性:
  • 一是解决互联网问题,实现大规模、异构网络的互联互通。
  • 二是分割顶层网络应用和底层网络技术之间的耦合关系,以利于两者的独立发展。根据端到端的设计原则,IP只为主机提供一种无连接、不可靠的、尽力而为的数据报传输服务。

IP地址,它是为标识Internet上主机位置而设置的。Internet上的每一台计算机都被赋予一个世界上唯一的32位Internet地址,这一地址可用于与该计算机有关的全部通信。为了方便起见,在应用上我们以8比特为一单位,组成四组十进制数字来表示每一台主机的位置。

1
2
3
4
5
6
IP地址分类:
A:1.0.0.0-126.0.0.0
B:128.1.0.0-191.255.0.0
C:192.0.1.0-223.255.255.0
D:224.0.0.0-239.255.255.255
E:240.0.0.0-255.255.255.254
  • 主机号全为0的地址表示网络地址
  • 主机号全为1的地址表示广播地址
内网(私有地址)
1
2
3
10.0.0.0-10.255.255.255
172.16.0.0-172.31.255.255
192.168.0.0-192.168.255.255
端口(Port)

一种数据的传输通道

用于接受某些数据,然后传输给相应的服务,而电脑将这些数据处理后,再将相应的恢复通过开启的端口传给对方。一般每一个端口的开放对应了相应的服务,要关闭这些端口只需要将对应的服务关闭就可以了。

  • 在网络技术中,端口大致有两种意思:
  • 一是物理意义上的端口,比如,ADSL Modem、集线器、交换机、路由器用于连接其他网络设备的接口,如RJ-45端口、SC端口等等。
  • 二是逻辑意义上的端口,一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。

我们这里将要介绍的就是逻辑意义上的端口。我们这里所说的端口,不是计算机硬件的I/O端口,而是软件形式上的概念.工具提供服务类型的不同,端口分为两种,一种是TCP端口,一种是UDP端口。计算机之间相互通信的时候,分为两种方式:一种是发送信息以后,可以确认信息是否到达,也就是有应答的方式,这种方式大多采用TCP协议;一种是发送以后就不管了,不去确认信息是否到达,这种方式大多采用UDP协议。对应这两种协议的服务提供的端口,也就分为TCP端口和UDP端口。

  • 按端口号可分为4大类:
  • 公认端口(Well-Known Ports):范围从0到1023
    它们紧密绑定(binding)于一些服务。通常这些端口的通讯明确表明了某种服务的协议。例如:21端口分配给FTP服务,25端口分配给SMTP(简单邮件传输协议)服务,80端口分配给HTTP服务,135端口分配给RPC(远程过程调用)服务等等。
    我们在IE的地址栏里输入一个网址的时候( 比如www.cce.com.cn)是不必指定端口号的,因为在默认情况下WWW服务的端口 号是“80”。
    网络服务是可以使用其他端口号的,如果不是默认的端口号则应该在 地址栏上指定端口号,方法是在地址后面加上冒号“:”(半角),再加上端口 号。比如使用“8080”作为WWW服务的端口,则需要在地址栏里输入“www.cce.com.cn:8080”。
    但是有些系统协议使用固定的端口号,它是不能被改变的,比如139 端口专门用于NetBIOS与TCP/IP之间的通信,不能手动改变。
  • 动态端口(Dynamic Ports):范围从1024到65535
    之所以称为动态端口,是因为它 一般不固定分配某种服务,而是动态分配。动态分配是指当一个系统进程或应用 程序进程需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配 一个供它使用。当这个进程关闭时,同时也就释放了所占用的端口号。
  • 注册端口(Registered Ports):从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用 于许多其它目的。例如:许多系统处理动态端口从1024左右开始。
  • 私有端口(Private Ports):从49152到65535。理论上,不应为服务分配这些端口。实际上,机器通常从1024 起分配动态端口。但也有例外:SUN的RPC端口从32768开始。
域名地址
  • 尽管IP地址能够唯一地标识网络上的计算机,但IP地址是数字型的,用户记忆这类数字十分不方便,于是人们又发明了另一套字符型的地址方案,即所谓的域名地址。

IP地址和域名是一一对应的,域名由两种基本类型组成:以机构性质命名的域和以国家地区代码命名的域

在这里插入图片描述
在这里插入图片描述
域名解析方式

  1. 递归查询法
    1. 如果DNS服务器无法解析出DNS客户机所要求查询的域名所对应的IP地址时,DNS服务器代表DNS客户机来查询或联系其他DNS服务器,以完全解析该名称,并将应答返回给客户机。
    2. 本机向本地域名服务器发出一次查询请求,就静待最终的结果。如果本地域名服务器无法解析,自己会以DNS客户机的身份向其它域名服务器查询,直到得到最终的IP地址告诉本机。
  2. 迭代查询法
    1. 客户机送出查询请求后,若该DNS服务器中不包含所需数据,它会告诉客户机另外一台DNS服务器的IP地址,使客户机自动转向另一台DNS服务器查询,以此类推,直到查询数据,否则由最后一台DNS服务器通知客户机查询失败。
    2. 本地域名服务器向根域名服务器查询,根域名服务器告诉它下一步到哪里去查询,然后它再去查,每次它都是以客户机的身份去各个服务器查询。
  3. 反向查询
    客户机利用IP地址查询其主机完整域名。
URL

统一资源定位器,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式。

  • 我们平时所说的网页地址指的即是URL,URL由三部分组成:资源类型、存放资源的主机域名、资源文件名。

如URL为:http://china-window.com/shanghai/news/wnw.html

  • http指超文本传输协议
  • china-window.com是其Web服务器域名地址
  • shanghai/news是网页所在路径,wnw.html是相应的网页文件
1
2
3
4
标识Internet网上资源位置的三种方式: 
IP地址:202.206.64.33 
域名地址:dns.hebust.edu.cn
URL:http://china-window.com/shanghai/news/wnw.html
HTTP 请求/响应的步骤
  1. 客户端连接到Web服务器
    一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。例如,http://www.oakcms.cn。
  2. 发送HTTP请求
    通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。
  3. 服务器接受请求并返回HTTP响应
    Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
  4. 释放连接TCP连接
    若connection模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求。
  5. 客户端浏览器解析HTML内容
    客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
  • 如果我们在客户端(客户端)浏览器输入 http://www.baidu.com,而baidu.com为要访问的服务器(服务器),下面详细分析客户端为了访问服务器而执行的一系列关于协议的操作:
  1. 客户端浏览器通过DNS解析到www.baidu.com的IP地址220.181.27.48,通过这个IP地址找到客户端到服务器的路径。客户端浏览器发起一个HTTP会话到220.161.27.48,然后通过TCP进行封装数据包,输入到网络层。
  2. 在客户端的传输层,把HTTP会话请求分成报文段,添加源和目的端口,如服务器使用80端口监听客户端的请求,客户端由系统随机选择一个端口,如5000,与服务器进行交换,服务器把相应的请求返回给客户端的5000端口。然后使用IP层的IP地址查找目的端。
  3. 客户端的网络层不用关系应用层或者传输层,主要做的是通过查找路由表确定如何到达服务器,期间可能经过多个路由器,这些都是由路由器来完成的工作,不作过多的描述,无非就是通过查找路由表决定通过那个路径到达服务器。
  4. 客户端的链路层,包通过链路层发送到路由器,通过邻居协议查找给定IP地址的MAC地址,然后发送ARP请求查找目的地址,如果得到回应后就可以使用ARP的请求应答交换的IP数据包。现在就可以传输了,然后发送IP数据包到达服务器的地址。
肉鸡

肉鸡,即拥有管理权限的远程电脑,也是指受黑客远程控制的电脑。
在这里插入图片描述

webshell

web指的是在web服务器上,而shell是用脚本语言编写的脚本程序,webshell就是就是web的一个管理工具,可以对web服务器进行操作的权限

  • webshell是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门。
  • 黑客在入侵了一个网站后,通常会将asp或php后门文件与网站服务器web目录下正常的网页文件混在一起,然后就可以使用浏览器来访问asp或者php后门,得到一个命令执行环境,以达到控制网站服务器的目的。
提权

&emsp;&emsp;提高自己在服务器中的权限

暴力破解
  • 用某种暴力破解软件,对某种需要密码才能使用的服务,把可能的密码尽量多得聚集在一起,列成一个表格然后一个接一个的试,直到试验出正确的密码,也就是破解成功了。不过这种破解方式成功几率不高,耗费时间多,技术成分低,不是迫不得已是不使用的。
ASP脚本入门与防御思想
  1. ASP
    ASP是Active Server Page的缩写,意为“动态服务器页面”,它可以与数据库和其他程序交互。
  • 与HTML相比,ASP网页具有以下特点:
  • (1)利用ASP可以实现突破静态网页的一些功能限制,实现动态网页技术。
  • (2)ASP文件是包含在HTML代码所组成的文件中的,易于修改和测试。
  • (3)服务器上的ASP解释程序会在服务器端制定ASP程序,并将结果以HTML格式传送到客户端浏览器上,因此使用各种浏览器都可以正常浏览ASP所产生的网页。
  • (4)ASP提供了一些内置对象,使用这些对象可以使服务器端脚本功能更强。例如可以从web浏览器中获取用户通过HTML表单提交的信息,并在脚本中对这些信息进行处理,然后向web浏览器发送信息。
  1. ASP的工作原理
    1、用户向浏览器地址栏输入网址,默认页面的扩展名是.asp
    2、浏览器向服务器发出请求
    3、服务器引擎开始运行ASP程序
    4、ASP文件按照从上到下的顺序开始处理,执行脚本命令,执行HTML页面内容
    5、页面信息发送到浏览器
  • ASP能做什么
    • 动态地编辑、改变或者添加网页的任何内容
    • 对由用户从 HTML 表单提交的查询或者数据作出响应
    • 访问数据或者数据库,并向浏览器返回结果
    • 为不同的用户定制网页,提高这些页面的可用性
    • 提供安全性保障——ASP代码不能从浏览器查看
    • 优秀的 ASP 编程可最小化网络流量
  1. ASP脚本防御思想
    • 一直以来, ASP给人的印象都是不太安全,实则不然。脚本语言和数据库本身并没有过错。ASP脚本的安全隐患在于管理员的不重视和对 ASP脚本的不了解,实际上只要使用得当, ASP脚本其实可以很安全。
    • 首先要谈到的就是注入,关于要如何过滤ASP脚本注入,已经不是第一次提到了,但想要过滤非法字符这也不是意见简单的事。防止了Get,Post提交方式注入,还要记得过滤Cookie注入;防止了数字型、字符型注入,还要记得防止搜索型注入;防止了常规的非法字符,还要防范编码和大小写转换等情况。还有表名、字段名的选取问题,很多情况下注入者大都不知道什么是表名和字段名的。可是很多程序员们都喜欢用admin、manage、user、pass、password、username等容易被猜到的表名和字段名,导致ASP脚本变得极度不安全。
    • 其实,只要在所有表名上加个简单的前缀,就可以有效的防止被人猜到。也不影响正常的使用。还要防止暴库,对密码进行MD5加密、修改默认数据库路径、常给网站程序打补丁、不用sa权限连接数据库、删除SQL server的存储扩展以及禁止非法IP连接1433端口。一般来讲,防守比入侵容易,入侵一个网站的传统方法需要同时满足很多条件,管理只要破坏其中一个条件就能够保住网站,所以防御仅仅是对安全性的重视和耐心。
弱口令
  • 弱口令没有严格和准确的定义,通常认为容易被别人猜测到或被破解工具破解的口令均为弱口令。弱口令指的是仅包含简单数字和字母的口令,例如“123”、“abc”等,因为这样的口令很容易被别人破解,从而使用户的计算机面临风险,因此不推荐用户使用。

  • 弱口令避免措施:

  1. 不使用空口令或系统缺省的口令,因为这些口令众所周知,为典型的弱口令
  2. 口令长度不小于8个字符
  3. 口令不应该为连续的某个字符或重复某些字符的组合
  4. 口令应该为以下四类字符的组合,大写字母(A-Z)、小写字母(a-z)、数字(0-9)和特殊字符。每类字符至少包含一个。如果某类字符只包含一个,那么该字符不应为首字符或尾字符
  5. 口令中不应包含本人、父母、子女和配偶的姓名和出生日期、纪念日期、登录名、E-mail地址等等与本人有关的信息,以及字典中的单词
  6. 口令不应该为用数字或符号代替某些字母的单词
  7. 口令应该易记且可以快速输入,防止他人从你身后很容易看到你的输入
  8. 至少90天内更换一次口令,防止未被发现的入侵者继续使用该口令
SQL注入
  • SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

  • SQL是操作数据库数据的结构化查询语言,网页的应用数据和后台数据库中的数据进行交互时会采用SQL。而SQL注入是将Web页面的原URL、表单域或数据包输入的参数,修改拼接成SQL语句,传递给Web服务器,进而传给数据库服务器以执行数据库命令。如Web应用程序的开发人员对用户所输入的数据或cookie等内容不进行过滤或验证就直接传输给数据库,就可能导致拼接的SQL被执行,获取对数据库的信息以及提权,发生SQL注入攻击。

注入原理:
&emsp;&emsp;SQL注入攻击是通过操作输入来修改SQL语句,用以达到执行代码对web服务器进行攻击的方法。简单的说就是在表单、输入域名或页面请求的查询字符串中插入SQL命令,最终使web服务器执行恶意命令的过程。可以通过一个例子简单说明SQL注入攻击。假设某网站页面显示时URL为http://www.example.com?test=123,此时URL实际向服务器传递了值为123的变量test,这表明当前页面是对数据库进行动态查询的结果。由此,我们可以在URL中插入恶意的SQL语句并进行执行。另外,在网站开发过程中,开发人员使用动态字符串构造SQL语句,用来创建所需的应用,这种情况下SQL语句在程序的执行过程中被动态的构造使用,可以根据不同的条件产生不同的SQL语句,比如需要根据不同的要求来查询数据库中的字段。这样的开发过程其实为SQL注入攻击留下了很多的可乘之机。

注入过程:

  1. SQL注入点探测
    探测SQL注入点是关键的一步,通过适当的分析应用程序,可以判断什么地方存在SQL注入点。通常只要带有输入提交的动态网页,并且动态网页访问数据库,就可能存在SQL注入漏洞。如果程序员信息安全意识不强,采用动态构造SQL语句访问数据库,并且对用户的输入未进行有效性验证,则存在SQL注入漏洞的可能性很大。一般通过页面的报错信息来确定是否存在SQL注入漏洞。
  2. 收集后台数据库信息
    不同数据库的注入方法、函数都不尽相同,因此在注入之前,我们先要判断一下数据库的类型。判断数据库类型的方法很多,可以输入特殊字符,如单引号,让程序返回错误信息,我们根据错误信息提示进行判断。
  3. 猜解用户名和密码
    数据库中的表和字段命名一般都是有规律的。通过构造特殊SQL语句在数据库中依次猜解出表名、字段名、字段数、用户名和密码。
  4. 查找web后台管理入口
    web后台管理通常不对普通用户开放,要找到后台管理的登录网址,可以利用web目录扫描工具快速搜索到可能的登录地址,然后逐一尝试,便可以找到后台管理平台的登录网址。
  5. 入侵和破坏
    一般后台管理具有较高权限和较多的功能,使用前面已破译的用户名、密码成功登录后台管理平台后,就可以任意进行破坏,比如上传木马、篡改网页、修改和窃取信息等,还可以进一步提权,入侵Web服务器和数据库服务器。
脚本攻击
  • 脚本是使用一种特定的描述性语言,依据一定的格式编写的可执行文件,又称宏或批处理软件,脚本通常可以由应用程序临时调用并执行。各类脚本目前被广泛地应用于网页设计中。脚本不仅可以较小网页的规模和提高网页浏览速度,而且可以丰富网页的表现,如动画、声音等。
蜜罐
  • 蜜罐技术本质上是一种对攻击方进行欺骗的技术,通过布置一些作为诱饵的主机、网络服务或者信息,诱使攻击方对它们实施攻击,从而可以对攻击行为进行捕获和分析,了解攻击方所使用的工具与方法,推测攻击意图和动机,能够让防御方清晰地了解他们所面对的安全威胁,并通过技术和管理手段来增强实际系统的安全防护能力。

  • 蜜罐好像是故意让人攻击的目标,引诱黑客前来攻击。所以攻击者入侵后,你就可以知道他是如何得逞的,随时了解针对服务器发动的最新的攻击和漏洞。还可以通过窃听黑客之间的联系,收集黑客所用的种种工具,并且掌握他们的社交网络。蜜罐是一个包含漏洞的系统,它模拟一个或多个易受攻击的主机,给黑客提供一 个容易攻击的目标。由于蜜罐没有其它任务需要完成,因此所有连接的尝试都应被视为是可疑的。蜜罐的另一个用途是拖延攻击者对其真正目标的攻击,让攻击者在蜜罐上浪费时间。与此同时,最初的攻击目标受到了保护,真正有价值的内容将不受侵犯。

蜜罐使用方法:

  • 迷惑入侵者,保护服务器
    一般的客户/服务器模式里,浏览者是直接与网站服务器连接的,换句话说,整个网站服务器都暴露在入侵者面前,如果服务器安全措施不够,那么整个网站数据都有可能被入侵者轻易毁灭。但是如果在客户/服务器模式里嵌入蜜罐,让蜜罐作为服务器角色,真正的网站服务器作为一个内部网络在蜜罐上做网络端口映射,这样可以把网站的安全系数提高,入侵者即使渗透了位于外部的“服务器”,他也得不到任何有价值的资料,因为他入侵的是蜜罐而已。虽然入侵者可以在蜜罐的基础上跳进内部网络,但那要比直接攻下一台外部服务器复杂得多,许多水平不足的入侵者只能望而却步。蜜罐也许会被破坏,可是不要忘记了,蜜罐本来就是被破坏的角色。

  • 抵御入侵者,加固服务器
    入侵与防范一直都是热点问题,而在其间插入一个蜜罐环节将会使防范变得有趣,这台蜜罐被设置得与内部网络服务器一样,当一个入侵者费尽力气入侵了这台蜜罐的时候,管理员已经收集到足够的攻击数据来加固真实的服务器。
    采用这个策略去布置蜜罐,需要管理员配合监视,否则入侵者攻破了第一台,就有第二台接着承受攻击了……

  • 诱捕网络罪犯
    这是一个相当有趣的应用,当管理员发现一个普通的客户/服务器模式网站服务器已经牺牲成肉鸡的时候,如果技术能力允许,管理员会迅速修复服务器。那么下次呢?既然入侵者已经确信自己把该服务器做成了肉鸡,他下次必然还会来查看战果,一些企业的管理员不会罢休,他们会设置一个蜜罐模拟出已经被入侵的状态,做起了姜太公。同样,一些企业为了查找恶意入侵者,也会故意设置一些有不明显漏洞的蜜罐,让入侵者在不起疑心的情况下乖乖被记录下一切行动证据,通过与电信局的配合,可以轻易揪出IP源头的那双黑手。

后门
  • 后门是指一种绕过安全性控制而获取对程序或系统访问权的方法。在软件的开发阶段,程序员常会在软件内创建后门以便可以修改程序中的缺陷。如果后门被其他人知道,或是在发布软件之前没有删除,那么它就成了安全隐患。
计算机病毒

计算机病毒(Computer Virus)是编制者在计算机程序中插入的破坏计算机功能或者数据的代码,能影响计算机使用,能自我复制的一组计算机指令或者程序代码。

  • 依附的媒体类型分类:
    (1)网络病毒:通过计算机网络感染可执行文件的计算机病毒。
    (2)文件病毒:主攻计算机内文件的病毒。
    (3)引导型病毒:是一种主攻感染驱动扇区和硬盘系统引导扇区的病毒。
  • 计算机特定算法分类:
    (1)附带型病毒:通常附带于一个EXE文件上,其名称与EXE文件名相同,但扩展是不同的,一般不会破坏更改文件本身,但在DOS读取时首先激活的就是这类病毒。
    (2)蠕虫病毒:它不会损害计算机文件和数据,它的破坏性主要取决于计算机网络的部署,可以使用计算机网络从一个计算机存储切换到另一个计算机存储来计算网络地址来感染病毒。
    (3)可变病毒:可以自行应用复杂的算法,很难发现,因为在另一个地方表现的内容和长度是不同的。

计算机病毒有自己的传输模式和不同的传输路径。计算机本身的主要功能是它自己的复制和传播,这意味着计算机病毒的传播非常容易,通常可以交换数据的环境就可以进行病毒传播。有三种主要类型的计算机病毒传输方式:
(1)通过移动存储设备进行病毒传播:如U盘、CD、软盘、移动硬盘等都可以是传播病毒的路径,而且因为它们经常被移动和使用,所以它们更容易得到计算机病毒的青睐,成为计算机病毒的携带者。
(2)通过网络来传播:这里描述的网络方法也不同,网页、电子邮件、QQ、BBS等都可以是计算机病毒网络传播的途径,特别是近年来,随着网络技术的发展和互联网的运行频率,计算机病毒的速度越来越快,范围也在逐步扩大。
(3)利用计算机系统和应用软件的弱点传播:近年来,越来越多的计算机病毒利用应用系统和软件应用的不足传播出去因此这种途径也被划分在计算机病毒基本传播方式中。

  • 隐蔽性
    计算机病毒不易被发现,这是由于计算机病毒具有较强的隐蔽性,其往往以隐含文件或程序代码的方式存在,在普通的病毒查杀中,难以实现及时有效的查杀。病毒伪装成正常程序,计算机病毒扫描难以发现。并且,一些病毒被设计成病毒修复程序,诱导用户使用,进而实现病毒植入,入侵计算机。因此,计算机病毒的隐蔽性,使得计算机安全防范处于被动状态,造成严重的安全隐患。
  • 破坏性
    病毒入侵计算机,往往具有极大的破坏性,能够破坏数据信息,甚至造成大面积的计算机瘫痪,对计算机用户造成较大损失。如常见的木马、蠕虫等计算机病毒,可以大范围入侵计算机,为计算机带来安全隐患。
  • 传染性
    计算机病毒的一大特征是传染性,能够通过U盘、网络等途径入侵计算机。在入侵之后,往往可以实现病毒扩散,感染未感染计算机,进而造成大面积瘫痪等事故。随着网络信息技术的不断发展,在短时间之内,病毒能够实现较大范围的恶意入侵。因此,在计算机病毒的安全防御中,如何面对快速的病毒传染,成为有效防御病毒的重要基础,也是构建防御体系的关键。
  • 寄生性
    计算机病毒还具有寄生性特点。计算机病毒需要在宿主中寄生才能生存,才能更好地发挥其功能,破坏宿主的正常机能。通常情况下,计算机病毒都是在其它正常程序或数据中寄生,在此基础上利用一定媒介实现传播,在宿主计算机实际运行过程中,一旦达到某种设置条件,计算机病毒就会被激活,随着程序的启动,计算机病毒会对宿主计算机文件进行不断辅助、修改,使其破坏作用得以发挥。
  • 可执行性
    计算机病毒与其他合法程序一样,是一段可执行程序,但它不是一个完整的程序,而是寄生在其他可执行程序上,因此它享有一切程序所能得到的权力。
  • 可触发性
    病毒因某个事件或数值的出现,诱使病毒实施感染或进行攻击的特征。
  • 攻击的主动性
    病毒对系统的攻击是主动的,计算机系统无论采取多么严密的保护措施都不可能彻底地排除病毒对系统的攻击,而保护措施充其量是一种预防的手段而已。
  • 病毒的针对性
    计算机病毒是针对特定的计算机和特定的操作系统的。
DDOS分布式拒绝服务攻击
  • 可以这么理解,凡是能导致合法用户不能够访问正常网络服务的行为都算是拒绝服务攻击。拒绝服务攻击的目的非常明确,就是要阻止合法用户对正常网络资源的访问,从而达成攻击者不可告人的目的。

  • 分布式拒绝服务攻击是指处于不同位置的多个攻击者同时向一个或数个目标发动攻击,或者一个攻击者控制了位于不同位置的多台机器并利用这些机器对受害者同时实施攻击。

攻击原理:

  • DDoS是一种基于DoS特殊形式的拒绝服务攻击,是一种分布的、协同的大规模攻击方式。单一的DoS攻击一般是采用一对一方式的,它利用网络协议和操作系统的一些缺陷,采用欺骗和伪装的策略来进行网络攻击,使网站服务器充斥大量要求回复的信息,消耗网络带宽或系统资源,导致网络或系统不胜负荷以至于瘫痪而停止提供正常的网络服务。与DoS攻击由单台主机发起攻击相比较,分布式拒绝服务攻击DDoS是借助数百、甚至数千台被入侵后安装了攻击进程的主机同时发起的集团行为。
ARP内网渗透
  • 协议欺骗是指通过对通信双方使用协议弱点的利用,冒充其中一方与另一方进行通信的行为。对于广播式网络,只要更改自己网卡的接收模式为混杂模式,理论上就可以截获所有内网上的通信。对于交换式网络环境,如果要截获网络上不属于自己的通信,可以通过协议欺骗来实现。

  • 内网渗透指的是在网络内部的渗透,在本地局域网内部对网内的其他系统进行渗透的过程。基于ARP欺骗的内网渗透指网络攻击者利用ARP欺骗截获不属于自身的通信,并从这一条件中获取更多利益的行为。

ARP地址解析协议是一个位于TCP/IP协议栈中的低层协议,负责将局域网中某个IP地址解析成对应的MAC地址。IP地址到MAC地址的映射关系主要是靠ARP协议来实现的。对于网络主机,这个映射关系存放在ARP高速缓存中。

ARP协议工作原理:

  1. 网络通信源机器向网络广播ARP请求包,请求网络通信目的机器IP所对应的MAC地址
  2. 然后使用该IP的机器会向请求方发送一个含有其MAC地址的ARP回应包,这样请求方就知道向哪个MAC地址(目的主机)发送数据

ARP协议存在以下安全问题:无连接、无认证、动态性、广播

ARP协议欺骗的步骤和方法:

  • 主机在不知道目的IP对应的MAC地址时,进行ARP广播请求,入侵者可以在接收到该ARP请求后以自己的MAC地址应答,进行假冒。
  • 由于被假冒机器所发送的ARP应答有可能比入侵者发送的应答晚到达请求主机,为了确保请求主机的缓存中绝大部分时间存放的是入侵者的MAC地址,可以在收到ARP请求后稍微延迟一段时间再发送一遍ARP应答。
  • 有些系统会向自己缓存中的地址发送非广播的ARP请求来更新自己的缓存。在交换网络环境下,如果请求主机缓存中已存有正确的主机MAC地址,入侵者就不能用以上接收请求然后应答的方法来更换被攻击主机缓存内容。由ARP弱点分析可知,应答可以随意发送,不一定要在请求之后。

无漏洞渗透技术是相对于利用软件漏洞进行网络渗透的技术来说的。在以太网中,只要被渗透机器在网络中传输的数据包经过本地网卡,在本地就可以截获其数据包中的敏感信息,并可以更改数据包内容、替换数据包中的传输实体,使得被渗透机器上的敏感信息泄露,并可以使其在接收到被更改过的数据包之后,产生更多的损失。对内网中的机器进行渗透,不一定需要软件漏洞的存在。将这种不需要软件漏洞进行渗透的技术称为无漏洞渗透技术。

对数据包处理的方式主要有两种,敏感信息的截取和传输实体的获取与替换

  • 报文中敏感信息的获取
    对于明文传输的面向连接和非面向连接的协议,在截获报文之后,对报文中传输的信息进行还原,并提取其中的敏感信息。
  • 传输实体的获取与替换
    明文传输的面向链接的协议中有很多协议支持文件实体的传输,如HTTP协议、FTP协议、SMTP协议。

在交换式网络中防范ARP欺骗主要有以下几种方法:

  1. 使用静态ARP表
    在关键设备如网关、防火墙和边界路由器等设置静态的ARP,不要让系统刷新设定好的ARP转换表。在网关中使用静态ARP表,则可以避免通过网关进行ARP欺骗。

  2. 使用ARP服务器
    在内部网络中设置ARP服务器,通过该服务器查找自己的ARP转换表来响应其他机器的ARP广播,而禁止其他系统响应ARP请求。

  3. 定期轮询
    管理员定期轮询网络内部的IP地址与MAC地址的对应关系,通过与已有记录的比较来发现ARP欺骗。

  4. 主动出击
    主动出击,用一些安全工具在网络中进行检测,可以检测到本地网络上的ARP欺骗报文。

  5. 使用加密通信
    无漏洞渗透的报文中敏感信息的获取和传输实体的替换主要针对非加密通信,将内网的通信进行加密可以有效地防止这类攻击。

  6. 划分虚拟局域网
    欺骗攻击无法跨网段工作,将网络进行越细致地分段,无漏洞渗透成功的可能性就越小。将受信任主机设置在同一社区VLAN中,将绝密性主机设置在隔离VLAN中,可以有效地防止无漏洞渗透的渗入。

  7. 提高防范意识
    目前,很多容易被攻击者注意的网站,都采用了HTTPS代替了HTTP协议来传输网页和交易数据,已经避免了这类攻击发生的可能。但对于那些没有采用加密通信的web站点来说,web链接替换攻击依然有成功的可能。对这种攻击可以从浏览器终端用户的角度来防范,使用一些较为安全的浏览器来访问网站。
    在这里插入图片描述

在这里插入图片描述

1. 操作系统的概念及功能

操作系统(OS)是控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配;以提供给用户和其他软件方便的接口和环境;它是计算机系统中最基本的系统软件。
在这里插入图片描述
操作系统的功能:

  1. 操作系统是系统资源的管理者
  2. 向上层提供方便易用的服务
  3. 是最接近硬件的一层软件

我们通过使用QQ软件的例子来体会操作系统的功能:
在这里插入图片描述
1.操作系统作为管理者需要做的事情
在这里插入图片描述
2.理解向上层提供方便易用的服务
封装思想:操作系统把一些丑陋的硬件功能封装成简单易用的服务,使用户能更方便地使用计算机,用户无需关心底层硬件的原理,只需要对操作系统发出命令即可。

那么具体提供方便易用的服务有哪些呢?
(1)GUI:图形化用户接口
用户可以使用形象的图形界面进行操作,而不再需要记忆复杂的命令,参数。
(2)命令接口
在这里插入图片描述
3.操作系统作为最接近硬件的层次
在这里插入图片描述
小结:
在这里插入图片描述

2. 操作系统的四个特征

操作系统有 并发,共享,虚拟,异步 这四个基本特征,其中并发和共享是两个最基本的特征,二者互为存在条件。

(1)并发:指两个或多个事件在同一时间间隔内发生。在这些事件宏观上是同时发生的。
注意:这里通常与并行混淆,并行是指两个或多个事件在同一时刻同时发生。

为了方便理解,我们来个例子!老渣和小渣
在这里插入图片描述

  1. 操作系统的并发性:指计算机系统中同时运行着多个程序,这些程序宏观上看是同时运行着的,而微观上看是交替运行的。操作系统就是伴随着多道程序技术而出现的。因此,操作系统和程序并发是一起诞生的。
  2. 单核CPU同一时刻只能执行一个程序,各个程序只能并发的执行。多核CPU同一时刻可以同时执行多个程序,多个程序可以并行的执行。

(2)共享:即资源共享,是指系统中地资源可供内存中多个并发执行地进程共同使用。

  • 两种资源共享方式:互斥共享方式与同时共享方式

  • 互斥共享方式:系统中的某些资源,虽然可以提供给多个进程使用,但一个时间段内只允许一个进程访问该资源。

  • 同时共享方式:系统中的某些资源,允许一个时间段内由多个进程同时对它们进行访问。所谓的同时往往是宏观上的,而在微观上,这些进程可能是交替地对该资源进行访问的(即分时共享)。

举个例子:
互斥共享方式:比如在使用QQ和微信视频,同一时间段内摄像头只能分配给其中一个进程。
同时共享方式:使用QQ发送文件A,同时使用微信发送文件B。宏观上看,两边都在同时读取并发送文件,说明两个进程都在访问硬盘资源,从中读取数据。微观上看,两个进程是交替着访问硬盘的。

  • 并发和共享的关系
    在这里插入图片描述
1
并发和共享是两个最基本的特征,二者互为存在条件

(3)虚拟:是指把一个物理上的实体变为若干个逻辑上的对应物。物理实体(前者)是实际存在的,而逻辑上对应物(后者)是用户感受到的。
(4)异步:指多道程序环境下,允许多个程序并发执行,但由于资源有限,进程的执行不是一贯到底的,而是走走停停,以不可预知的速度向前推进,这就是进程的异步性。
在这里插入图片描述
小结:
在这里插入图片描述

3. 操作系统的运行机制

在这里插入图片描述
程序是如何运行的:
在这里插入图片描述
注意:指令就是处理器(CPU)能识别,执行的最基本命令。

1
程序运行的过程其实就是CPU执行一条一条的机器指令的过程
1
内核是操作系统最重要最核心的部分,也是最接近硬件的部分
  • 特权指令与非特权指令
  • 应用程序只能使用非特权指令,如:加法指令,减法指令等。
    操作系统内核作为管理者,有时会让CPU执行一些特权指令,如:内存清零指令。

在CPU设计和生产的时候划分了特权指令和非特权指令,因此CPU执行一条指令前就能判断出其类型。

内核态与用户态
CPU既然能判断出指令类型,但是它怎么区分此时正在运行的是内核程序或者是应用程序呢?

其实,CPU有两种状态,即内核态和用户态

  • 处于内核态时,说明此时正在运行的是内核程序,此时可以执行特权指令
  • 处于用户态时,说明此时正在运行的是应用程序,此时只能执行非特权指令

CPU中有一个寄存器叫程序状态字寄存器(PSW),其中有个二进制位,1表示内核态,0表示用户态。
在这里插入图片描述
&emsp;&emsp;内核程序在合适的时候主动让出CPU,从核心态切换到用户态,即将让应用程序在CPU上运行。一旦CPU处于用户态时,我们的应用程序将会在此用户态下运行,此时CPU会执行应用程序中一条又一条的指令。但是如果此时在应用程序中恶意植入了一条特权指令,那么此时CPU当执行这条特权指令时,会检查自己的PSW,发现自己依然处于用户态却要执行这条特权指令,那么这个非法事件会引发一个中断信号发送给CPU,使得CPU强行从用户态转成核心态,并且CPU会拒绝执行该特权指令以及暂停执行后面的其他指令。转而CPU会执行一个处理中断信号的内核程序。
&emsp;&emsp;换句话说,在发生中断信号之后,中断使操作系统内核再次夺回CPU的控制权,从用户态再次切换到核心态,操作系统会对引发中断的事件进行处理,处理完了再把CPU使用权交给别的应用程序。

  1. 当操作系统从内核态切换到用户态时:
    在这里插入图片描述
  2. 当操作系统从用户态切换到内核态时:
    在这里插入图片描述
    从用户态切换到内核态的前提是由中断引发的,除了非法使用特权指令之外,还有许多事件会触发中断信号。一个共性是,但凡需要操作系统介入的工作地方,都会触发中断信号。

小结:
在这里插入图片描述

4. 操作系统的中断与异常

在这里插入图片描述
我们知道在CPU上会运行两种程序,一种是操作系统的内核程序,一种是应用程序。内核程序是整个系统的管理者,在计算机启动时,CPU处于内核态,只是在合适的情况下,操作系统内核会把CPU的使用权主动让给应用程序。一个应用程序在上CPU运行之后,它就会一直运行下去,除非发送中断。

(1)中断的作用

中断是让操作系统内核夺回CPU使用权的唯一途径,使CPU由用户态变为内核态。

  • 那么问题来了,如果没有中断机制,那么一旦应用程序上CPU运行,CPU就会一直运行这个应用程序。如果CPU一直运行同一个应用程序,将如何实现多道程序并发这个事情呢?
    在这里插入图片描述

(2)中断的类型

中断分为内中断与外中断

  • 内中断:与当前执行的指令有关,中断信号来源于 CPU内部
  • 外中断:与当前执行的指令无关,中断信号来源于 CPU外部

我们来看内中断的例子:

  1. 试图在用户态下执行特权指令
    在这里插入图片描述
  2. 执行除法指令时发现除数为0
    总之,若当前执行的指令是非法的,或执行指令的参数是非法的,那么就会引发一个中断信号。
  3. 有时候应用程序想请求操作系统内核的服务,此时会执行一条特殊的指令——陷入指令,该指令会引发一个内部中断信号。

执行陷入指令,意味着应用程序主动地将CPU控制权还给操作系统内核,系统调用就是通过陷入指令完成的

1
陷入指令是一种特殊的指令不是特权指令

我们再来看外中断的例子:

  1. 时钟中断——由时钟部件发来的中断信号
    在这里插入图片描述系统中想要并发的运行两个应用程序,首先应用程序1运行在用户态,CPU执行其指令,假如当CPU执行两条指令后,时钟部件检测已经执行过了50ms,于是时钟部件会给CPU发送一个中断信号,此时CPU会暂停执行应用程序1,转而去执行处理时钟中断的内核程序,去处理这个中断信号。
    在这里插入图片描述
    此时CPU由用户态转为内核态,在处理中断信号的内核程序中,操作系统内核发现应用程序1已经用了50ms,为了公平起见,操作系统内核决定接下来让另一个应用程序2上CPU运行。
    CPU又从内核态转为用户态,CPU执行应用程序2的指令,同样的,时钟部件检测已经过了50ms,于是时钟部件再次会给CPU发送一个中断信号,此时CPU又会暂停执行应用程序2,转而去执行处理时钟中断的内核程序。
    在处理中断信号的内核程序中,操作系统内核发现应用程序2已经用了50ms,为了公平起见,操作系统内核决定接下来又让应用程序1上CPU运行,于是将CPU的使用权让给应用程序1,然后应用程序1就会执行它之后的未执行的指令,依次往复。
  2. I/O中断——由输入/输出设备发来的中断信号
    每一条指令执行结束时,CPU都会例行检查是否有外部中断信号
    在这里插入图片描述

(3)中断机制的基本原理

不同的中断信号,需要用不同的中断处理程序来处理

当CPU检测到中断信号后,会根据中断信号的类型去查询中断向量表,以此来找到相应的中断处理程序在内存中的存放位置。
在这里插入图片描述
小结:
在这里插入图片描述

5. 进程的自述

程序:是静态的,就是个存放在磁盘里的可执行文件,就是一系列的指令集合。比如在Windows中就是以后缀为.exe的可执行文件

进程:是动态的,是程序的一次执行过程

同一个程序多次执行会对应多个进程

进程的组成部分——(PCB)
那么既然操作系统是这些进程的管理者,它要怎么区分各个进程呢?

  • 当进程被创建时,操作系统会为该进程分配一个唯一的,不重复的身份证号(PID)
  • 操作系统要记录PID,进程所属用户ID(UID),操作系统可以根据PID,UID这些基本的进程描述信息,让操作系统区分各个进程。
  • 操作系统要记录给进程分配了哪些资源(如:分配了多少内存,正在使用哪些I/O设备,正在使用哪些文件),这些信息可以用于实现操作系统对资源的管理。
  • 操作系统要记录进程的运行情况(如:CPU使用时间,磁盘使用情况,网络流量使用情况等),这些信息可以实现操作系统对进程的控制,调度。

那么既然操作系统要记录这么多信息,那该如何整理呢?

  • 其实这些信息都被保存在一个数据结构PCB中,即进程控制块。总之,操作系统需要对各个并发运行的进程进行管理,但凡管理时所需要的信息,都会被放在PCB中。
    在这里插入图片描述
    PCB是进程存在的唯一标志,当进程被创建时,操作系统为其创建PCB,当进程结束时,会收回其PCB。

进程的组成部分:PCB,程序段,数据段
在这里插入图片描述

这里需要明确的是PCB是给操作系统用的,而程序段,数据段是给进程自己用的。

什么是程序的运行?

  1. 我们知道程序在运行之前,都需要通过编译器将代码“翻译”成二进制的机器指令,一条高级语言的代码翻译过来可能会对应多条机器指令。那么程序运行的过程其实就是CPU执行一条一条的机器指令的过程。
  2. 当我们写完一个程序之后,经过编译链接等一系列的步骤,最终会形成一个后缀为.exe的可执行文件,这个可执行文件一直是存放在硬盘当中的,在系统运行这个可执行文件前需要把程序放入内存中,同时操作系统会为其创建相应的PCB,以及一些程序段也会调入内存中,我们知道程序段中包含了一条一条的指令,我们的CPU就是从内存中取出这些指令。此外,程序是基于代码的,那代码的一些运算结果同样需要被调用。换句话说,数据段也要被调入到内存之中,数据段包含了运行过程中产生的,需要使用的各种数据,比如我们定义了某些变量,就是包含在数据段里的。
    在这里插入图片描述
    总结:一个进程的实体(进程映像)是由PCB,程序段,数据段组成的

进程的深度解读:
在这里插入图片描述

1
PCB是进程存在的唯一标志!

进程的特征:
在这里插入图片描述
注意:
(1)进程是动态的,动态性是进程最基本的特征
(2)异步性会导致并发程序执行结果的不确定性

小结:
在这里插入图片描述

6. 进程的状态与转换

在这里插入图片描述
进程的状态——创建态,就绪态
(1)进程正在被创建时,它的状态是创建态,在这个阶段操作系统会为进程分配资源,初始化PCB。
(2)当进程创建完成后,便进入就绪态,处于就绪态的进程已经具备运行条件,但由于没有空闲CPU,就暂时不能运行。
在这里插入图片描述
进程的状态——运行态
在一个进程中可能会有很多个进程都处于就绪态,当CPU空闲时,操作系统就会选择一个就绪进程,让它上处理机运行。
在这里插入图片描述
如果一个进程此时在CPU上运行,那么这个进程处于运行态。换句话说,此时CPU会执行该进程对应的程序(执行指令序列)。

进程的状态——阻塞态
在进程运行的过程中,可能会请求等待某个事件的发生,如等待某种系统资源的分配,或者等待其他进程的响应。
在这个事件发生之前,进程无法继续往下执行,此时操作系统会让这个进程下CPU,并让它进入阻塞态。当CPU空闲时,又会选择另一个就绪态进程上CPU运行。

进程的状态——终止态
假设此时运行在CPU上的进程1已经运行结束了,在运行结束时会发出一个(exit)的系统调用,请求操作系统终止该进程。
在这里插入图片描述
此时该进程会进入终止态,操作系统会让该进程下CPU,并回收内存空间等资源,最后还是要回收该进程的CPU。当终止进程的工作完成之后,这个进程就彻底消失了。
在这里插入图片描述
进程状态的转换(图解):
在这里插入图片描述
进程的状态:
在这里插入图片描述
进程PCB中,会有一个变量state来表示进程的当前状态。如:1表示创建态,2表示就绪态,3表示运行态。为了对同一个状态下的各个进程进行统一的管理,操作系统会将各个进程的PCB组织起来。

  • 那么如何将各个进程的PCB组织起来?

链接方式与索引方式

  1. 链接方式
    (1)执行指针——指向当前处于运行状态(执行态)的进程
    (2)就绪队列指针——指向当前处于就绪状态的进程
    (3)阻塞队列指针——指向当前处于阻塞态的进程

在这里插入图片描述

  1. 索引方式
    在这里插入图片描述
    在这里插入图片描述

小结:
在这里插入图片描述

7. 进程控制及原语

在这里插入图片描述
(1)什么是进程控制?
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程,撤销已有进程,实现进程状态转换等功能。

简单的说:进程控制就是要实现进程状态转换

在这里插入图片描述
(2)如何实现进程控制?
进程实现进程不同状态的转换是需要用“原语”来实现的
在这里插入图片描述

  • 原语是一种特殊的程序,它的执行具有原子性。也就是说,这段程序的运行必须一气呵成,不可中断。

那么思考一个问题,为什么进程控制(状态转换)的过程要一气呵成?
在这里插入图片描述
我们假设此时进程2等待的事件发生,则操作系统中,负责进程控制的内核程序至少做这样两件事:
(1)将PCB2的(state)设为1
(2)将PCB2从阻塞队列放到就绪队列

如果此时我们已经将PCB2的(state)设为1,如图:
在这里插入图片描述
但是恰好此时收到了中断信号,那么PCB2的(state)=1本来表示就绪状态却被放在阻塞队列里,这时如果不能一气呵成,就有可能导致操作系统中的某些关键数据结构信息不统一的情况,这会影响操作系统进行别的管理工作。所以这就是为什么进程状态的转换必须一气呵成!

(3)如何实现原语的原子性?
原语的执行具有原子性,即执行过程只能一气呵成,期间不允许被中断。可以用关中断指令和开中断指令这两个特权指令实现原子性。

CPU正常情况下运行:
在这里插入图片描述
CPU执行了关中断与开中断指令:
在这里插入图片描述
思考:如果这两个特权指令允许用户程序使用的话,会发生什么情况?

  • 这样就意味着我们可以在程序的开头植入一个关中断指令,然后在程序的末尾植入一个开中断指令,如此只要该程序上CPU运行,那程序就可以一直霸占CPU,显然这是不应该发生的。

所以开中断与关中断是两条特权指令

(4)进程控制相关的原语

  1. 进程的创建
    在这里插入图片描述
  2. 进程的终止
    在这里插入图片描述
  3. 进程的阻塞和唤醒
    在这里插入图片描述
  4. 进程的切换
    在这里插入图片描述
    总结:无论哪个进程控制原语,要做的无非是三件事情:
    1.更新PCB中的信息,修改进程状态(state)保存和恢复运行环境
    2.将PCB插入合适的队列
    3.分配和回收资源

小结:
在这里插入图片描述

8. 线程与多线程模型

在这里插入图片描述
在没有引入进程之前,系统中的各个程序只能串行执行,换句话说我们如果想要一边听音乐,一边玩QQ是不可以的。在引入进程之后,我们可以一边听音乐,一边玩QQ。但是问题来了,QQ中许多的功能,比如视频,文字聊天,传送文件等,可我们知道进程是程序的一次执行,那么这些功能显然不可能是由一个程序顺序处理就能实现的。
在这里插入图片描述
因此我们引入了线程的概念:
在这里插入图片描述
传统的进程是程序执行流的最小单位,但在引入线程后,线程成为了程序执行流的最小单位。

同样,我们可以把线程理解为轻量级的进程

引入线程之后,不仅是进程之间可以并发,进程内的各线程之间也可以并发,进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务,这时进程只作为除CPU之外的系统资源的分配单元

引入线程机制后,有什么变化?
在这里插入图片描述
线程的属性:
在这里插入图片描述
线程的实现方式与多线程模型
在这里插入图片描述
(1)用户级线程
在这里插入图片描述
很多编程语言提供了强大的线程库,可以实现线程的创建,销毁,调度等功能。

  • 那么线程的管理工作由谁来完成的?

线程的管理工作由应用程序提供的线程库来完成的,并不是由操作系统来完成的。

  • 线程切换是否需要CPU从用户态转换为内核态?

    其实线程切换是由应用程序的线程库自己完成的,在用户态下就可以完成线程切换,不需要操作系统的切换。

  • 操作系统是否能意识到用户级线程的存在?

不能,只有用户才能感知到用户级线程的存在。

用户级线程的优缺点:

  • 优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
  • 缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并行运行。

(2)内核级线程
内核级线程,由操作系统支持的线程
在这里插入图片描述
注意:内核级线程与用户级线程不同

  1. 内核级线程的管理工具由操作系统内核完成
  2. 线程调度,切换等工作都由内核负责,因此内核级线程的切换必须需要在核心态下才能完成
  3. 操作系统会为每个内核级线程建立相应的TCB(线程控制块),通过TCB对线程进行管理。内核级线程就是从操作系统内核视角看能看到的线程。

内核级线程的优缺点:

  • 优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
  • 缺点:一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。

多线程模型
之前我们讨论了用户级线程与内核级线程的优缺点,那么我们是否可以将这两种线程方式结合起来呢?
(1)一对一模型
在这里插入图片描述
(2)多对一模型
在这里插入图片描述
(3)多对多模型
在这里插入图片描述

1
2
3
4
5
总结:内核级线程与用户级线程
(1)用户级线程是代码逻辑的载体
(2)内核级线程是运行机会的载体
(3)内核级线程才是处理机分配的单位
(4)一段代码逻辑只有获得了运行机会才能被CPU执行

内核级线程中可以运行任意一个有映射关系的用户级线程代码,只有两个内核级线程中正在运行的代码逻辑都阻塞时,这个进程才会阻塞。

小结:
在这里插入图片描述

9. 处理机调度

在这里插入图片描述
调度的基本原理:
当我们去银行取钱时,我们或许需要排队等待,作为普通客户,一般银行遵循着先到先服务的顺序。但是如果有VIP客户的话,他们未必需要排队就可以优先被服务。
在这里插入图片描述
当有一堆任务要处理,但由于资源有限,任务没法同时处理这就需要确定某种规则来决定处理这些任务的顺序,这就是调度研究的问题。

高级调度

作业:一个具体的任务

用户向系统提交一个作业 = 用户让操作系统启动一个程序(来处理一个具体的任务)

但是问题来了,我们的内存空间有限,有时无法将用户提交的作业全部放入内存,那该这么办呢?

  • 所谓高级调度(作业调度),是按一定的原则从外存的作业后备队列中挑选一个作业调入内存,并创建进程。每个作业只调入一次,调出一次。作业调入时会建立PCB,调出时才撤销PCB。

低级调度

  • 指按照某种策略从就绪队列中选择一个进程,将处理机分配给它。
  • 进程调度是操作系统中最基本的一种调度,在一般的操作系统中都必须配置进程调度。

在这里插入图片描述
中级调度
内存不够时,可将某些进程的数据调出外存,等内存空闲或者进程需要运行时再重新调入内存。暂时调到外存等待的进程状态为挂起状态,被挂起的进程PCB会被组织成挂起队列。
在这里插入图片描述

  • 所谓中级调度(内存调度)是指按照某种策略决定将哪个处于挂起状态的进程重新调入内存。
  • 一个进程可能会被多次调出,调入内存,因此中级调度发生的频率要比高级调度更高。

挂起状态
在这里插入图片描述
在这里插入图片描述
三层调度的联系与对比:
在这里插入图片描述
小结:
在这里插入图片描述

10. 进程调度的时机

在这里插入图片描述
进程调度的时机
我们知道进程调度(低级调度),就是按照某种算法从就绪队列中选择一个进程为其分配处理机。那什么时候进行进程调度与切换的情况?
(1)当前运行的进程主动放弃处理机
(2)当前运行的进程被动放弃处理机

在这里插入图片描述

  • 进程在操作系统内核程序临界区中不能进行调度与切换
  • 进程处于临界区时不能进行处理机调度

临界资源:是指一个时间段内只允许一个进程使用的资源,各进程需要互斥的访问临界资源。

临界区:指访问临界资源的这段代码。

  • 内核程序临界区一般是用来访问某种内核数据结构的,比如进程的就绪队列(由各就绪进程的PCB组成)

举两个例子:
(1)内核程序临界区访问的临界资源需要尽快的释放,不可以进行进程调度和切换
在这里插入图片描述
在这里插入图片描述
(2)普通临界区访问的临界资源时可以进行进程调度和切换
在这里插入图片描述
进程调度的方式

  1. 非剥夺调度方式,又称非抢占式。即只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达,当前进程依然会继续使用处理机,直到该进程终于或主动要求进入阻塞状态。
  2. 剥夺调度方式,又称抢占方式。当一个进程正在处理机上执行时,如果有一个更重要或更紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给更重要紧迫的那个进程。
  • 非剥夺调度方式实现简单,系统开销小但是无法及时处理紧急任务,适合于早期的批处理系统。

  • 剥夺调度方式可以优先处理更紧急的进程,也可以实现让各进程按时间片轮流执行的功能,适合于分时操作系统,实时操作系统。

进程的切换与过程

  • 狭义的进程调度指的是从就绪队列中选中一个要运行的进程
  • 广义的进程调度包含了选择一个进程和进程切换两个步骤

进程切换是指一个进程让出处理机,由另一个进程占用处理机的过程。
在这里插入图片描述
小结:
在这里插入图片描述

11. 调度算法的评价指标

在这里插入图片描述
CPU的利用率

CPU利用率:是指CPU忙碌的时间占总时间的比例
在这里插入图片描述
来道例题:
在这里插入图片描述
系统吞吐量

系统吞吐量是指:单位时间内完成作业的数量
在这里插入图片描述
来道例题:
在这里插入图片描述
周转时间

周转时间是指:从作业被提交给系统开始,到作业完成为止的这段时间间隔

  • 周转时间包括四个部分:
  1. 作业在外存后备队列上等待作业调度(高级调度)的时间
  2. 进程在就绪队列等待进程调度(低级调度)的时间
  3. 进程在CPU上执行的时间
  4. 进程等待I/O操作完成的时间

周转时间与平均周转时间
在这里插入图片描述
带权周转时间

对于周转时间相同的两个作业,实际运行时间长的作业在相同时间内被服务的时间更多,带权周转时间更小,用户满意度更高。
在这里插入图片描述
对于实际运行时间相同的两个作业,周转时间短的带权周转时间更小,用户满意度更高。

1
2
3
注意:
带权周转时间必然大于(等于)1
带权周转时间与周转时间都是越小越好

平均带权周转时间
在这里插入图片描述
等待时间

等待时间:指进程(作业)处于等待处理机状态时间之和,等待时间越长,用户满意度越低。
在这里插入图片描述
一个作业总共需要被CPU服务多久,被I/O设备服务多久一般是确定不变的,因此调度算法其实只会影响作业/进程的等待时间。

响应时间

响应时间:指从用户提交请求到首次产生响应所用的时间。

小结:
在这里插入图片描述

12. 调度算法思想及规则(上)

在这里插入图片描述
一. 先来先服务调度算法(FCFS)

(1)算法思想:

  • 主要是从公平的角度考虑(类似于我们生活中排队买东西的例子)

(2)算法规则:

  • 按照作业/进程到达的先后顺序进行服务
  • 按照到达的先后顺序调度,事实上就是等待时间越久的越优先得到服务

(3)用于作业(进程)调度:

  • 用于作业调度时,考虑的是哪个作业先到达后备队列
  • 用于进程调度时,考虑的是哪个进程先到达就绪队列

例题:
在这里插入图片描述
(4)先来先服务调度算法的优缺点:

  • 优点:公平,算法实现简单
  • 缺点:排在长作业(进程)后面的短作业需要等待很长时间,带权周转时间很大,对短作业来说用户体验不好
  • 即先来先服务调度算法对长作业有利,对短作业不利
  • 先来先服务调度算法是不会导致饥饿的

(5)先来先服务调度算法概述:
在这里插入图片描述
二. 短作业优先调度算法(SJF)

(1)算法思想:

  • 追求最少的平均等待时间,最少的平均周转时间,最少的平均带权周转时间

(2)算法规则:

  • 最短的作业/进程优先得到服务(所谓”最短“,是指要求服务时间最短)

(3)用于作业(进程)调度:

  • 即可用于作业调度,也可用于进程调度
  • 用于进程调度时称为”短进程优先算法(SPF)

(4)是否可抢占:

  • SJF和SPF是非抢占式的算法
  • 但是也有抢占式的版本——最短剩余时间优先算法(SRTN)

例题:
使用非抢占式的短作业优先调度算法SJF
在这里插入图片描述
使用抢占式的短作业优先调度算法SRTN
在这里插入图片描述
在这里插入图片描述
使用短作业优先算法需要注意:
在这里插入图片描述
(5)短作业优先调度算法的优缺点:

  • 优点:最短的平均等待时间,平均周转时间
  • 缺点:对短作业有利,对长作业不利。可能产生饥饿现象。

(6)短作业优先调度算法概述:
在这里插入图片描述
对FCFS和SJF两种算法的思考:

FCFS算法是在每次调度的时候选择一个等待时间最长的作业(进程)为其服务。但是没有考虑到作业的运行时间,因此导致了对短作业不友好的问题。

SJF算法是选择一个执行时间最短的作业为其服务。但是又完全不考虑各个作业的等待时间,因此导致了对长作业不友好的问题,甚至还会造成饥饿问题。

那么,我们能不能设计一个算法,即考虑到各个作业的等待时间,也能兼顾运行时间?

三. 高响应比优先调度算法(HRRN)

(1)算法思想

  • 要综合考虑作业/进程的等待时间和要求服务的时间

(2)算法规则

  • 在每次调度时先计算各个作业(进程)的响应比,选择响应比最高的作业(进程)为其服务
    在这里插入图片描述

(3)用于作业(进程)调度

  • 即可用于作业调度,也可用于进程调度

(4)是否可抢占?

  • 非抢占式的算法。因此只有当前运行的作业/进程主动放弃处理机时,才需要调度,才需要计算响应比

(5)高响应比优先算法优缺点

  • 优点:综合考虑了等待时间和运行时间(要求服务时间)
  • 等待时间相同时,要求服务时间短的优先(SJF的优点) 要求服务时间相同时,等待时间长的优先(FCFS的优点)
  • 缺点:对于长作业来说,随着等待时间越来越久,其响应比也会越来越大,从而避免了长作业饥饿的问题

例题:
在这里插入图片描述
(6)高响应比优先算法概述
在这里插入图片描述
小结:
在这里插入图片描述

13. 调度算法思想及规则(下)

在这里插入图片描述
四. 时间片轮转调度算法(RR)

(1)算法思想

  • 公平的,轮流地为各个进程服务,让每个进程在一定时间间隔内都可以得到响应。

(2)算法规则

  • 按照各进程到达就绪队列的顺序,轮转让各个进程执行一个时间片。若进程未在一个时间片内执行完,则剥夺处理机,将进程重新放到就绪队列队尾重新排队。

(3)用于作业(进程)调度

  • 用于进程调度(只有作业放入内存建立了相应的进程后,才能被分配处理机时间片)

(4)是否可抢占?

  • 若进程未能在时间片内运行完,将被强行剥夺处理机使用权,因此时间片轮转调度算法属于抢占式的算法。由时钟装置发出时钟中断来通知CPU时间片已到。

(5)时间片轮转调度算法的优缺点

  • 优点:公平:响应快,适用于分时操作系统,不会导致饥饿
  • 缺点:由于高频率的进程切换,因此有一定开销;不区分任务的紧急程度

例题:
在这里插入图片描述
1.当时间片大小为2时:在这里插入图片描述
时刻流程图:
(1)0-5时刻(2)6-11时刻(3)12-16时刻
在这里插入图片描述
2.当时间片大小为5时:
在这里插入图片描述
同一道例题,我们对比用先来先服务调度算法
在这里插入图片描述

  • 我们发现:如果时间片太大,使得每个进程都可以在一个时间片内就完成,则时间片轮转调度算法退化为先来先服务调度算法,并且会增大进程响应时间。因此时间片不能太大。
  • 如果时间片太小,会导致进程切换过于频繁,系统会花大量的时间来处理进程切换,从而导致实际用于进程执行的时间比例减少。可见时间片也不能太小。

(6)时间片轮转调度算法概述
在这里插入图片描述
五. 优先级调度算法

(1)算法思想

  • 随着计算机的发展,特别是实时操作系统的出现,越来越多的应用场景需要根据任务的紧急程度来决定处理顺序

(2)算法规则

  • 每个作业(进程)有各自的优先级,调度时选择优先级最高的作业/进程

(3)用于作业(进程)调度

  • 既可以用与作业调度,也可以用于进程调度。甚至还会用于I/O调度。

(4)是否可抢占?

  • 抢占式,非抢占式都有。非抢占式只需在进程主动放弃处理机时进行调度即可。而抢占式还需要在就绪队列变化时,检查是否会发生抢占。

(5)优先级调度算法的优缺点

  • 优点:用优先级区分紧急程度,重要程度,适合于实时操作系统。可灵活的调整对各种作业(进程)的偏好程度。
  • 缺点:若源源不断地有高优先级进程到来,则可能导致饥饿。

例题:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(6)优先级调度算法概述
在这里插入图片描述
思考:
在这里插入图片描述
六. 多级反馈队列调度算法(简述)
在这里插入图片描述
例题:
在这里插入图片描述
小结:
在这里插入图片描述

14. 进程同步与进程互斥

在这里插入图片描述
什么是进程同步?

我们来看一个管道通信的例子:
在这里插入图片描述

异步性是指:各并发执行的进程以各自独立的,不可预知的速度向前推进

  • 同步亦称直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而产生的制约关系。
  • 进程间的直接制约关系就是源于它们之间的相互合作。

什么是进程互斥?
进程的并发需要共享的支持,各个并发执行的进程不可避免地需要共享一些系统资源。
在这里插入图片描述
对临界资源地互斥访问,可以在逻辑上分为如下四个部分:
1.进入区 2.临界区 3.退出区 4.剩余区

注意:
临界区是进程中访问临界资源的代码段
进入区和退出区是负责实现互斥的代码段

为了实现对临界资源的互斥访问,同时保证系统整体性能,需要遵循以下原则:
在这里插入图片描述
小结:
在这里插入图片描述

15. 生产者消费者问题

问题描述:

系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。(这里的产品理解为某种数据)

1.整理思路
(1)生产者,消费者共享一个初始为空,大小为n的缓冲区。
如图:缓冲区的大小为5
在这里插入图片描述
只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待。

(2)如果缓冲区已经满了,生产者进程依然向缓冲区里写数据,此时生产者进程必须等待,当缓冲区中有空的时候才能向缓冲区里写数据。
在这里插入图片描述
(3)只有缓冲区不空时,消费者才能从中取出产品,否则必须等待。
在这里插入图片描述
(4)缓冲区是临界资源,各进程必须互斥地访问。
在这里插入图片描述
2.关系分析
在这里插入图片描述

  • 当生产者进程向缓冲区放入一个产品后,此时信号量需要执行一个V操作
  • 当消费者进程在缓冲区取走一个产品前,此时信号量需要执行一个P操作

3.代码实现
在这里插入图片描述
实现两对同步关系:
在这里插入图片描述

1
因为缓冲区是临界资源,各进程必须互斥地访问,所以我们还需要设置互斥信号量

在这里插入图片描述
4. 代码分析
执行V操作的进程会唤醒对应执行P操作的进程:
在这里插入图片描述
思考:能否改变相邻P,V操作的顺序?
在这里插入图片描述
小结:
在这里插入图片描述

16. 读者与写者问题

问题描述:

有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致不一致的错误。

因此要求:
(1)允许多个读者可以同时对文件执行读操作
(2)只允许一个写者往文件中写信息
(3)任意写者在完成写操作之前不允许其他读者或写者工作
(4)写者执行写操作前,应让己有的读者和写者全部退出

注意:

  • 与消费者进程不同,读者进程在读数据后并不会将数据清空,并不会改变数据。因此多个读者可同时访问共享数据。
  • 读进程与写进程同时共享数据,可能导致读出的数据不一致的问题。

在这里插入图片描述
如果两个写进程同时共享数据,可能导致数据错误覆盖的问题

1.关系分析
在这里插入图片描述
2.如何实现
在这里插入图片描述
潜在的问题:只要有读进程还在读,写进程就要一直阻塞等待,可能饿死。因此,这种算法中,读进程是优先的。

那么如何解决写进程饿死的状态呢,我们来看代码:
在这里插入图片描述
结论:在这种算法中,连续进入的多个读者可以同时读文件。写者和其他进程不能同时访问文件,写者不会饥饿,但也并不会是真正的写优先,而是相对公平的先来先服务原则。

读者写者问题为我们解决复杂的互斥问题提供了一个参考思路。其核心思想在于设置了一个计数器(count)用来记录当前正在访问共享文件的读进程数。我们可以用(count)的值来判断当前进入的进程是否是第一个/最后一个读进程,从而做出不同的处理。
另外,对(count)变量的检查和赋值不能一气呵成导致了一些错误,如果需要实现一气呵成,自然应该想到用互斥信号量。

17. 哲学家进餐问题

问题描述:

一个圆桌上坐着5名哲学家,每两个哲学家之间的桌子上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需要等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。
在这里插入图片描述

1.关系分析

  • 系统中有5个哲学家进程,5位哲学家与左右邻居对其中间筷子的访问是互斥关系。

2.整理思路

  • 哲学家问题只有互斥关系,但与之前遇到的问题不是的是,每个哲学家进程需要同时持有两个临界资源才能开始吃饭。如何避免临界资源分配不当造成的死锁现象,是哲学家问题的精髓。

3.解决方案

  • 信号量设置。定义互斥信号量数组chopstick[5]={1,1,1,1,1}用于实现对5个筷子的互斥访问。并对哲学家按0-4编号,哲学家i左边的筷子编号为i,右边的筷子编号为(i+1)%5,每个哲学家吃饭前依次拿起左,右两支筷子。
    在这里插入图片描述
  • 但是如果5个哲学家并发地拿起了自己左手边的筷子,那么会出现的问题是每位哲学家循环等待右边的人放下筷子(阻塞),发生死锁。
    在这里插入图片描述

如何防止死锁的发生呢?
在这里插入图片描述

4.解决思路
(1)最多允许四个哲学家同时进餐
(2)在每个哲学家进餐之前先判断哲学家是奇数还是偶数

当然,我们还可以这样解决死锁问题:

我们可以规定仅当一个哲学家左右两支筷子都可用时才允许他抓起筷子

在这里插入图片描述
我们用信号量mutex,保证了哲学家拿筷子这件事必须互斥的进行。即使一个哲学家在拿筷子拿到一半时被阻塞,也不会有别的哲学家会继续尝试拿筷子。这样的话,当前正在吃饭的哲学家放下筷子后,被阻塞的哲学家就可以获得等待的筷子了。

18. 死锁的自述

在这里插入图片描述
什么是死锁?
(1)我们来看一个哲学家进餐的问题
在这里插入图片描述
每位哲学家都在等待自己右边的人放下筷子,这些哲学家进程都因为等待筷子资源而被阻塞,即发生死锁。

那么在并发环境下,各进程因竞争资源而造成的一种相互等待对方手里的资源,导致各进程都被阻塞,都无法向前推进的现象,就是死锁。

死锁,饥饿,死循环的区别:
在这里插入图片描述
死锁产生的必要条件:

产生死锁必须同时满足一下四个条件,只要其中任一条件不成立,死锁就不会发生

  1. 互斥条件
    只有对必须互斥使用的资源的争抢才会导致死锁(如哲学家的筷子,打印机设备)。像内存,扬声器这样可以同时让多个进程使用的资源是不会导致死锁的(因为进程不用阻塞等待这些资源)。

  2. 不剥夺条件
    进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。

  3. 请求和保持条件
    进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。

  4. 循环等待条件
    存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。

注意:

  • 发生死锁时一定有循环等待,但是发生循环等待时未必死锁(循环等待是死锁的必要不充分条件)。
  • 如果同类资源大于1,则即使有循环等待,也未必发生死锁。但如果系统中每类资源都只有一个,那循环等待就是死锁的充分必要条件了。

什么时候会发生死锁?
在这里插入图片描述
总之,对不可剥夺资源的不合理分配,可能导致死锁

死锁的处理策略:
在这里插入图片描述
预防死锁:
在这里插入图片描述
小结:
在这里插入图片描述

1.哈夫曼树
在这里插入图片描述

1
HC=((d,0),(i,10),(a,110),(n,111))

2.如何构造哈夫曼树

构造哈夫曼树非常简单,将所有的节点放到一个队列中,用一个节点替换两个频率最低的节点,新节点的频率就是这两个节点的频率之和。这样,新节点就是两个被替换节点的父节点了。如此循环,直到队列中只剩一个节点(树根)。

  1. 对给定的n个权值{W1,W2,W3,…,Wi,…,Wn}构成n棵二叉树的初始集合F= {T1,T2,T3,…,Ti,…,Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。

  2. F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。

  3. F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。

  4. 重复二和三两步,直到集合F中只有一棵二叉树为止。

举个例子:

  • 如图有A,B,C,D共4棵二叉树,其权值分别为5,7,2,13
    在这里插入图片描述
  1. 选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,即A和C,新二叉树的根结点的权值为其左右子树的根结点的权值之和,即2+5=7
    在这里插入图片描述
  2. 继续在此基础上,选择一棵根节点结点权值小的树作为新构造的二叉树的左子树,即B和7,新二叉树的根结点的权值为其左右子树的根结点的权值之和,即7+7=14
    在这里插入图片描述
  3. 继续在此基础上,选择一棵根节点结点权值小的树作为新构造的二叉树的左子树,即D和14,新二叉树的根结点的权值为其左右子树的根结点的权值之和,即13+14=27
    在这里插入图片描述
  4. 此时哈夫曼树构成
  5. 引入二进制编码,各叶子结点的二进制编码如下:D(0),B(10),C(110),(111)

编程题:
1.领会哈夫曼的构造过程以及哈夫曼编码的生产过程
2.构造一棵哈夫曼树,输出对应的哈夫曼编码和平均查找长度
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <stdio.h>
#include <string.h>
#define N 50
#define M 2 * N-1

typedef struct
{
char data[5];
int weight;
int parent;
int lchild;
int rchild;
}HTNode;

typedef struct
{
char cd[N];
int start;
}HCode;

void CreateHT(HTNode ht[],int n)
{
int i,k,lnode,rnode;
int min1,min2;
for (i=0;i<2*n-1;i++)
ht[i].parent=ht[i].lchild=ht[i].rchild=-1;
for (i=n;i<2*n-1;i++)
{
min1=min2=32767;
lnode = rnode = -1;
for(k=0;k<=i-1;k++)
if(ht[k].parent==-1)
{
if(ht[k].weight<min1)
{
min2=min1;
rnode=lnode;
min1=ht[k].weight;
lnode=k;
}
else if(ht[k].weight<min2)
{
min2=ht[k].weight;
rnode=k;
}
}
ht[lnode].parent=i;
ht[rnode].parent=i;
ht[i].weight=ht[lnode].weight+ht[rnode].weight;
ht[i].lchild=lnode;
ht[i].rchild=rnode;
}
}

void CreateHCode(HTNode ht[],HCode hcd[],int n)
{
int i,f,c;
HCode hc;
for(i=0;i<n;i++)
{
hc.start=n;
c=i;
f=ht[i].parent;
while(f!=-1)
{
if(ht[f].lchild==c)
hc.cd[hc.start--]='0';
else
hc.cd[hc.start--]='1';
c=f;
f=ht[f].parent;
}
hc.start++;
hcd[i]=hc;
}
}

void DispHCode(HTNode ht[],HCode hcd[],int n)
{
int i,k;
int sum=0,m=0,j;
printf("输出哈夫曼编码:\n");
for (i=0;i<n;i++)
{
j=0;
printf(" %s:\t",ht[i].data);
for(k=hcd[i].start;k<=n;k++)
{
printf("%c",hcd[i].cd[k]);
j++;
}
m+=ht[i].weight;
sum+=ht[i].weight * j;
printf("\n");
}
printf("\n平均长度=%g\n",1.0 * sum/m);
}

int main()
{
int n=15,i;
char * str[]={"The","of","a","to","and","in","that","he","is","at","on","for","His","are","be"};
int fnum[]={1192,677,541,518,462,450,242,195,190,181,174,157,138,124,123};
HTNode ht[M];
HCode hcd[N];
for(i=0;i<n;i++)
{
strcpy(ht[i].data,str[i]);
ht[i].weight=fnum[i];
}
CreateHT(ht,n);
CreateHCode(ht,hcd,n);
DispHCode(ht,hcd,n);
return 1;
}

大家都知道在C语言中是没有字符串这个数据类型的,那我们如何来处理字符串呢?事实上我们是把字符串当成字符数组来处理。

**字符串的实质是:字符数组+’\0’**,用反斜杠0来表示字符串的结尾。
那当字符串作为数组的元素,我们如何操作呢?

1
char[5]="hello"; //字符数组

那么如果我们要表示:{“aaa”,”aaa”,”aaa”,”aaa”,”aaa”},该用什么数据类型呢?
【指针数组】
指针数组是指
数组元素为指针
数组

指针数组是由二维数组演化而来

1
int a[2][3]={1,2,3,4,5,6};

我们都知道二维数组可以看成是元素是数组的一维数组,也就是可以把上面改为:int a[2][3]={{1,2,3},{4,5,6}},每一行用括号括起来。换句话说,可以表示为int a[2]={a1[3],a2[3]};

元素是数组的数组

这里的a1[3]={1,2,3}a2[3]={4,5,6},既然如此,我们便可以声明两个指针p1,p2,由于数组名是首地址,即int *p1=a1int *p2=a2,所以我们可以得到int *a[2]={p1,p2};

元素是指针的数组

【字符串是一种特殊的数组】

1
char str[]="hello";

字符串=字符串+‘\0’
字符串的名称就是字符串的首地址

我们声明三个字符串:

1
2
3
char str1[10]="hello";
char str2[10]="welcome";
char str3[10]="string";

我们再声明三个指针,分别指向三个字符串

1
2
3
char *p1= str1;
char *p2= str2;
char *p3= str3;

我们把三个指针放到字符数组里面

1
char *string[3]={p1,p2,p3};//因为存放的是指针,所以数组名前要加星号

将p1,p2,p3用字符串首地址代替

1
char *string[3]={str1,str2,str3}

再将字符串的值代入数组

1
char *string[3]={"hello","welcome","string"}

字符串做元素的数组,这个数组的类型是字符串指针类型,也就是char *类型。

我们来看个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
int main(void)
{
char *week[7]={"Monday","Tuesday","Wednesday","Thursday","Friday","Satuaday","Sunday"};
int day=0;
printf("input day(1...7):")
scanf("%d",&day);
if(day==7&&day==0)
{
printf("%d\n equ:%s\n",day,week[day-1]);//下标是0-6,所以要减一
}
else
{
printf("错误")
}
}

字符串指针的性质
1.字符串可以当作数组来处理;
2.可以声明一个指针p,利用指针偏移量来访问数组;

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[]="hello world";
char *p=str;
for(i=0;i<strlen(str);i++)
{
printf("%c",str[i]);
printf("%c",*(p+i));//指针偏移量
}
return 0;
}

字符串与字符数组的联系
1.字符串被当做字符数组来处理;
2.字符串的首地址是字符串名,字符数组的首地址是数组名,其性质相同;

字符串与字符数组的区别
1.字符串有终止符‘\0’,而字符数组没有;
2.可以通过指针类型来修改字符数组的元素,但是不能修改字符串中的字符;

今天萌新博主来给大家介绍C语言指针中指针与数组部分,在这一块也是非常难以理解的,闲言少叙,咱们开始吧!

大家都知道数组与指针有着紧密的联系,数组是由一组若干个元素构成,我们在访问数组的时候,采用的是循环体的方式,就是把数组的下标逐步的循环一次,这样我们就可以读出所有数组元素的值。那么重点来了,数组下标与地址的之间的关系,如果我们掌握好,大家学起来指针与数组就会非常的吃力,萌新博主与大家也是感同身受啊!

【数组的存储方式】
在这里插入图片描述
数组的三个特点:
1.数组有若干个元素
2.元素的数据类型必须相同
3.数组中的数据元素是有序排列的

1
2
3
arr1[4]={1,2,3,4}
arr2[4]={4,3,2,1}
//这两个数组不一样

数组元素占用的字节长度都是相等的;
指向数组的指针的初始值就是数组的首地址;

1
int arr[4]={1,2,3,4}

每个元素占用的字节长度为:sizeof(int);

若sizeof(int)==2,假设数组的第一个元素a[0]的地址为0x00000001,则数组的第二个元素a[1]的地址为0x00000003,则数组的第三个元素a[2]的地址为0x00000005

所以说数组中的数据元素是有序排列的,那我们在查找数组元素的时候,只需要查找到第一个元素的地址,就能按照顺序找到相应的元素了。

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main(void)
{
int arr[4]={1,2,3,4};
printf("arr[0]=%p\n",&arr[0]);
printf("arr[1]=%p\n",&arr[1]);
printf("arr[2]=%p\n",&arr[2]);
printf("arr[3]=%p\n",&arr[3]);
return 0;
}

运行结果:
在这里插入图片描述
那我们再来看指针的性质:
【指针与整数的运算】

1
指针加n=指针原来的值+n*sizeof(指针所指变量类型)
1
指针减n=指针原来的值-n*sizeof(指针所指变量类型)

int *p=NULL
p=&arr[0]
p=0x000001
p+1=0x000001+1×sizeof(int)
p+2=0x000001+2×sizeof(int)
p+3=0x000001+3×sizeof(int)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
int main(void)
{
int arr[4]={1,2,3,4};
int *p=NULL;//声明一个指针
int i=0;
p=&arr[0];//等价于p=arr;
printf("sizeof(int)=%d\n",sizeof(int));
printf("arr[0]=%p\n",&arr[0]);
printf("arr[1]=%p\n",&arr[1]);
printf("arr[2]=%p\n",&arr[2]);
printf("arr[3]=%p\n",&arr[3]);
for(i=0;i<=3;i++)
{
printf("p+%d=%p\n",i,p+i);//循环输出指针的值
}
}
1
2
3
4
5
6
7
8
9
10
结果:
sizeof(int)=4
arr[0]=0019FF20
arr[1]=0019FF24
arr[2]=0019FF28
arr[3]=0019FF2C
p+0=0019FF20
p+1=0019FF24
p+2=0019FF28
p+3=0019FF2C

我们不难发现,我们可以通过指针来访问一维数组在这里插入图片描述
注意:以上对应关系的成立,指针p必须指向数组元素的首地址。

【那指针是如何访问数组的呢?】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
int main(void)
{
int arr[8]={1,2,3,4,5,6,7,8};
int i=0;
int *p=NULL;
p=arr;//指针指向首地址
printf("arr[i]\n");
for(i=0;i<8;i++)
{
printf("arr[%d]=%d\n",i,arr[i]);
}

printf("p+i\n");
for(i=0;i<8;i++)
{
printf("p+%d=%d\n",i,*(p+i));
}
return 0;
}

运行结果:
在这里插入图片描述
【访问指针的四种方法】
1.直接下标法
有一个数组a[i]
int *p=a等价于int *p=&a[0]

2.首地址自加法*(a+i)
数组名就是首地址:a等价于p

3.指针自加法*(p+i)

4.指针下标法p[i]
在这里插入图片描述
通过指针可以修改数组元素

1
2
3
4
5
6
7
int arr1[8]={省略};
int arr2[8]={省略};
int i=1;
int *p1=arr1;//使指针p指向arr的首地址
int *p2=arr2;
p1=p2;//改变指针指向
p++;//让指针指向下一个元素

注意:指针可以自加,但是数组名不可以自加,如arr1++,同时数组名也不能相互赋值,如arr1=arr2;

【用数组访问与用指针访问的区别】
1.访问方式不同

使用数组名访问时直接访问,使用指针是间接访问;
下标从0开始的原因:因为下标的实质是地址偏移量

2.占用空间不同

系统为arr分配了“sizeof(类型)*元素个数”个字节,给指针分配了“sizeof(类型)”个字节

换句话说使用指针可以降低空间复杂度,我们来看个程序:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main(void)
{
int arr[8]={1,2,3,4,5,6,7,8};
int *p=NULL;
p=arr;//指针指向首地址
printf("sizeof(arr)=%d\n",sizeof(arr));
printf("sizeof(*p)=%d\n",sizeof(*p));
return 0;
}

运行结果:
在这里插入图片描述
很明显,系统为arr分配了32个字节,给指针分配了4个字节。

【让我们来回顾二维数组】
二维数组a[i][j],具有两个下标:行下标与列下标
存储方式:先存储行下标,再存储列下标
即先在一个行下标内把a[0]列存满,再存a[1]列的元素。

1
2
a[0][0],a[0][1],a[0][2],a[0][3]
a[1][0],a[1][1],a[1][2],a[1][3]

【二维数组arr[ ][ ]的首地址有多种表达形式】
1.将二维数组看成元素是一维数组的元素
2.数组中第0行第0列的地址,&arr[0][0]
3.由于二维数组先存储行,所以第一行的第一个元素的地址,即&a[0]也是首地址

二维数组的元素个数:i×j

1
2
3
4
arr[2][3]={(1,2,3),(4,5,6)}
arr[2][3]={arr1[3],arr2[3]}
二维数组的首地址:&arr[0][0],&arr[0]
二维数组的数组名,不是首地址
1
2
int arr[i][j];
int *p=&arr[0][0];

此时p指向第一行第一个元素,即arr[0][0],而列指针是arr[i],指向第(i+1)列的元素。

不允许操作:p=arr或声明的时候使用int *p=arr(arr会指向一整行的地址)

那我们从a[0][0]跳到a[2][2],如何操作?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
int main(void)
{
int a[3][3]={1,2,3,4,5,6,7,8,9};
int i=0;
int j=0;
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
printf("a[%d][%d]=%d\t",i,j,a[i][j]);
}
printf("\n");
}
}

运行结果:
在这里插入图片描述
a[0][0]是第一行第一列的元素 ,p=&[0][0]
a[2][2]是第三行第三列的元素

有一个指针p指向二维数组的首地址:&a[0][0]
指针的偏移量为:p+行数×行下标+列下标;

我们来看个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
int main(void)
{
int a[3][3]={1,2,3,4,5,6,7,8,9};
int i=0;
int j=0;

int *p=NULL;
p=&a[0][0];

for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
printf("a[%d][%d]=%d\t",i,j,a[i][j]);
}
printf("\n");
}

for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
printf("p+N*%d+%d\t",i,j,*(p+3*i+j));
}
printf("\n");
}
printf("p+N*i+j\n");
}

运行结果:
在这里插入图片描述
于是我们得到二维数组的四种表现形式,如下图
在这里插入图片描述
【二维数组的矩阵转置】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
int main(void)
{
int a[3][3]={1,2,3,4,5,6,7,8,9};
int i=0;
int j=0;
int *p=NULL;
p=&a[0][0];//p指向首地址

printf("转置前:\n");
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
printf("a[%d][%d]==%d\t",i,j,*(p+3*i+j));
}
printf("\n");
}

printf("转置后:\n");
for(j=0;j<3;j++)
{
for(i=0;i<3;i++)
{
printf("a[%d][%d]==%d\t",i,j,*(p+3*i+j));
}
printf("\n");
}
}

运行结果:
在这里插入图片描述
在for循环中,我们只需要把i换成j,j换成i就可以实现矩阵的转置,而不需要中间变量。

【二维数组形参的形式】
另外当我们用数组作为函数的形式参数,我们需要给函数传递一个数组首地址和一个数组长度

【将数组和数组长度分别作为形参】

1
2
void function(int array[],int size)
void function(int *arry,int size)

我们再来一个例子
【在一个升序数组中查找大于3的元素】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
void printarray(int *arr,int n)
{
int i=0;
for(i=0;i<n;i++)
{
printf("%4d",arr[i]);
}
printf("\n");
}


int main(void)
{
int arr[9]={1,2,3,4,5,6,7,8,9};
int *p=NULL;
p=arr;
while(*p<3)//while循环对指针p进行取值
{
p++;//如果p的值<3,则将地址下移,再继续与3比较
}
printarray(p,9-(p-arr));//经过3次自加后,指针由指向元素1到指向元素4
//那么指针p将以arr[3]为首地址传到函数中,(p-arr)为arr[3]与首地址arr[0]的差值,也就之间的个数
return 0;
}

运行结果:
在这里插入图片描述
【用二维数组形参函数来完成矩阵倒置】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
void function1(int *p,int M,int N)
{
int i,j;
for(i=0;i<M;i++)
{
for(j=0;j<M;j++)
{
printf("%d",*(p+i*N+j));//也可以用*p++:输出当前值,地址下移
}
printf("\n");
}

}

void function2(int *p,int M,int N)
{
int i,j;
for(j=0;j<M;j++)
{
for(i=0;i<M;i++)
{
printf("%d",*(p+i*N+j));//*(p+i*N+j)是指针偏移量法
}
printf("\n");
}

}
//注意形参M,N是行列长度,不是下标

int main(void)
{
int arr[3][3]={1,2,3,4,5,6,7,8,9};
int *p=NULL;
p=&arr[0][0];
function1(p,3,3);
printf("\n");
function2(p,3,3);
return 0;
}

运行结果:
在这里插入图片描述
二维数组形参的其他形式
在这里插入图片描述
数组首地址表示法
在这里插入图片描述

小萌新相信大家在安装Kali的过程中,尤其在配置网络这一块遇到过各种问题,博主也是一样,亲测整理了一些解决方案供大家参考学习。才疏学浅,但真心想帮助大家解决一些问题!笔芯!

问题1.网络有线连接失败“设备未托管” 解决办法:
可以在命令行中输入:

1
vim /etc/NetworkManager/NetworkManager.conf

在这里插入图片描述
将managed=false改成true,重启网络

1
service networking restart//重启网络命令

问题2.虚拟机中没有eth0解决方法:

1.首先启动eth0,在命令行中输入

1
sudo ifconfig eth0 up

2.之后在/etc/network/interfaces文件中加入

1
iface eth0 inet dhcp(或是静态static)

在这里插入图片描述
3.最后重启网络

1
sudo service networking restart 

4.查看ifconfig,可以发现添加eth0成功
在这里插入图片描述

问题3.使用NAT模式配置网络
(1)在虚拟机中打开设置,选择Kali的连接方式为NAT模式
在这里插入图片描述
(2)在编辑中选择虚拟网络编辑器,查看NAT模式下所属的网段,如我自己的虚拟机在NAT下的网段为192.168.220.0,网关为192.168.220.2
在这里插入图片描述
在这里插入图片描述

如果使用校园网锐捷认证,注意:VMware NAT Service这个服务要开启!至于为什么后面会详细叙述!
在这里插入图片描述
(3)在命令行中输入vim /etc/network/interfaces修改静态网络的IP信息,保持与NAT在同一网段,如我设置的IP地址为192.168.220.20,最后注意保存

注意:如果这个文件打不开可能是因为权限不够,可以使用chmod命令更改文件权限

在这里插入图片描述
(4)配置DNS
在命令行中输入vim /etc/resolv.conf

注意:如果这个文件打不开可能是因为权限不够,可以使用chmod命令更改文件权限

在这里插入图片描述

114.114.114.114和8.8.8.8,这两个IP地址都属于公共域名解析服务DNS其中的一部分,而且由于不是用于商业用途的,这两个DNS都很纯净,不用担心因ISP运营商导致的DNS劫持等问题,而且都是免费提供给用户使用的。

114.114.114.114是国内移动、电信和联通通用的DNS,手机和电脑端都可以使用,干净无广告,解析成功率相对来说更高,国内用户使用的比较多,而且速度相对快、稳定,是国内用户上网常用的DNS;
8.8.8.8是Google公司提供的DNS,该地址是全球通用的,相对来说,更适合国外以及访问国外网站的用户使用;

(5)选择桌面右下角网络小图标,右击编辑连接
在这里插入图片描述
(6)选择设置配置eth0

在这里插入图片描述
(7)手动添加网络地址,添加与在文件/etc/network/interfaces中保持相同IP地址

在这里插入图片描述
查看网络连接信息
在这里插入图片描述
在这里插入图片描述

(8)重启网络服务

1
/etc/init.d/networking restart

在这里插入图片描述

1
sudo service networking restart

在这里插入图片描述

(9)查看虚拟机网络信息sudo ifconfig
在这里插入图片描述
在这里插入图片描述

问题4.更换Kali的更新源

在命令行中输入:vim /etc/apt/sources.list
在这里插入图片描述
添加以下更新源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#中科大
deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib

#阿里云
deb http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
deb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib

#清华大学
deb http://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling main contrib non-free
deb-src https://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling main contrib non-free

#浙大
deb http://mirrors.zju.edu.cn/kali kali-rolling main contrib non-free
deb-src http://mirrors.zju.edu.cn/kali kali-rolling main contrib non-free

#东软大学
deb http://mirrors.neusoft.edu.cn/kali kali-rolling/main non-free contrib
deb-src http://mirrors.neusoft.edu.cn/kali kali-rolling/main non-free contrib

#官方源
deb http://http.kali.org/kali kali-rolling main non-free contrib
deb-src http://http.kali.org/kali kali-rolling main non-free contrib

deb http://mirrors.163.com/debian/ jessie main non-free contrib
deb http://mirrors.163.com/debian/ jessie-updates main non-free contrib
deb http://mirrors.163.com/debian/ jessie-backports main non-free contrib
deb-src http://mirrors.163.com/debian/ jessie main non-free contrib
deb-src http://mirrors.163.com/debian/ jessie-updates main non-free contrib
deb-src http://mirrors.163.com/debian/ jessie-backports main non-free contrib
deb http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib
deb-src http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib

在这里插入图片描述
保存后执行:

1
2
3
4
apt-get update  # 取回更新的软件包列表信息
apt-get upgrade # 进行一次升级
apt-get clean # 删除已经下载的安装包
reboot #重启

问题5.锐捷下连接网络!亲测NAT下成功几率不高
众所周知毒瘤的锐捷会定时强制关闭NAT服务,导致虚拟机连不上网

VMware提供的NAT联网功能,利用网络地址转换原理使虚拟机与主机共享一个外网IP地址,从而实现主机与虚拟机同时访问Internet,十分方便。然而如果我们使用了锐捷客户端作为网络认证的工具其多网卡限制措施会每隔固定时间将Vmware NAT Service服务关闭,导致虚拟机无法访问外部网络

1.外部使用桥接模式
在这里插入图片描述
VMware NAT Service这个服务要开启!锐捷会杀掉VMware NAT Service服务
在这里插入图片描述
至于如果暴力杀锐捷,可以参考https://www.cnblogs.com/qswg/p/9805831.html

2.内部使用NAT模式(配置静态IP地址)
在这里插入图片描述
在命令行中输入 vim /etc/network/interfaces修改静态网络的IP信息,保持与NAT在同一网段,同上NAT配置网络一样
在这里插入图片描述
3.手动配置网络
在这里插入图片描述
4.查看连接信息
在这里插入图片描述
5.重启网络服务

1
/etc/init.d/networking restart

6.外言
锐捷客户端会大概每37秒关闭VMware NAT Service, 因此如果我们在它关闭之后的极短时间里重新开启该服务,则可以继续使用网络。
我们可以使用一个批处理程序,具体如何解决锐捷客户端与NAT的冲突,可参考文件https://jingyan.baidu.com/article/6fb756ec900e1f241958fb51.html

1
2
3
4
5
6
7
//.bat文件
@echo off
:s
ping -n 1 127.1>null
net start|findstr /i /c:"VMware NAT Service">nul&&set k=1||set k=0
if %k%==0 (net start "VMware NAT Service")
goto s

问题6.有时候Kali中ping不通外网
在这里插入图片描述
如果添加了DNS还是无法ping外网,
原因一可能是VMware NAT Service服务关闭,如果不是,那就要查看路由中的网关设置

使用命令:netstat -rn
在这里插入图片描述
若网关没有设置则需要添加路由网关

1
route add  default gw 192.168.220.2(我的路由网关)

再次查看netstat -rnping一下百度,发现连接成功
在这里插入图片描述
最后,希望自己整理的一些配置网络过程中遇到的问题可以为大家提供必要的参考。Kali 是一个安全工具,对于一个专业用户来说,它是一个极好的工具。如果你是一个网络管理员,想对你的网络做一个真实的测试,Kali 可以做到你所希望的一切事情。Kali 也有一些优秀的开发工具,用于去实时审计它们的应用程序。当然,如果你正好有兴趣学习关于安全的知识,你可以在某些受控环境中去使用 Kali,它可以教你学习很多的非常优秀的知识。总之,对于专业用户来说,它有难以置信的好处,但是对于一些不怀好意的人来说,也可以产生很多大麻烦,加油!

【指针能否作为函数的形式参数?】
其实指针是可以作为函数的形式参数的,我们使用指针形参,实际上是传地址到函数中(注意,是传地址喔!)
●普通的形参,只是将实参的传入函数;
●指针形参,将实参的地址传入函数;

1
void function1(int *p1,int *p2)//形式参数

1.我们注意星号表示这个地方是传进来的是一个指针,要与普通的形式参数区分开;
2.函数的执行结果必须是void类型;

【指针形参的性质】
●普通形参不会造成实参的值的改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
void swap(int a,int b)
{
int t;
t=a;
a=b;
b=t;
printf("after swap\n");
printf("a=%d\t",a);
printf("b=%d\t",b);
}
int main(void)
{
int num1=9;
int num2=8;
printf("before swap:\n");
printf("a=%d\t",num1);
printf("b=%d\t",num2);
//调用函数前输出
swap(num1,num2);
printf("after swap:\n");
printf("a=%d\t",num1);
printf("b=%d\t",num2);
//调用函数后输出
return 0;
}

结果

1
2
3
4
5
6
7
8
before swap:
a=9,b=8

after swap
a=8,b=9

after swap:
a=9,b=8

●指针形参会造成实参的值的改变

因为指针形参会导致两个变量的地址“搬家”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
void swap(int *pa,int *pb)
{
int t;
t=*pa;
*pa=*pb;
*pb=t;
printf("after swap\n");
printf("a=%d\t",*pa);
printf("b=%d\t",*pb);
}
int main(void)
{
int num1=9;
int num2=8;
int *pa=&num1;//取地址
int *pb=&num2;
printf("before swap:\n");
printf("a=%d\t",*pa);
printf("b=%d\t",*pb);
//调用函数前输出
swap(pa,pb);
printf("after swap:\n");
printf("a=%d\t",*pa);
printf("b=%d\t",*pb);
//调用函数后输出
return 0;

结果

1
2
3
4
5
6
7
8
before swap:
a=9,b=8

after swap
a=8,b=9

after swap:
a=8,b=9

【定义指针形参的函数一般需要以下步骤】

1.定义一个含有指针变量的函数
2.在主函数定义一个指针变量
3.以这个指针变量作为实参调用函数

指针函数指的是:返回值是一个指针的函数

声明:数据类型 *函数名

我们来看个例子,使用指针函数求最大值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <stdio.h>
int *max(int *a,int *b,int *c)
{
int *flag=NULL;
if(*a>*b)
{
if(*a>*c)
{
flag=a;
}
else
{
flag=c;
}
}
else
{
if(*b>*c)
{
flag=b;
}
else
{
flag=c;
}
}
return flag;
}

int main(void)
{
int num1=0;
int num2=0;
int num3=0;
int *p=NULL;
printf("input num1:");
scanf("%d",&num1);
printf("input num2:");
scanf("%d",&num2);
printf("input num3:");
scanf("%d",&num3);

p=max(&num1,&num2,&num3);//三个变量的地址传给函数
printf("the max:%d\n",*p);
return 0;
}

大家可以尝试运行一下!

【指向函数的指针】
C语言的函数也是由内存空间来存放的。
如果有一个函数声明为

1
int function1(int t1,int t2)

则可以声明一个指针来指向这个函数

1
int  (*fp)function1(int t1,int t2)

而在int (*fp)function1(int t1,float t2)中,有一个形参和函数指针类型不符,所以这个函数声明是错误的,注意:函数类型和函数形参都必须和函数指针类型完全相同。

【函数指针的赋值】

先得到函数地址,再带上参数列表

1
2
3
4
5
6
7
int (*fp)function1(int t1,int t2)
//此处省略函数模块

fp(5,6)//直接取地址调用
(*fp)(5,6)//取函数值进行调用

//注意:函数值就等于函数的地址

【通过函数指针调用求和函数】

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int add(int a,int b)//声明一个普通函数(普通形参)
{
return a+b;//求和
}

int main(void)
{
int (*fp)(int a,int b);
fp=add;
printf("3+4=%d\n",fp(3,4));
printf("3+4=%d\n",(*fp)(3,4));
}

在这里插入图片描述
最后注意一点:
【void指针表示:无类型指针】
●可以使用void指向任何类型;
●不能对void指针做加减运算;
●不能对void指针进行取值操作;
●void指针一般用来做函数的形参;

【我们为什么要使用结构体?】
假如我们要设计一个员工管理系统,一共有100名员工,我们需要记录每一个员工的姓名,性别,职位,地址,电话号码等相关信息,如果我们逐一去申请变量,那么我们需要用到600个变量,显然不好操作;那我们如果定义数组,显然也是不可采取的,因为定义每一个员工中需要用到数据的类型都各不相同。那我们怎么办呢?

为了方便处理数据,结构体就是把一些相关的,类型不同的子项数据结合在一起,结构体实际上是一种自定义的数据类型,可以包含若干个数据类型相同或者不同的数据项。
简单的来说,结构体可以将多个单一的数据结合在一起,方便处理数据

【定义结构体】

1
2
3
4
5
struct  结构体名 { 

数据类型 结构体成员;

} 结构体变量名(可选);

来个例子吧!

1
2
3
4
5
6
struct person_info{//描述人的信息,struct一定不能丢
char name[];
int age;
char add[];
...
}Rual,Kobe;//可以定义结构体变量,也可以不定义

结构体的成员可以是人员类型,也可以包括其他结构体

1
2
3
4
5
6
7
8
9
struct person_infor
{
char name[];
int age;
char job[];
char add[];
long int phone;
struct date time;//结构体的成员可以是其他结构体,但是必须提前定义好
};

我们可以根据结构体来定义结构体变量:

1
2
3
4
5
6
7
8
struct person_info{
char name[];
int age;
char add[];
...
};
struct employee_infor Kobe;//定义变量Kobe
struct employee_infor Gasol;//定义变量Gasol

注意:
1.我们在定义结构体与结构体变量时,struct一定不能丢,函数体必须在函数体外定义,也可以把结构体定义在头文件中,然后在程序中用#include包含。
2.定义结构体必须在函数体外,使用结构体的时候必须在函数体外。

来个例子吧!估计大家又听不懂了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
struct pe_info//定义结构体
{
char name[100];//切记不要初始化变量
int age;
char add[100];
};//注意;表示定义结束


int main(void)
{
//pe_info Kobe;这样定义变量是错误的
//pe_info Gasol;这样定义变量是错误的
struct pe_info Kobe={"Kobe",37,"Lakers"};//赋值
struct pe_info Gasol={"Gasol",35,"bull"};
//加上struct是为了区分普通变量


//访问Kobe的姓名
printf("struct pe_for Kobe lst mem %s\n",Kobe.name);
printf("struct pe_for Gasol lst mem %s\n",Gasol.name);
return 0;
}

运行结果:
在这里插入图片描述
如何访问结构体的成员?
形式:结构体变量名.成员

我们在初始化结构体变量的时候,也可以用typedef,去简化结构体变量名。
我们来看一段代码:
我们用pinf来简化struct pe_info;与上面代码的效果是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
typedef struct pe_info//定义结构体
{
char name[100];//切记不要初始化变量
int age;
char add[100];
}pinf;//注意;表示定义结束


int main(void)
{
//pe_info Kobe;这样定义变量是错误的
//pe_info Gasol;这样定义变量是错误的
pinf Kobe={"Kobe",37,"Lakers"};//赋值
pinf Gasol={"Gasol",35,"bull"};
//加上struct是为了区分普通变量


//访问Kobe的姓名
printf("struct pe_for Kobe lst mem %s\n",Kobe.name);
printf("struct pe_for Gasol lst mem %s\n",Gasol.name);
return 0;
}

运行结果:
在这里插入图片描述
我们再来个例子:给结构体赋值并输出结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
typedef struct pe_info//定义结构体
{
char name[100];//切记不要初始化变量
int age;
char add[100];
}pinf;//注意;表示定义结束


int main(void)
{
//pe_info Kobe;这样定义变量是错误的
//pe_info Gasol;这样定义变量是错误的
pinf Kobe={"Kobe",37,"Lakers"};//赋值
pinf Gasol={"Gasol",35,"bull"};
//加上struct是为了区分普通变量


//访问Kobe的姓名
printf("struct pe_for Kobe lst mem %s\n",Kobe.name);
printf("struct pe_for Kobe 2st mem %d\n",Kobe.age);//注意年龄是int整型
printf("struct pe_for Kobe 3st mem %s\n",Kobe.add);
printf("\n");
printf("struct pe_for Gasol lst mem %s\n",Gasol.name);
printf("struct pe_for Gasol 2st mem %d\n",Gasol.age);
printf("struct pe_for Gasol 3st mem %s\n",Gasol.name);
return 0;
}

运行结果:
在这里插入图片描述
那么结构体在内存中是怎么处理的呢?
我们看下面的结构体:

1
2
3
4
5
struct date_typ{
int a;
char b;
double c;
}tt;

int a————4个字节
char b———1个字节
double c——16个字节

所以结构体总长度为4+1+16=21字节;

【结构体数组】
结构体数组和基本数组定义方法相同

先定义结构体,再声明结构体数组

1
2
3
4
5
6
7
typedef struct pe_infor{
char name[SIZE];
int age;
char team[SIZE];
}pe_info;

pe_info pers[30];//声明一个数组,包含30个元素

我们用结构体数组来写个程序:输入5个同学的学号,姓名和成绩:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
typedef struct stu_info//定义结构体
{
int id;
char name[100];
int score;
}s_inf;

int main(void)
{
s_inf stu[5];//定义一个长度为5的结构体数组
for(int i=0;i<5;i++)//循环输入学生信息
{
printf("input %d stu_name:",i+1);//姓名
scanf("%s",&stu[i].name);
printf("input %d stu_id:",i+1);//学号
scanf("%d",&stu[i].id);
printf("input %d stu_score:",i+1);//分数
scanf("%d",&stu[i].score);
printf("\n");
}
return 0;
}

运行结果:
在这里插入图片描述
【指向结构体的指针】

1
2
3
4
5
6
7
8
typedef struct pe_infor{
char name[SIZE];
int age;
char team[SIZE];
}pe_info;

pe_info *p;//赋值
p=&pe_info;

结构体指针一般用->符号来引用变量

1
pe_info *p=NULL;//定义结构体类型的指针,分别引用结构体成员
1
2
3
4
p->name;等价于(*p).name;
p->age;等价于(*p).age;
p->team;等价于(*p).team;
//指向结构体的指针p->结构体成员