两层网络、三层网络的理解

对于搞IT的同行而言,大部分人都不会直接和网络打交道,因此除非从事网络开发,否则对网络内部机制也不会太关心,但是明白网络数据是怎么走的,这对每个IT工程师应该是很重要的基础知识。网络数据包如何在网络上游荡,长久以来也困扰了我很长时间,现在把这部分内容总结分享一下。

  说起网络,大家不约而同会想起大学课本上那个臭名昭著的ISO七层模型,但是ISO模型只是提供了一个参考,并不是具体实现,目前我们使用最多的实现其实是TCP/IP协议族。但是对于TCP/IP,除了表示层和会话层没有体现,其它几层和ISO基本是对应的,从这个角度讲ISO模型还是有参考意义的。

  不扯那么多,我们落地一点:

  • 对于物理层而言打交道的基本都是电信号和光信号,例如网卡、光纤、双绞线等都被归到物理层考虑;
  • 对于链路层,数据在离散电/光信号的基础之上,被逻辑划分成一帧一帧(Frame)来管理,这一层是数据交换的主要层面,交换的依据主要是网卡MAC地址,以太网(定义了一种帧格式)、交换机、集线器都划归这一层;
  • 网络层是比链路层更高一级的逻辑层,在这一层主要工作的是路由器,路由器基于IP地址进行跨网链路的计算;
  • 传输层顾名思义是用来控制网络层传输的,因为网络层只是一个“尽力而为”的层,其传输不是完全可靠的,如果将超时重传等可靠性保障机制都交给程序员来做,估计大部分程序员都要疯了,幸好有了传输层提供了TCP和UDP两种机制给我们,才让我们可以高枕无忧的传输数据,而我们在代码里要做的只是打开一个传输层的套接字(即Socket)就可以了;
  • 至于表示层和会话层我们就不多做理解了,这两层基本只是摆设;
  • 应用层是最高层的协议,Web HTTP协议、远程登录SSH协议等都被划归这一层,确切来说这一层已经不属于基础网络了,基本都是软件自定义协议。


  理解了网络各层的概念后,我们再回过头来看,在这七层中网络层、链路层、物理层属于低三层,其余属于高四层,从字面上可以看出传输层以下才是真正通过网络传输数据的层面。对于物理层,主要定义的是各种传输介质的信号传输方式,比如时钟频率、电平高低、信道编码等,这一层只是机械的传输,不涉及数据包选路逻辑,链路层(L2层)和网络层(L3层)才是我们要理解的地方。

  首先看L2链路层,这一层以帧(Frame)为单位组织物理信号,每个帧都需要有一个源地址和目的地址,绝大多数情况下使用的都是网卡MAC地址。这一层主要的数据转发设备是集线器和交换机,对于集线器,由于每一个数据帧都会被复制到各个端口,使每个连接主机收到很多跟自己无关的数据帧,这直接导致主机和集线器之间信道冲突剧烈(冲突域属于物理层概念),因此现在基本不用该设备。而交换机则具有MAC地址学习功能,能够向各个端口准确投放数据帧,这样就大大提高了数据传输效率。对于L2层,交换机只能转发一个子网内的数据帧(子网是通过IP地址划分的),如果要将一个数据帧跨网转发,则需要借助于L3层的路径规划功能,这个一会再说。

  现在假设有如下网络拓扑结构,ABCD四台主机属于10.0.0.0子网,网关都指向路由器的10.0.0.1端口,EFGH属于10.0.1.0子网,网关指向路由器的10.0.1.1端口。


   先看同一子网内的通信的情况(A向C发送数据,这种情况下都是通过IP地址指定的),假如所有的主机、交换机和路由器都刚刚加电,内部没有缓存任何MAC映射表和路由表。A在发送之前,发现C和A在同一个子网内,于是A试图先在物理子网内找一下C,但是在同一物理子网内是通过硬件MAC地址来寻址的,而A此时并不知道C的MAC地址,于是A通过ARP广播来试图获取,发出的广播包包括如下类似内容:(注:广播时用的MAC地址是ff:ff:ff:ff:ff:ff)


  交换机1在收到这个ARP广播包后,首先学习到了主机A原来是和1口连接的,然后在缓存中查找C的MAC地址,但是最终没有找到,于是交换机1将这个包从所有端口(连接A的1口除外)发出去,交换机2收到后也会继续广播出去。当主机B和D收到这个广播包之后,发现和自己无关,于是便直接丢弃这个包,不做任何处理;C收到这个广播包后,发现原来是找自己的,于是它发出如下类似格式的回应内容,来告知A自己的身份。


  这个过程对于所有参与的交换机也是个学习的过程,因此交换机1和交换机2也学习到了A和C的位置。至此AC相互找到对方后,便可以在同一物理子网之间直接通过指定MAC地址通信了,他们发送数据帧的类似格式如下:


  下面再来看跨物理网络通信的情况(A向E发送数据),同样假设设备都刚刚加电,缓存为空。A发现E的IP也是同一网段的,于是又开始广播,但是这次BCD都没有回应。我们此时把视线转到路由器1上,当路由器1收到这个ARP广播包后,为了避免广播风暴的产生,路由器1不会继续广播这个ARP包,但是路由器1会把自己的MAC告诉A,回发如下类似格式的内容:


  A在等待超时后,发现当前物理子网内找不到E,但是A已经知道了网关路由器的MAC地址,于是便会将发给E的数据包扔给网关(也就是路由器1的1口),路由器1收到这个包后,发现E的IP在自己内部也没有缓存,于是路由器1也开始了寻找E的过程。相比交换机的子网内“广播找人”,路由器的选路范围更大也更复杂,很多情况下是整个Internet,并且要夸多个运营商,所以在L3层面路由器的路径计算协议较多,包括:RIP、OSPF、IS-IS、BGP、IGRP等协议。路由器之间计算路径时,任何一台路由器都是无法窥探整个网络的,因此每台路由器都只是通过选路算法找到下一跳的最优路径,这些最优路径连接起来便形成了一条完整的路径。换句话说,路由器的转发路径不是一个路由器选择出来的,而是一群路由器共同选择出来的下一跳地址序列。具体的路由选路无法一一讲解,大家感兴趣可以自己调查一下,这里假设路由器1直接找到了路由器2。

  我们继续往下探索,当路由器2接到寻找主机E的广播包后,发现E位于自己的网络中(当然也提前需要一个广播学习的过程才能知道),便向前一跳路由器(即路由器1)反馈自己离主机E最近,最终经过这样一个“A→网关路由器→路由器间选路→找到主机E所在子网”的过程A终于可以与E进行通信了,由于A和E之间经历了多个物理子网,因此需要多次的L2转发才能实现数据包的到达,这个过程中L3层IP包外包帧的MAC地址会不断变换。A→B→A这个过程中,数据帧和IP包的地址经历过程如下(假设A使用的是本机的88端口,B使用的是本机的99端口):


  在这个过程中,数据包在路由器1和2的1<-->4口之间传递时,由于是在一个设备内部,因此可以直接转发,而不用变换帧头,从而提高转发效率。如果A要与其它子网的FGH主机通信,过程基本是一样的,只不过刚开始不会先在当前子网内“广播找人”,而是直接将数据包投递给出口网关。

  本文旨在向大家展示L2交换机和L3路由器在转发网络数据时的一个主要流程,希望能给大家带来帮助。由于3层跨子网计算IP包投送路径协议比较多,先请大家自己参考其它资料。

  • 51
    点赞
  • 156
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
### 回答1: 首先,我们需要定义一个函数来实现三层神经网络:def neural_network(inputs, weights, bias): layer_1 = sigmoid(np.dot(inputs, weights['h1']) + bias['b1']) layer_2 = sigmoid(np.dot(layer_1, weights['h2']) + bias['b2']) output_layer = sigmoid(np.dot(layer_2, weights['out']) + bias['out']) return output_layer 然后我们需要定义一个Sigmoid函数: def sigmoid(x): return 1 / (1 + np.exp(-x)) ### 回答2: C语言可以用来编写三层神经网络三层神经网络包括输入层、隐藏层和输出层。输入层接收输入数据,隐藏层进行特征提取和转换,输出层产生最终的结果。 在C语言中实现三层神经网络的关键是定义神经网络的结构和实现相应的算法。以下是一个简单的示例: ```c #include <stdio.h> // 定义神经网络的结构 #define INPUT_SIZE 2 #define HIDDEN_SIZE 3 #define OUTPUT_SIZE 1 // 定义激活函数(如Sigmoid函数) float sigmoid(float x) { return 1 / (1 + exp(-x)); } // 神经网络的前向传播 float feedforward(float* input, float** w_in_h, float* b_h, float** w_h_o, float* b_o) { float hidden[HIDDEN_SIZE] = {0}; float output[OUTPUT_SIZE] = {0}; // 隐藏层计算 for (int i = 0; i < HIDDEN_SIZE; i++) { for (int j = 0; j < INPUT_SIZE; j++) { hidden[i] += input[j] * w_in_h[j][i]; } hidden[i] += b_h[i]; hidden[i] = sigmoid(hidden[i]); } // 输出层计算 for (int i = 0; i < OUTPUT_SIZE; i++) { for (int j = 0; j < HIDDEN_SIZE; j++) { output[i] += hidden[j] * w_h_o[j][i]; } output[i] += b_o[i]; output[i] = sigmoid(output[i]); } return output[0]; } int main() { // 定义神经网络的权重和偏置 float weights_in_hidden[INPUT_SIZE][HIDDEN_SIZE] = {{0.1, 0.2, 0.3}, {0.4, 0.5, 0.6}}; float biases_hidden[HIDDEN_SIZE] = {0.1, 0.2, 0.3}; float weights_hidden_out[HIDDEN_SIZE][OUTPUT_SIZE] = {{0.4}, {0.5}, {0.6}}; float biases_output[OUTPUT_SIZE] = {0.4}; // 定义输入数据 float input[INPUT_SIZE] = {0.2, 0.3}; // 进行神经网络的前向传播 float output = feedforward(input, weights_in_hidden, biases_hidden, weights_hidden_out, biases_output); // 输出结果 printf("Output: %f\n", output); return 0; } ``` 这是一个简单的三层神经网络,它接收一个二维的输入数据(`input`),包含两个输入神经元。隐藏层有三个神经元,权重为`weights_in_hidden`和偏置为`biases_hidden`。输出层只有一个神经元,权重为`weights_hidden_out`和偏置为`biases_output`。通过`feedforward`函数进行前向传播,最终输出结果。 这只是一个简单的示例,实际上神经网络的实现可能更加复杂,并且需要考虑训练和优化等问题。但这个示例可以帮助你理解如何用C语言编写一个简单的三层神经网络。 ### 回答3: 三层神经网络是一种常见的神经网络结构,包括输入层、隐藏层和输出层。下面是一个使用C语言编写的三层神经网络的示例代码。 ```c #include <stdio.h> #include <stdlib.h> #include <math.h> // 定义输入层、隐藏层和输出层的大小 #define INPUT_SIZE 4 #define HIDDEN_SIZE 3 #define OUTPUT_SIZE 2 // 定义激活函数 double sigmoid(double x) { return 1 / (1 + exp(-x)); } // 定义神经网络结构 typedef struct { double input[INPUT_SIZE]; double hidden[HIDDEN_SIZE]; double output[OUTPUT_SIZE]; } NeuralNetwork; // 初始化神经网络 void initialize(NeuralNetwork* network) { for (int i = 0; i < HIDDEN_SIZE; i++) { network->hidden[i] = 0.0; } for (int i = 0; i < OUTPUT_SIZE; i++) { network->output[i] = 0.0; } } // 前向传播 void forward(NeuralNetwork* network) { // 计算隐藏层 for (int i = 0; i < HIDDEN_SIZE; i++) { double sum = 0.0; for (int j = 0; j < INPUT_SIZE; j++) { sum += network->input[j]; } network->hidden[i] = sigmoid(sum); } // 计算输出层 for (int i = 0; i < OUTPUT_SIZE; i++) { double sum = 0.0; for (int j = 0; j < HIDDEN_SIZE; j++) { sum += network->hidden[j]; } network->output[i] = sigmoid(sum); } } int main() { // 创建神经网络实例 NeuralNetwork network; // 输入层的数据 network.input[0] = 1.0; network.input[1] = 2.0; network.input[2] = 3.0; network.input[3] = 4.0; // 初始化神经网络 initialize(&network); // 进行前向传播 forward(&network); // 输出结果 printf("输出层的结果: %lf, %lf\n", network.output[0], network.output[1]); return 0; } ``` 以上代码是一个简单的三层神经网络的实现,通过使用sigmoid作为激活函数,实现了输入层到隐藏层的计算以及隐藏层到输出层的计算。输入层数据可以通过修改`network.input`数组中的值来改变。输出层的结果将会打印在控制台上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值