今天不经意间从电脑里发现了自己大学时的一课程设计,还是自己在学校时那会比较勤奋,敢于想象,敢天动脑筋
还是贴到这里留念一下
一、课程设计的目的
《路由与交换技术》这门课程的内容包含了计算机网络的关键技术,要学好这门课程,与实践是不能分开的。路由器和交换机已广泛应用于计算机局域网及互联网中,掌握路由器和交换机的使用方法和配置知识及相关内容,对于学好《路由与交换技术》这门课程有很大的帮助。希望同学们能够在一周的时间里,切实按照课程设计的要求,结合所学的理论知识和平时积累的实践能力,积极查找相关资料,完成好这次课程设计,熟悉所选项目的内容,提高网络应用能力,丰富自己的网络实践经历,为将来的实际工作积累一些经验。
二、设计内容
1、设计题目:
交换机MAC地址表的形成过程动态演示
2、原理知识:
如果网络中的计算机是通过集线器连接的,那么这种网络就被称为共享式以太网。使用集线器互连的网络环境很容易发生数据的碰撞,因为不管发送数据还是接收数据都使用同一个数据通道,所以,主机在发送数据的同时必须使用接收线对检测是否发生了碰撞,这种机制使得主机只能以半双工的模式工作。另一方面,集线器是物理层设备,通过对信号的中继放大,延长了网线的通达距离,扩展了网络规模。网络规模的扩大意味着碰撞域的扩大,进一步地降低了网络的性能。
共享式局域网的特性严重制约着网络性能的提高,逐渐地被使用交换机构成的交换式局域网所取代:
(1)交换机取代集线器解决了碰撞问题。交换机是工作在数据链路层的设备(所以也称第2层交换机),它可以识别数据帧中封装的MAC地址,并根据地址信息把数据交换到特定的端口,而不是像集线器工作时那样,把从一个端口接收到的数据复制到所有其他端口。这样的工作方式使交换机的不同端口之间不会产生碰撞,也就是说交换机可以分割碰撞域。如果一个端口只连接一台主机的话,就等于消除了碰撞。
(2)交换机解决了集线器与和它相连的主机不能全双工通信的问题。交换机使用独立的收、发通道为每个接口相连的主机转发数据,这样主机可以全双工地工作。
(3)交换机可以为任意两个交换数据的端口建立一条独立的数据通道进行交换数据,大大提高了数据交换的效率。
交换机具有这些特性是由它的工作原理决定的。
交换机根据数据帧中封装的目的地MAC地址做出转发数据的决定。交换机在转发数据前必须知道它的每一个端口所连接的主机的MAC地址,构建出一个MAC地址表,以便作出正确的转发决定。当交换机从某个端口收到数据帧后,读取数据帧中封装的目的地MAC地址信息,然后查阅事先构建的MAC地址表,找出和目的地址相对应的端口,从该接口把数据转发出去,其他接口不受影响。交换机在地址表中找不到目的地址和端口的相对应记录时,则会把数据向除了数据来源端口外的其他所有端口转发,所以广播数据会被交换机转发到其所有端口,使得和交换机相连的设备处于同一个广播域内。
下面举例说明交换机建立地址表的过程。
当交换机启动时,初始的MAC地址表是空的。
三、设计方法
2.设计思想
Computer发Message{frommac,data,tomac},Switcher从Computer所在的port(1~5)接收Message,对Message拆封获得Message的来源mac和目标mac,重新计算机MACTable(驻留在内存),并存入文件Table.txt,而又MACTable.txt是记录MAC表每次变化的情况,相当于Table的日志文件。(Table.txt和MACTable.txt在MAC目录下)
然后从类存中的MACTable中查找与目标mac对应的port,如果找到便直接向该port转发Message,否则向每一个port广播Message,系统中的每台Computer一直在接收系统消息,如果是系统中的消息是发给自己的,就收下并存入自己的文件com(A~D).txt,否则丢弃。每一台Computer有自己的存储收到消息的文件:comA.txtcomb.txt comC.txt comD.txt come.txt全在COMPUTER目录下
一定要先计算MACTable再转发,这样计算机要是给自己发消息的话即frommac==tomac,交换机便可以直接向消息来的port转发,就不需要广播了.其实实际情况是如果frommac==tomac,消息在经过计算机网卡时,便立刻返回给了自己,根本没有向网络上发布.这也是本系统做的一个不符合实际情况的地方,实际情况是计算机给自己发消息,自己收到消息,而交换机的MACTable没有记录源主机的MAC地址.因为时间伧促没有及时修改,十分遗憾。
其它的工作情况基本符合事实,而且运行正常的很。哪怕在系统工作的时候将Computer换接了Switcher端口,仍然按照修改后的系统正常工作。
3.开发工具
Microsoft Visual C++ 6.0
4.具体设计
系统工作的步骤:
co[from].SendData(sw);
sw.RecvData(co[from].port,co[from].mess);
sw.ComputeMACTable();
sw.SendData();
sw.SaveMAC();//将MAC的变化输出到文件以供查看
for(int i=0;i<COMPUTER_NUM;i++)
co[i].RecvData(message1);
程序文件结构
Class.h :声明结构和类
Class.cpp :定义类的成员函数数数
Main.cpp :主程序
struct Message//消息结构
{
char FromMAC[18];
char Data[1024];
char ToMAC[18];
};
struct Recordset //MAC表的记录结构
{
int p_num;
char MAC[18];
};
class Computer //计算机类
{
public:
char recvfile[20];
void PrintInfo();
char name[20];
int port;
char MAC[18];
Message mess;
Computer();
Computer(char* c_name,int s_port,char* c_MAC);
void RecvData(Message s_mess);
void SendData(Switcher sw);
void BindPort(int s_port);
void SetMACAddr(char c_MAC[]);
void SetName(char c_name[]);
void SetData(char* data, char* ToMAC);
void SetRecvFile(char *filename);
virtual ~Computer();
friend Switcher;
};
class Switcher //交换机类
{
public:
void SetName(char sname[]);
char name[20];
Switcher();
void ComputeMACTable();
void SendData();
void RecvData(int p_num,Message str_me);
void PrintTable();
void SaveMAC();
Switcher(int s_pnum,char* s_name );
friend Computer;
virtual ~Switcher();
private:
int mess_FromPort;
char mess_FromMAC[18];
int PORTNUM;
int mess_ToPort;
char mess_ToMAC[18];
Message buffer;
Recordset Temp;
vector<Recordset>MACTable;//使用创建一个Recordset类型的序列容器MACTAble
};
主程序(详细的见程序源代码,有注解)
const COMPUTER_NUM=5;
void Graph();//打印拓扑图
void Menu();//打印可选菜单
char SelectNum();//等待输入选项
void MakeMessage();//构造消息
void Init();//系统初始化
char* InputMAC();//专门用于输入MAC地址并返回其指针
void SetComputer();//用于设置计算机
void SetSwitcher();//用于设置交换机
void Start();//演示系统运行
void Working();//环境系统工作
//构造演示系统的环境和设备
Switcher sw(5,"switcher");//实例一个五口的交换机 名:switcher
Computer co[COMPUTER_NUM];//定义五台电脑
Message message1;//定义一个消息
int port;//消息从交换机的port端口进入
int from,to;//记录消息的始,终计算机的下标
char MAC[18];//全局MAC Input()对其改变并返回指向它的指针
//主函数 程序入口
int main(int argc,char*[])
{
Init();
Start();
return 0;
}
主程序中的部分函数
char SelectNum()
{
char error='e';
char ch[2];
ch[0]=getche();
ch[1]=getch();
if(ch[1]=='\r'||ch[1]=='\n')//一定要保证第二个从键盘输入的为回车
return ch[0];
else
return error;//否则返回一个error
}
void MakeMessage()
{
int i=1;
char computer_name[20];
while(i)//确保系统中有源主机,直到输入正确的为至
{
cout<<"选择源主机:";
cin>>computer_name;
for(i=0;i<COMPUTER_NUM;i++)
{
if(!strcmp(co[i].name,computer_name))//找到这台计算机
{
from=i;//记录源主机在数组中的下标
port=co[i].port;//源主机所接入的switcher的端口号
break;
}
}
if(i==5)
cout<<"输入错误!\n";
else
i=0;
}
i=1;//同样确保系统中有宿主机,直到输入正确的为至
while(i)
{
cout<<"选择宿主机:";
cin>>computer_name;
for(i=0;i<COMPUTER_NUM;i++)
{
if(!strcmp(co[i].name,computer_name))
{
to=i;
break;
}
}
if(i==5)
cout<<"输入错误!\n";
else
i=0;
}
strcpy(co[from].mess.FromMAC,co[from].MAC);//构造源主机所发消息的源MAC目标MAC
strcpy(co[from].mess.ToMAC,co[to].MAC);
port=co[from].port;
strcpy(message1.FromMAC,co[from].MAC);//构造系统消息的源MAC目标MAC
strcpy(message1.ToMAC,co[to].MAC);
cout<<"输入要发送的内容(以@结束):";
cin.getline(message1.Data,1024,'@');
//ASSERT(message1.Data[0]=='\r');//用宏调试出message1.Data[0]为'\r'
//所以要过滤第一个字符'\r'
for(i=0;i<1024&&message1.Data[i]!='\0';i++)
message1.Data[i]=message1.Data[i+1];
strcpy(co[from].mess.Data,message1.Data);//构造源主机所发消息的数据部分
}
char* InputMAC()
{
int i;
string temp;
bool error=true;
while(error)//直到输入正确的MAC形式如"fd-ae-bc-97-65-01"
{
cout<<"input string:";cin>>temp;
if(temp.length()!=17)//输入的字符长度不是17立刻跳到最后
{
error=true;
goto last;
}
for(i=0;i<5;i++)//只要有一个错误立刻跳到最后
if(temp.at(3*i+2)!='-')
{
error=true;
goto last;
}
for(i=0;i<18;i=i+3)//只要一个不在"0123456789abcdef"中产生错误
if(!( (temp.at(i)>='0'&&temp.at(i)<='9')||
(temp.at(i)>='A'&&temp.at(i)<='F')||
(temp.at(i)>='a'&&temp.at(i)<='f') )
&&
!((temp.at(i+1)>='0'&&temp.at(i+1)<='9')||
(temp.at(i+1)>='A'&&temp.at(i+1)<='F')||
(temp.at(i+1)>='a'&&temp.at(i+1)<='f'))
)
{
error=true;
goto last;
}
error=false;//如果程度执行到这里肯定是正确的MAC,没有error
last: if(error)
{
cout<<"输入有错误\n";
}
else
{
for(i=0;i<temp.length();i++)
MAC[i]=temp[i];
MAC[i]='\0';
}
}//正确的MAC,退出while(error)返回MAC
return MAC;
}
void Start()
{
bool flag=true;
char num_select;
while(flag)
{
system("cls");
Graph();
Menu();
num_select=SelectNum();
switch(num_select)
{
case'1':
cout<<endl;
cin.clear();//清空输入流
MakeMessage();
Working();
cout.flush();
system("pause");
break;
case'2':
cout<<endl;
sw.PrintTable();
cout.flush();//刷新输出流
system("pause");//暂停
break;
case'3':
cout<<endl;
if(port>0&&port<6)//构造消息以后从交换机哪个port进入是确定的1~5
{ //才能工作
Working();
}
else
cout<<"先构造消息\n";
cout.flush();
system("pause");
break;
case'4':
cout<<endl;
SetComputer();
cout.flush();
system("pause");
break;
case'5':
cout<<endl;
SetSwitcher();
cout.flush();
system("pause");
break;
case'6': //退出程序结束while(flag)
cout<<endl;
flag=false;
break;
default:
cout<<endl;
cout<<"选择错误重新选择";
system("pause");
break;
}
}
}
void Working()
{
co[from].SendData(sw);
sw.RecvData(co[from].port,co[from].mess);
sw.ComputeMACTable();
sw.SendData();
sw.SaveMAC();//将MAC的变化输出到文件以供查看
for(int i=0;i<COMPUTER_NUM;i++)
co[i].RecvData(message1);
}
四、软件说明书
1.界面如图所示
2.操作流程
输入程序提供的选项,从键盘接收的只允许输入两个键值:选项+回车
必须为如下格式:1+回车
输入任何不合法的都为错误,程序并返回,等待重新输入直到输入正确为至
①1+回车
[1]
选择源主机:A
选择宿主机:D
输入要发送的内容(以@结束):你 好!@
计算机A发出数据:你 好!
switcher从端口1接收数据:你 好!
switcher向端口2发送数据:你 好!
switcher向端口3发送数据:你 好!
switcher向端口4发送数据:你 好!
switcher向端口5发送数据:你 好!
计算机D收到数据:你 好!
请按任意键继续. . .
②2+回车
[2]
SwitcherName:switcher
PORT1: MAC:11-11-11-11-11-11
请按任意键继续. . .
③3+回车
[3]
计算机A发出数据:你 好!
switcher从端口1接收数据:你 好!
switcher向端口2发送数据:你 好!
switcher向端口3发送数据:你 好!
switcher向端口4发送数据:你 好!
switcher向端口5发送数据:你 好!
计算机D收到数据你 好!
请按任意键继续. . .
④4+回车
[4]
计算机名:A
1.NAME:A
2.MAC :11-11-11-11-11-11
3.LPort:1
选择选项:[1]
输入计算机名:Z
请按任意键继续. . .
⑤5+回车
[5]
1.switcher
选择选项:[1]
输入交换机名:jiaohuan
请按任意键继续. . .
⑥6+回车
(退出程序)
可以查看COMPUTER和MAC目录下的文件确认是否正常工作了
文件:MAC\MACTable.txt
端口 |
MAC地址 |
1 |
11-11-11-11-11-11 |
文件:MAC\MAC.txt
1
11-11-11-11-11-11
文件:COMPUTER\com D.txt
计算机D收到数据你 好!
五、参考文献
《标准C++程序设计教程》电子工业出版社 林丽闽等著