网络程序设计

网络程序设计

一、实验目的

1写一个程序来模拟网桥功能。

2编写一个计算机程序用来计算一个文件的16位效验和。

二、实验内容及要求

1模拟实现网桥的转发功能,以从文件中读取帧模拟网桥从网络中收到一帧,即从两个文件中读入一系列帧,从第一个文件中读入一帧然后从第二个文件中再读入一帧,如此下去。对每一帧,显示网桥是否会转发,及显示转发表内容。

要求:

WindowsLinux环境下运行,程序应在单机上运行。

2编写一个计算机程序用来计算一个文件的16位效验和。最快速的方法是用一个32位的整数来存放这个和。记住要处理进位(例如,超过16位的那些位),把它们加到效验和中。

要求:

1)以命令行形式运行:check_sum  infile

   其中check_sum为程序名,infile为输入数据文件名。

2)输出:数据文件的效验和

三、实验分析

1用程序模拟网桥功能,可以假定用两个文件分别代表两个网段上的网络帧数据。而两个文件中的数据应具有帧的特征,即有目的地址,源地址和帧内数据。程序交替读入帧的数据,就相当于网桥从网段中得到帧数据。

   对于网桥来说,能否转发帧在于把接收到的帧与网桥中的转发表相比较。判断目的地址后才决定是否转发。由此可见转发的关键在于构造转发表。这里转发表可通过动态生成。

2参见RFC1071 - Computing the Internet checksum

原理:把要发送的数据看成16比特的二进制整数序列,并计算他们的和。若数据字节长度为奇数,则在数据尾部补一个字节的0以凑成偶数。例子:16位效验和计算,下图表明一个小的字符串的16位效验和的计算

  为了计算效验和,发送计算机把每对字符当成16位整数处理并计算效验和。如果效验和大于16位,那么把进位一起加到最后的效验和中。

四、实验程序流程图

 1、模拟网桥

 

             模拟网桥程序流程图


2、检验和

 

                  检验和程序流程图

  

五、实验代码

1Bridge.cpp        /*模拟网桥*/

#include

#include

using namespace std;

#define MAX  10     /*转发表最大项数*/

#define null ' '        /*转发表项为空*/

//定义帧类

class frame

{

       char source;      /*帧的源MAC地址*/

       char  dest;  /*帧的目的MAC地址*/

public:

       //设置和获取帧信息

       void SetSource(char source)     /*设置帧的源MAC地址*/

       {

       this->source=source;

       }

       void SetDest(char dest)         /*设置帧的目的MAC地址*/

       {

       this->dest=dest;

       }

       char GetSource()               /*获取帧的源MAC地址*/

       {

       return source;

       }

       char GetDest()                 /*获取帧的目的MAC地址*/

       {

       return dest;

       }

};

//定义转发表类

class  tranTable

{

char  dest[MAX]={ null,null,null,null,null,null,null,null,null,null };     /*收到帧的源地址作为下一次帧查表转发的目的地址,初始化为空表*/

        int   intface[MAX];     /*收到此帧的接口*/

public:

//设置转发表项

       void SetDest(int i,char source)     /*设置转发表项的目的地址*/

       {

              this->dest[i]=source;

       }

       void SetIntface(int i,int intface)  /*设置转发表项目的地址的接口*/

       {

              this->intface[i]=intface;

       }

 

//获取转发表项

       char GetDest(int i)              /*获取转发表项的目的地址*/

       {

              return dest[i];

       }

       char GetIntface(int i)          /*获取转发表项的目的地址的接口*/

       {

              return intface[i];

       }

//转发表的其它操作

       void updateTable(char source,int intface)  /*更新转发表*/

       {

   int i;

   for (i = 0; this->dest[i] != null; i++);      /*查找转发表的空项*/

SetDest(i, source);                      /*添加转发表项*/

SetIntface(i, intface);

cout<<"2.转发表中无主机"<MAC地址与转发接口信息,"<<"将主机"<MAC地址和转发接口信息添加到转发表。"<

 

cout<<"3.添加项("<<"MAC地址:"<接口:"<

 

 

       }

 

       void searchTable(char source,char dest,int intface)   /*查找转发表*/

       {

   int flag = 0;      /*是否需要更新的标志*/

       for(int i=0;this->dest[i]!=null;i++)

       {

   if (this->dest[i] == source)      //转发表中没有和帧的源地址匹配的项目

   {

   cout << "2.无需更新转发表!" << endl;

   flag = 1;               /*无需更新转发表*/

   break;

   }

       }

   if (flag == 0)

   {

   updateTable(source, intface);  //更新转发表

   cout << "4.已更新转发表!" << endl;

 

   }

    flag = 0;      /*需要更新转发表*/

       for(int i=0;this->dest[i]!=null;i++)

       {

 

 

   if (this->dest[i] == dest&&this->intface[i] == intface)

   {

   cout << "5.转发表中给出的接口和该帧进入网桥的接口相同,丢弃此帧,不转发" << endl<

   flag = 1;

   break;

   }

  if(this->dest[i] == dest&&this->intface[i] != intface)

   {

   cout << "3.选择网桥接口:接口" << this->intface[i] << "进行转发" << endl<

   flag = 1;

   break;

   }

       }

 

   if (flag==0)  //转发表没有和目标地址相匹配的项

   {

   cout << "5.转发表没有和目标地址相匹配的项,选择非进入网桥的接口:接口" << 1 - intface << "进行转发" << endl<

   }

       }

};

int main()

