为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

2120090356_苑爱泉_第二次作业_界面版

2018-09-10 21页 doc 443KB 33阅读

用户头像

is_061280

暂无简介

举报
2120090356_苑爱泉_第二次作业_界面版《计算机网络技术》--------------- ARP协议获取局域网活动主机MAC地址 (第二次作业—界面版) 学号:2120090356 姓名:苑爱泉 日期:2009年10月28日 目 录 1一、背景知识 11、ARP协议的工作原理 12、数据结构 23、Winpcap开发包简介 3二、作业内容 4三、开发环境 41、开发环境与开发语言 42、Winpcap编程环境配置 53、运行环境 6四、流程及核心算法实现 61、程序流程 62、主要功能代码实现 6(1)全局变量...
2120090356_苑爱泉_第二次作业_界面版
《计算机网络技术》--------------- ARP获取局域网活动主机MAC地址 (第二次作业—界面版) 学号:2120090356 姓名:苑爱泉 日期:2009年10月28日 目 录 1一、背景知识 11、ARP协议的工作原理 12、数据结构 23、Winpcap开发包简介 3二、作业内容 4三、开发环境 41、开发环境与开发语言 42、Winpcap编程环境配置 53、运行环境 6四、流程及核心算法实现 61、程序流程 62、主要功能代码实现 6(1)全局变量 7(2)包含文件 7(3)结构体定义 8(4)显示设备列 8(5)取得本机MAC地址 10(6)发送ARP请求包函数 11(7)收取ARP回复包函数 13(8)封装ARP请求包函数MackPacket(….) 13(9)获取MAC函数 14(10)线程体函数 14(11)退出函数 16五、测试 161、开始 162、列出设备详细信息 173、获取MAC地址 174、程序子窗体 185、退出 19六、总结 19七、参考资料 一、背景知识 在TCP/IP网络环境下,每个主机都分配了一个32位的IP地址,这种互联网地址是在网际范围标识主机的一种逻辑地址。为了让报文在物理网路上传送,必须知道对方目的主机的物理地址。这样就存在把IP地址变换成物理地址的地址转换问题。以以太网环境为例,为了正确地向目的主机传送报文,必须把目的主机的32位IP地址转换成为48位以太网的地址。这就需要在互连层有一组服务将IP地址转换为相应物理地址,这组协议就是ARP协议。 1、ARP协议的工作原理   在每台安装有TCP/IP协议的电脑里都有一个ARP缓存表,表里的IP地址与MAC地址是一一对应的,如附表所示。 我们以主机A(192.168.1.5)向主机B(192.168.1.1)发送数据为例。当发送数据时,主机A会在自己的ARP缓存表中寻找是否有目标IP地址。如果找到了,也就知道了目标MAC地址,直接把目标MAC地址写入帧里面发送就可以了;如果在ARP缓存表中没有找到相对应的IP地址,主机A就会在网络上发送一个广播,目标MAC地址是“FF.FF.FF.FF.FF.FF”,这表示向同一网段内的所有主机发出这样的询问:“192.168.1.1的MAC地址是什么?”网络上其他主机并不响应ARP询问,只有主机B接收到这个帧时,才向主机A做出这样的回应:“192.168.1.1的MAC地址是00-aa-00-62-c6-09”。这样,主机A就知道了主机B的MAC地址,它就可以向主机B发送信息了。同时它还更新了自己的ARP缓存表,下次再向主机B发送信息时,直接从ARP缓存表里查找就可以了。ARP缓存表采用了老化机制,在一段时间内如果表中的某一行没有使用,就会被删除,这样可以大大减少ARP缓存表的长度,加快查询速度。 2、数据结构 (1)ARP数据帧结构如下 如图:   [4]ARP协议的数据结构:   typedefstructarphdr   {   unsignedshortarp_hrd;/*硬件类型*/   unsignedshortarp_pro;/*协议类型*/   unsignedchararp_hln;/*硬件地址长度*/   unsignedchararp_pln;/*协议地址长度*/   unsignedshortarp_op;/*ARP操作类型*/unsignedchararp_sha[6];/*发送者的硬件地址*/   unsignedlongarp_spa;/*发送者的协议地址*/   unsignedchararp_tha[6];/*目标的硬件地址*/   unsignedlongarp_tpa;/*目标的协议地址*/ }ARPHDR,*PARPHDR; (2)以太网物理帧和ARP数据帧结构如下图: 3、Winpcap开发包简介  winpcap(windows packet capture)是windows平台下一个免费,公共的网络访问系统。开发winpcap这个项目的目的在于为win32应用程序提供访问网络底层的能力。它提供了以下的各项功能:   1> 捕获原始数据包,包括在共享网络上各主机发送/接收的以及相互之间交换的数据包;   2> 在数据包发往应用程序之前,按照自定义的规则将某些特殊的数据包过滤掉;   3> 在网络上发送原始的数据包;   4> 收集网络通信过程中的统计信息。   winpcap的主要功能在于独立于主机协议(如TCP-IP)而发送和接收原始数据包。也就是说,winpcap不能阻塞,过滤或控制其他应用程序数据包的发收,它仅仅只是监听共享网络上传送的数据包。因此,它不能用于QoS调度程序或个人防火墙。目前,winpcap开发的主要对象是windows NT/2000/XP,这主要是因为在使用winpcap的用户中只有一小部分是仅使用windows 95/98/Me,并且M$也已经放弃了对win9x的开发。因此本文相关的程序T-ARP也是面向NT/2000/XP用户的。其实winpcap中的面向9x系统的概念和NT系统的非常相似,只是在某些实现上有点差异,比如说9x只支持ANSI编码,而NT系统则提倡使用Unicode编码。有个软件叫sniffer pro.可以作网管软件用,有很多功能,可监视网络运行情况,每台网内机器的数据流量,实时反映每台机器所访问IP以及它们之间的数据流通情况,可以抓包,可对过滤器进行设置,以便只抓取想要的包,比如POP3包,smtp包,ftp包等,并可从中找到邮箱用户名和密码,还有ftp用户名和密码.它还可以在使用交换机的网络上监听,不过要在交换机上装它的一个软件.还有一个简单的监听软件叫 Passwordsniffer,可截获邮箱用户名和密码,还有ftp用户名和密码,它只能用在用HUB网络上著名软件tcpdump及ids snort都是基于libpcap编写的,此外Nmap扫描器也是基于libpcap来捕获目标主机返回的数据包的。   winpcap提供给用户两个不同级别的编程接口:一个基于libpcap的wpcap.dll,另一个是较底层的packet.dll。对于一般的要与unix平台上libpcap兼容的开发来说,使用wpcap.dll是当然的选择。 二、作业内容 本次作业基于ARP协议,编写程序,实现获取本局域网内全部活动主机MAC地址与IP地址对应列表的程序。 要求使用Winpcap,手动封装ARP请求数据包,实现ARP响应数据包的接收和解析。 最好有界面,如果没有界面,则把获取到的局域网主机MAC信息输出到文件中去。 如果有界面的话,要求实现列出本机所有网络设备,用户能自由选择可用设备进行局域网主机MAC地址的获取,并把获取结果输出到界面上。 三、开发环境 1、开发环境与开发语言 开发时选择了C++语言作为开发语言,并选择Microsoft Visual C++ 6.0作为开发环境和编译环境。 (1)开发平台截图如下: (2)开发并运行截图如下: 2、Winpcap编程环境配置 (1) 新建Win32 Console Application工程,然后再新建一个C++ Source File文件; (2)Tools -> Options -> Directories标签 -> 在Show directories for:下面选择Include files,然后在下面添加winpcap的include库; 接着,还是在Show directories for:下面,选择Library files,然后在下面添加winpcap的lib库。 (3)Project -> Settings -> Link标签 -> 在Object/library modules:下面的文本框,最后添加wpcap.lib。 3、运行环境 运行环境是在Windows环境下,鼠标双击生成的exe可执行程序,即可弹出程序界面。 四、流程及核心算法实现 1、程序流程 本次程序采用VC6.0K控制台开发,程序流程图如下: 2、主要功能代码实现 (1)全局变量 全局变量便于保存一些固定值,如线程数目等,并可以在不同的成员函数里实现调用,非常方便。代码如下: CRITICAL_SECTION cs;//临界区定义 int myThreadCount = 0;//线程数目 pcap_if_t *d;//全局变量,存放选中的网络设备 pcap_pkthdr* pktHd;//存放收到包的包头 const unsigned char* pktDt;//存放收到包的数据域 pcap_t *pt;//全局变量,存放打开的网络连接句柄 (2)包含文件 代码如下: #include "iostream.h" #pragma comment(lib,"ws2_32.lib") #define HAVE_REMOTE #include "pcap.h" #include "windows.h" #include (3)结构体定义 包含三个结构体:以太网物理帧头,ARP数据结构,ARP请求包(回复包)。 实现代码如下: #pragma pack(1)//操作系统界地址对齐方式换为网络对齐方式 struct ethernet_head//以太网物理帧头 {unsigned char dest_mac[6]; unsigned char source_mac[6]; unsigned short eh_type; }; struct arp_head//ARP数据包数据部分结构 {unsigned short hardware_type; // 2硬件类型:Ethernet网接口类型为1 unsigned short protocol_type; // 2协议类型:IP协议类型为0x0800 unsigned char add_len; // 1硬件地址长度:MAC地址长度为6字节 unsigned char pro_len; // 1协议地址长度:IP地址长度为4字节 unsigned short option; // 2ARP操作类型:1表示请求,2表示应答 unsigned char sour_addr[6]; // 6源MAC地址:发送方的MAC地址 unsigned long sour_ip; // 4源IP地址:发送方的IP地址 unsigned char dest_addr[6]; // 6目的MAC地址:在ARP请求中没有意义,在ARP响应中为接收方的MAC地址 unsigned long dest_ip; // 4目的IP地址:在ARP请求中为待解析的IP地址,在ARP响应中为接收方的IP地址 unsigned char padding[18]; // 18填充字节:用来补齐最小长度 }; struct arp_packet//ARP数据包结构 { ethernet_head eth; arp_head arp; }myPacket;//全局变量,存放ARP请求数据包 (4)显示设备列表 本函数在设备列表框完成,用户鼠标单击列表框中的某一项时,即运行。 实现代码如下: if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)//获得设备列表 { cout<<"搜索设备时出错!"<next)//打印本机可用的所有网络设备 { cout<<"网卡序号 ["<<++i<<"]:"<name<description) cout<<"描述信息:"<description<addresses!=NULL) { cout<<"可用性 :可用!"<next, i++);// 跳转到已选设备 if ( (pt= pcap_open(d->name,65536,PCAP_OPENFLAG_PROMISCUOUS,1000,NULL,errbuf) ) == NULL)// 打开适配器 { cout<<"打开网络接口失败!"<addresses;pAdr;pAdr=pAdr->next)//本循环用来搜索IP地址所有网段并发送ARP请求给本机 { localip=((struct sockaddr_in *)pAdr->addr)->sin_addr.s_addr;//获得IP地址 nlNetMask=((struct sockaddr_in *)(pAdr->netmask))->sin_addr.S_un.S_addr;//获得子网掩码 cout<<(localip&255)<<"."<<(localip>>8&255)<<"."<<"."<<(localip>>16&255)<<"."<<(localip>>24&255)<eth.eh_type)// 判断是不是ARP包,则将其丢弃并重新接收 { continue; } if (recv->arp.option==htons(0x0002)&& recv->arp.dest_ip == 0x50505050 && recv->arp.sour_ip == localip)//检查是不是发送给本机的,并且是不是回复包 { memcpy(localmac, recv->arp.sour_addr, 6); printf("本网卡MAC地址是: "); for(i=0;i<6;i++) printf("%x ",localmac[i]); printf("\n"); pcap_close(pt); break; } } else if (0 == res) // 如果超时,则重新接收 { continue; } else // 如果出错,则关闭WinPcap会话,然后返回 { pcap_close(pt); break; } } (6)发送ARP请求包函数 该函数实现向本机(取得本机MAC地址时)或者局域网发送ARP请求数据包。 实现代码如下: EnterCriticalSection(&cs);//临界资源控制对变量myThreadCount的惟一写读,保持数据一致性 myThreadCount++; LeaveCriticalSection(&cs); while (myThreadCount < 2)//线程同步 { Sleep(50); } char errorBuffer[PCAP_ERRBUF_SIZE]; pcap_t* session = pcap_open(d->name, 60, PCAP_OPENFLAG_PROMISCUOUS, 10, NULL, errorBuffer);//建立发送ARP数据连接 while (true) { iptosendh++; iptosendn=htonl(iptosendh); MakePacket(localip,iptosendn,&myPacket);//封装ARP请求包 if (pcap_sendpacket(session, (unsigned char*)&myPacket, 60) != 0)//发包 cout<<"发送数据时出错!"<name, sizeof (arp_packet), PCAP_OPENFLAG_PROMISCUOUS, 10, NULL,errbuf); if (NULL == session) { EnterCriticalSection(&sp); myThreadCount--; LeaveCriticalSection(&sp); return; } // 设置过滤器 bpf_program filterCode; char* filterString = "ether protoarp"; if (-1 != pcap_compile(session, &filterCode, filterString, 1, 0xFFFF0000)) { pcap_setfilter(session, &filterCode); } // 接收数据主循环 while (isCatching==true) { if ((pcap_next_ex(session, &myPackedHead, &myPacketData))>=1 ) // 如果接收到数据,则对其分析 { arp_packet* recvData = (arp_packet*) myPacketData;//创建接收到的回复包的缓存区 // 如果不是ARP包,则将其丢弃,并重新接收 if (htons(0x0806) != recvData->myEther.EnterType) { continue; } // 如果是发给本机的ARP响应包,则接收它,并发送消息给主窗口 if (htons(0x0002) == recvData->myArp.myOperation && 0 == memcmp(myMac, recvData->myArp.yourMac, sizeof (myIp))) { myInfo=ShowIp(recvData->myArp.myIp);//长整型的IP地址转化为点分四字节可读形式 myInfo+="---->"; for(int i=0;i<6;i++) { myInfo1.Format("%2x ",recvData->myArp.thisMac[i]); myInfo+=myInfo1; } m_list3.InsertString(-1,myInfo);//列表框3输出获取到的主机MAC地址和对应的IP地址 } } else // 如果超时,则重新接收 continue; } EnterCriticalSection(&sp); myThreadCount--; LeaveCriticalSection(&sp); return; (8)封装ARP请求包函数MackPacket(….) 该函数根据传递过来的三个参数(本机IP地址,本机MAC地址,封装后的包的地址)实现请求包的封装。 实现代码如下: //填充以太网物理帧枕头 memset(arpPacket->myEther.YourMac,0xff,6); //填写为全1,向所有主机发送 memcpy(arpPacket->myEther.thisMac,myMac,sizeof(myMac)); //本机地址 arpPacket->myEther.EnterType=htons(0x0806);//以太网帧类型,0x0806 //填充ARP数据帧帧内容 arpPacket->myArp.HardWareType=htons(0x0001);//硬件类型,以太网是1 arpPacket->myArp.ProtocolType=htons(0x0800);//上层协议类型,IP协议是0800 arpPacket->myArp.AddressLength=0x06;//硬件地址长度 arpPacket->myArp.IpLength=0x04;//IP地址长度 arpPacket->myArp.myOperation=htons(0x0001);//操作,1为请求,2为应答,3为RARP请求,4为RARP应答 memcpy(arpPacket->myArp.thisMac,myMac,sizeof(myMac));//设置为本机MAC地址,为0,不管是取得本机MAC还是取得局域网MAC arpPacket->myArp.myIp=thisIp;//本机IP地址,以使得目的主机回复时能找到本机 memset(arpPacket->myArp.yourMac,0x00,6);//对方主机MAC地址,设置为0 arpPacket->myArp.yourIp=thatIp;//目的主机IP地址 //最后的18个填充数据字节全部填写为0 memset(arpPacket->myArp.myFill,0,18); (9)获取MAC函数 该函数是“获取MAC”按钮的点击事件,点击后,即启动两个线程:发包线程和接收线程(监听线程)。 实现代码如下: CString myBtnText; m_btn1.GetWindowText(myBtnText); if (myBtnText.Compare("获取MAC")==0) { m_btn1.EnableWindow(false); isCatching=true;//线程函数得以进行的条件信号量 m_list3.ResetContent();//清空列表框 IptoSend=ntohl(myIp&myMask);//给IP地址最后一个字节赋初值,发包时也在此基础上逐个发包 AfxBeginThread(ThreadtoListen,(LPVOID)this);//启动接收线程 AfxBeginThread(ThreadtoSend,(LPVOID)this); //启动发送线程 m_btn1.SetWindowText("停止获取"); m_btn1.EnableWindow(true); } else if (myBtnText.Compare("停止获取")==0) { m_btn1.EnableWindow(false); while (myThreadCount>0)//等待线程结束,同时把线程函数循环条件信号量设置为false { isCatching=false; Sleep(50); } m_btn1.SetWindowText("获取MAC"); m_btn1.EnableWindow(true); } (10)线程体函数 线程函数必须定义为static UINT类型,返回值为0。在线程函数中,先定义主进程类的一个对象,用该对象调用线程的托管函数。 实现代码如下: //接收ARP回复包线程 UINT CSharpDlg::ThreadtoListen(LPVOID lParam) { CSharpDlg *myListen=(CSharpDlg *)lParam;//创建CSharpDlg类对象 myListen->ArpListening();//利用此对象调用本类的成员函数 return 0; } //发送ARP请求包线程 UINT CSharpDlg::ThreadtoSend(LPVOID lParam) { CSharpDlg *mySend=(CSharpDlg *)lParam;//创建CSharpDlg类对象 mySend->ArpSending();//利用此对象调用本类的成员函数 return 0; } (11)退出函数 该函数是“退出”按钮的点击事件函数,程序先判断当前是否有线程正在执行,如果么有,则执行退出操作;如果有,则提示用户,如果用户执意退出,则终止线程后退出。 实现代码如下: if (myThreadCount>0) { if (IDYES == MessageBox("子线程仍在运行,是否结束它们并退出程序?", "sharp", MB_YESNO)) { m_text.SetWindowText("正在终止子线程,请稍候……");// 在窗口标题栏上显示当前操作信息 while (myThreadCount>0)// 等待子线程结束 { isCatching = false; Sleep(20); } } } if (alldevs!=NULL)// 释放设备列表 { pcap_freealldevs(alldevs); alldevs = NULL; } DeleteCriticalSection(&sp);// 删除临界区对象 CDialog::OnOK();// 关闭主窗口,程序结束 五、测试 测试按开发过程分为两个部分,即黑盒测试和白盒测试。 事实上,在编写代码过程中,不断进行白盒测试,即了解实现原理和细节后,阅读并编译程序,以确定没有语法和逻辑错误。 开发完成后进行黑盒测试,即生成可执行程序,直接运行可执行程序,看可执行程序是否能完成预定功能。下面给出黑盒测试的过程: 1、开始 程序点击运行后,自动列出当前本机所有可用设备。截图如下: 2、列出设备详细信息 用户鼠标选择左上角某一设备后,如果该设备可用,则在左下方的列表框内列出该设备的详细信息,同时使得“获取MAC”按钮可用。截图如下: 如果不可用,则弹出警示框。截图如下: 3、获取MAC地址 在选择有效设备后,点击“获取MAC”按钮,程序自动在右侧的列表框内列出获取到的局域网内IP地址和MAC地址信息。截图如下: 4、程序说明子窗体 点击“About us”按钮,弹出程序说明子窗体。截图如下: 5、退出 点击“退出”按钮,程序先判断当前是否有线程正在执行,如果么有,则执行退出操作;如果有,则提示用户,如果用户执意退出,则终止线程后退出。截图如下: 测试结果符合预想。 不管是黑盒测试,还是白盒测试,都是多次反复进行的,不是一蹴而就,不断修改程序,不断接近最后的程序代码。经过测试,我认为,程序能够完成开发要求,达到了预定效果。 六、总结 本次作业总共进行了将近一周的时间。由于本人首次接触基于VC6.0的界面编程(MFC),所以实际上是边学边做。一边学习基于winpcap的网络编程原理,一边学习MFC编程。 在调试程序的过程中,出现了许多明显问题,特别是调试的过程,简直可以用痛苦和艰难来形容。遇到问题,分析问题或是请教别人,到最后解决问题,深入地了解了问题,也学到了好多东西。 问题总结如下: 1、指针概念不清晰。多次把指针定义好后,忘记了赋值,导致指针指向了一个空地址,调试出现错误。通过本次作业,对指针,引用等的概念有了明显的认识。 2、类的观念不强。虽然C++的特点是面向对象编程,但类和对象的观念并没有深入的建立起来,实际上还是停留在面向过程的阶段。通过本次作业,类的观念明显增强。 3、线程知识有了新认识。对于VC6.0的线程的应用,本次作业是第一次。 4、学会了窗体控件应用。对于VC+MFC的控件使用,则是第一次,特别是控件和控件的对象,控件对象和函数之间的关联关系有了清晰的理解。 总之,经过本次作业实践,我认为在对ARP工作的原理的理解和MFC编程两个层面都有了较大长进,取得了一定的进步,达到了预定目的。 七、参考资料 [1] 吴功宜,《计算机网络技术》南开大学信息技术科学学院 [2] 吴功宜等,《计算机网络高级软件编程技术》南开大学信息技术科学学院 [3] 刘璟等,《高级语言C++程序设计》南开大学信息技术科学学院 提示出错 结束 主窗体界面的列表框3显示获取的活动主机MAC地址 监听ARP响应数据包 向局域网内主机逐个发送ARP请求包 获得本机IP地址,和MAC地址,并在列表框2中显示出来 开启两个工作线程,分别负责向局域网内发送ARP请求包和接受局域网返回的ARP回复包 可用? 用户选择一个设备 获取所有可用设备,并在列表框显示 开始 否 是
/
本文档为【2120090356_苑爱泉_第二次作业_界面版】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
热门搜索

历史搜索

    清空历史搜索