【计算机网络】路由原理验证实验 & SMTP模拟实验

实验一 路由原理验证实验.

1.算法简述.

距离矢量(Distance Vector)路由算法,简称DV算法。我们常见的网络层协议中,路由信息协议RIP在进行路由选择时,采用的就是DV算法。DV算法是一种分散式路由算法(Decentralized Routing Algorithm),路由器以迭代、分布式的方式计算出最低开销路径,但没有一个节点拥有关于所有网络链路开销的完整信息(这也是DV算法区别于LS算法的最主要特征)。DV算法中,每一个节点只需要知晓与其相邻节点的连接链路的开销知识即可开始工作。通过迭代计算过程以及与相邻结点的信息交换,一个节点逐渐计算出到达某目的节点的最低开销路径。那么DV算法什么时候才认为已经完成了工作呢?DV算法实质上是一类不动点算法,当在一次迭代计算时,如果每一个路由器节点的链路代价信息都没有发生更新,那就意味着路由器中的路由表已经达到了稳定状态。只要网络的拓扑结构不发生改变,每一个路由器的路由表都不会再发生改变了。DV算法本身的思想很简洁,如果我们不考虑网络拓扑结构会发生变化这一因素的话,它的算法流程图可以给出如下:
在这里插入图片描述

2.实验平台及语言.

本次实验选择的IDE是Visual Studio 2017,程序设计语言是C++.

3.实验源代码.

DV算法C++实现(Visual Studio 2017环境).

4.实验分析.

  • 【实验过程】DV算法的逻辑相当清晰,我们根据这个逻辑,只要编程不出差错,几乎是不会遇到难点的。在我自己的实验过程中,最主要的一个问题在于如何计算每一次迭代计算时,所有路由表的标识更新变量。一开始由于我自己的疏忽,我将标识每一个路由表的布尔变量在每一次迭代后进行与运算,导致的结果是while循环的条件判断中,只要有一个路由表未发生变化,条件就不成立,最终停止算法执行而输出结果。解决方案也并不复杂,实际上也是利用了C++语言本身的一个小特性——非零值都视为true。我将每个路由表的标志变量进行迭加,这样只有在所有标志变量都为0,也就是false时,while循环才会退出,这样就保证了“不动点”的正确抵达,也就是每个路由器路由表的正确构建。
  • 【运行结果以及分析】我们采用的网络拓扑结构图如下所示: 在这里插入图片描述
    在程序中,出于表示的方便,我将[A…H]映射到[0…7]上,最终的运行结果,也就是稳定之后路由器A的路由表如下:
    在这里插入图片描述
    最终输出的路由表中,我们去除了路由器到它本身的记录,这一条记录没有实际意义。我们选取上图,也就是路由器A的最终路由表为例,来具体分析其结果的正确性。第一条记录是A-B的路由选择,直接到达就是最短路径。一条很典型的路径:A-G,其直接到达的代价为6,而路由表中显示,通过B路由器转发到达G的代价只有5。我们从拓扑图中,直观地观察可以发现,这一结论是正确的。其余的路由记录,也是类似的。下面是剩余6个路由器B-G的路由表,我们展示出来:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

5.算法思想理解.

DV算法是一个很巧妙的路由算法,它实际上利用了很多算法设计的技巧,包括但不限于分散式计算,迭代优化思想以及逼近不动点的思想。分散式计算在于每一个路由器,实际上都不知道整个网络是什么样子,它们只是在和自己的相邻路由器进行“沟通”,这样很大程度上简降低了这一算法的实现难度以及逻辑上的复杂度。迭代优化和逼近不动点是不可分割的,通过每一次迭代计算,来获取可能存在的、更优的路径结果,直至最后不动点——稳定路由表的抵达,算法也就完成了它的工作。

6.算法展望.

DV算法的巧妙在于它的分散式计算,而它最大的问题也就在于这一点。没有一个节点知道网络拓扑的全部信息,这就导致如果一个节点崩溃了,其他的节点很难得知这一信息,因为路由器中还保存着抵达该崩溃节点的路径,这就导致了路由选择环路(Routing Loop)的出现,直到TTL达到上限,网络中的路由器才能反应过来——有一台路由器崩溃了。有一种很直观的解决方法,毒性逆转(Poisoned Reverse),它的基本思想是:如果路由器Z通过路由器Y抵达了路由器X,那么Z和Y交换路由信息时,Z将会告诉Y"我到X的距离是无穷大"。如此一来,当X崩溃时,Y就不会认为它可以通过Z到达X,从而很快将X崩溃的消息传递出去,而不是产生路由选择环路。

实验二 Socket 网络编程设计实验.

1.SMTP协议简述.