{

frame data;            /*定义数据帧*/

tranTable trantable1;  /*定义转发表*/

        fstream f1("d:\\LAN1.txt",ios::out),f2("d:\\LAN2.txt",ios::out);     /*创建文件流对象f1,设置流为输出流,流的终点为指定路径文本文件*/

//写入数据,ABCD分别代表主机1、主机2、主机3、主机4MAC地址,“ABA表示源MAC地址,B表示目的MAC地址

        f1 << "AB BA AC AD BC BD";       /*向文件流写入数据*/

f2 << "CD DC CA DA CB DB";

f1.close();    /*关闭文件流f1*/

f2.close();    /*关闭文件流f2*/

f1.open("d:\\LAN1.txt",ios::in);       /*打开以输入流方式的文件流f1,流的起点为指定路径文本文件*/

f2.open("d:\\LAN2.txt",ios::in);

cout<<"本实验有LAN1LAN2两个网段和一个网桥,其中主机1、主机2LAN1网段,主机3、主机4LAN2网段。"<

        while(f1.peek()!=EOF&&f2.peek()!=EOF)      /*读取到文件尾*/

{//交替读取文件

  char source,dest;

  f1>>source>>dest;     /*从文件流中读取一个帧*/

  cout <<"1.主机"<< source-'A'+1 <<"发送帧到主机"<

 

  data.SetSource(source);

  data.SetDest(dest);

  trantable1.searchTable(data.GetSource(),data.GetDest(),0);  /*查找转发表*/

  f2>>source>>dest;   /*从文件流中读取一个帧*/

  cout <<"1.主机"<< source-'A'+1 <<"发送帧到主机"<

  data.SetSource(source);

  data.SetDest(dest);

  trantable1.searchTable(data.GetSource(),data.GetDest(),1);  /*查找转发表*/

   }

        f1.close();   /*关闭文件流f1*/

        f2.close();   /*关闭文件流f2*/

}

2check_sum.cpp             /*检验和*/

#include

#include

using namespace std;

int main(int argc,char* argv[])            /*定义命令行参数*/

{

    char fbuf[128];                        /*缓存文件*/

    int  fbuflen=0;                        /*缓存文件大小*/

    int  checksum=0;                       /*检验和初始化为0*/

    fstream infile("infile",ios::out);     /*定义输出文件流*/

    infile<<"Hello world.";                /*向文件流中写入数据*/

    infile.close();    

    if(argc==2)                            /*命令行的参数个数为2*/

    {

         infile.open(argv[1],ios::in);     /*读取命令行第二个参数命令的文件*/      

         if(!infile)                       /*读取文件流失败*/

            {

               cout<<"读取文件失败!"<

               return 0;

             }

         infile.get(fbuf,256);            /*将文件流的数据保存到数组中*/        

         for(int i=0;fbuf[i]!='\0';i++)   /*计算字符个数*/

         {

             fbuflen++;             

         }

         cout<<"读取的文件内容:"<

         if(fbuflen%2!=0)                 /*数据为奇数个时,在最末尾补一个字节的0*/          

            fbuf[fbuflen++]='0';

         else

         {

             short bit_16[fbuflen/2];                      /*16位整数的个数*/

             cout<<"文件内容对应的整数序列:";

             for(int i=0;i<(fbuflen/2);i++)

            {

                bit_16[i]=(fbuf[i*2]<<8)|fbuf[i*2+1];       /*每两个字符凑成16位整数*/

cout<

                checksum+=bit_16[i];                        /*计算检验和*/

                if(checksum>>16)                            /*进位*/

                  checksum=(checksum&0xffff)+(checksum>>16);

            }

         }

 

         cout<校验和:"<

    }

    else                                 /*命令行的参数个数不为2*/

    {

        cout<<"请以命令行形式运行:check_sum  infile"<

    }

    return 0;

}

 

 

六、实验过程

1、模拟网桥

1)编辑并编译Bridge.cpp文件,生成可执行文件

 

2运行结果

 

 

 

2、检验和

1)编辑并编译check_sum.cpp文件,生成可执行文件

 

2)运行check_sum命令,提示输入check_sum infile命令运行

 

3)运行check_sum infill命令,输入错误的文件名,显示读取文件失败

 

4)运行check_sum infill命令,正确输入check_sum infile命令,显示正确的结果

 

七、实验总结

1、模拟网桥的基本思路是,两个文件模拟两个网段LAN1LAN2,文件的交替读取表示网段LAN1的主机发送数据帧到网段LAN2的主机。其中LAN1的主机发送数据帧到LAN1主机、LAN1主机发送数据帧到LAN2主机和LAN2主机发送数据帧到LAN1主机都要通过网桥转发表的存储转发。

   当主机进行通信时,数据帧首先发送到网桥。网桥提取到数据帧的信息后,会在转发表中查找是否有源主机的MAC地址,如有,无需更新转发表。如果没有,需要把该源主机的MAC地址和数据帧进入网桥的接口信息存储到转发表。网桥处理完数据帧的源MAC地址信息后,接着提取数据帧的目的MAC地址和转发表的表项进行匹配,根据匹配的信息选择是否转发。如无匹配信息,选择将数据帧从非进入接口进行转发。

   当网桥的转发表建立好时,相同网段的主机通信时,不转发数据帧,即丢弃此帧。不同网段的主机通信,转发数据帧。

2、校验和的实质是将数据分成多个16位整数,并进行求和。校验和求和过程中有几点是需要注意的:首先是必须检查16位整数的个数是否为偶数,如不是偶数个,需要在数据的末尾补上一个字节的0;其次是校验和的位数超过16位时,需要对校验和进行进位操作。

 

 

 

你可能感兴趣的:(网络程序设计)