SMTP是简单邮件传输协议(Simple Mail Transport Protocol)的首字母缩略词,它是Internet电子邮件的核心,它用于从发送方的用户代理程序发送报文到发送方的邮件服务器,以及从发送方的邮件服务器发送报文到接收方的邮件服务器。至于接收方邮件服务器到接收方用户代理这段路程,就不是SMTP在起作用了,而是POP3或者IMAP等协议的辖域。SMTP是一种推协议(Push Protocol),即发送邮件服务器把文件推向接收邮件服务器,特别是,这一段路程的TCP连接是要由发送邮件的服务器发起的,对于发送方的用户代理到发送服务器这段路程,也是如此。我们假设现在Adam要向Bob发送一封邮件,借此来说明整个SMTP的基本操作:

  1. Adam调用他的邮件代理程序(User Agent),并提供给Bob的邮件地址,撰写信息内容,并指示UA发送这封报文;
  2. Adam的UA把这封报文,发给他的邮件服务器,该报文被保存在邮件服务器的队列中;
  3. 运行在Adam的邮件服务器上的SMTP客户端发现了这封报文,于是它创建一个到Bob的邮件服务器的TCP连接;
  4. TCP连接建立后,SMTP客户端发送这封报文;
  5. Bob的邮件服务器上,SMTP服务端接收该报文,然后邮件服务器将其保存;
  6. Bob方便的时候,会调用他的UA阅读这封报文。

我的想法是实现发送方UA到发送方服务器这段路程的SMTP协议的交互过程。需要考虑的是,一个邮件服务器通常不是为哪一个用户服务的,所以这里涉及到多线程的交互过程,即服务器接收到一个用户发起的TCP连接请求,就创建一个线程来为该用户服务。这样可以使得协议的工作效率提高,否则一对一的服务,在现实中也是不可能实现的。

2.实验平台及语言.

本次实验选择的IDE是IntelliJ IDEA 2019.3.2,程序设计语言是Java.

3.实验源代码.

SMTP协议Java模拟实现(IntelliJ IDEA 2019.3.2环境).

4.实验分析.

  • 【实验过程】Java得益于本身的接口机制,Runnable接口的存在使得多线程的实现相当便捷,所以整个实验过程的框架很方便地就解决了。一个比较不舒服的情况是,由于输入输出流以及socket端口的打开,我们必须要使用try-catch语句块来捕获可能出现的异常,这就导致在用户端输入quit命令想要退出时(因为是简单实现,将用户命令和待发送信息放在了一个输入提示中),服务器会接收不到用户的信息,这里会产生一个NullPointerException,虽然并不影响客户端的继续运行以及并发用户的后续工作,但我想应该没有人会愿意看到频繁的异常提示,所以我在客户端的代码中,加入了单独的对于quit命令的处理代码,在用户退出时,不是粗暴地抛出异常,而是给出妥善的提示信息:【One client has left.】
  • 【运行结果分析】这一实验项目在启动时,必须要注意,服务器一定要先于所有的用户启动,否则用户启动后会发现,并没有和它进行交互的线程,会导致程序出现异常。下图是服务器启动时,给出的提示信息:
    在这里插入图片描述
    接下来我们启动一个客户端,并输入我们想要发送的信息,同时观察服务器端的信息:
    在这里插入图片描述
    在这里插入图片描述
    Enter键完成输入,同时也是发送这一消息的指令,我们看服务器端是否收到了该用户的信息。
    在这里插入图片描述
    从服务器给出的提示来看,它的确是收到了该用户发送过来的信息,并且也给用户回应了一个确认收到的信息:
    在这里插入图片描述
    接下来我们启动,另一个用户机,并且令两个用户机都执行quit命令,看看服务器的反应:
    在这里插入图片描述
    在这里插入图片描述
    上图中两个用户及都已经完成了quit命令的给出,下面是服务器的反应:
    在这里插入图片描述
    可看到,服务器已经知道了这两个客户机的断开,从而它也可以注销为这两个客户机分配的线程以及各种资源。

5.算法展望.

前面我们已经说过SMTP协议是整个因特网邮件的核心,它用于发送方UA和其服务器之间以及发送方服务器和接收方服务器之间的报文传递。并且在我们第一部分的举例说明中,我们也能发现一点——SMTP协议是不使用中间服务器的,即使发送方服务器和接收方服务器相距极远,这条规则也适用。SMTP协议所建立的TCP连接是直接将通信双方连接起来的,特别的,如果发送方服务器发现接收方服务器没有开机,那么发送方的服务器会将报文一直保存着,一段时间间隔后再次进行尝试,注意整个过程中,这条消息都在发送方的服务器上,而不是任何中间服务器。

猜你喜欢

转载自blog.csdn.net/weixin_44246009/article/details/106927052