《精品资料(2021-2022年收藏)计算机网络课程设计实验报告北京科技大学arp、ftp、ip包.docx》由会员分享,可在线阅读,更多相关《精品资料(2021-2022年收藏)计算机网络课程设计实验报告北京科技大学arp、ftp、ip包.docx(37页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、计算机网络课程设计实验报告任课教师: 班级: 学号: 姓名: 目录实验一 ARP封装并发送3课程设计目的:3课程设计要求:3课程设计分析:3程序设计分析:4实验结果6源程序6实验二 解析IP数据包9课程设计目的:9课程设计要求:9课程设计分析:10程序设计分析10实验结果14源代码14实验三 FTP客户机18课程设计目的:18课程设计要求:18课程设计分析:18程序设计原理:18实验结果24源代码25实验心得体会38实验一 ARP封装并发送课程设计目的: ARP协议用于完成IP地址与MAC地址之间的转换。通过封装与发送ARP数据包,加深对ARP协议的理解,掌握ARP帧结构和工作原理及其对协议栈
2、的贡献。课程设计要求: 编写程序,根据ARP帧的结构,封装ARP帧。 要求程序为命令行程序,以命令行的形式运行:SENDARP S-IP S-MAC D-IP D-MAC其中:SENDARP:可执行程序名S-IP:源IP地址S-MAC:源MAC地址D-IP:目的IP地址D-MAC:目的MAC地址课程设计分析: 使用winpcap访问网卡,手动封装 定义ARP的数据结构 填充数据包 发送数据包程序设计分析:ARP协议及工作原理 ARP协议是“Address Resolution Protocol”(地址解析协议)的缩写。在局域网中,网络中实际传输的是“帧”,帧里面是有目标主机的MAC地址的。在以
3、太网中,一个主机要和另一个主机进行直接通信,必须要知道目标主机的MAC地址。但这个目标MAC地址是如何获得的呢?它就是通过地址解析协议获得的。所谓“地址解析”就是主机在发送帧前将目标IP地址转换成目标MAC地址的过程。ARP协议的基本功能就是通过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。ARP的基本运行过程是:1) 主机A希望发送数据分组给主机B,但不知道B的物理地址。2) A发送广播报文,要求B主机用它的物理地址来响应。3) 网站上所有主机都接收到这个分组。4) B识别出自己的IP地址,发送应答报文,告诉A自己的物理地址。 ARP的分组格式物理帧头(14B)ARP帧
4、结构(28B)填充数据(18B)CRC(4B)图一 ARP分组格式目的MAC (6B)源MAC(6B) 类型(2B) 图2 物理帧头 0 8 16 24 31(位) 硬件类型(Ethernet:0x1) 上层协议类型(IP:0x0800)硬件地址长度(0x6)IP地址长度(0x4) 操作(请求: 0x1; 应答: 0x2) 源MAC地址 源MAC地址 源IP地址 源IP地址 目的MAC地址 目的MAC地址 目的IP地址图3 ARP帧结构ARP包的填充 将命令行的参数作适当的转换后填到ARP分组结构的各字段中即可。 要注意的是,填充请求包时。因为包要在Ethernet上广播,所以,物理帧头的“目
5、的MAC”字段要填充为FFFFFFFFFFFF;而ARP帧结构中的目的MAC可填充为任意值,因为它此时不起作用。“填充数据”字段要填充为0。实验结果源程序#include #include #pragma comment(lib,ws2_32.lib)#pragma comment(lib,wpcap.lib)#include #pragma pack(1)struct arp_packet /arp包结构 unsigned char dest_mac6; /目标主机MAC地址 unsigned char source_mac6; /源端MAC地址 unsigned short eh_type
6、; /以太网类型 unsigned short hardware_type; /硬件类型:以太网接口类型为1 unsigned short protocol_type; /协议类型:IP协议类型为0X0800 unsigned char add_len; /硬件地址长度:MAC地址长度为6B unsigned char pro_len; /协议地址长度:IP地址长度为4B unsigned short option; /操作:ARP请求为1,ARP应答为2 unsigned char sour_addr6; /源MAC地址:发送方的MAC地址 unsigned long sour_ip; /源
7、IP地址:发送方的IP地址 unsigned char dest_addr6; /目的MAC地址:ARP请求中该字段没有意义;ARP响应中为接收方的MAC地址unsigned long dest_ip; /目的IP地址:ARP请求中为请求解析的IP地址;ARP响应中为接收方的IP地址 unsigned char padding18; ; #pragma pack()unsigned char* BuildArpPacket(unsigned char* source_mac, unsigned char* dest_mac,unsigned long src_ip,unsigned long
8、dest_ip);void main()pcap_if_t *alldevs, *d;/用于存储网络设备int i=0; char errbufPCAP_ERRBUF_SIZE;if(pcap_findalldevs(&alldevs,errbuf)=-1)fprintf(stderr,Error in pcap_findalldevs:%sn,errbuf);exit(1);for(d=alldevs;d;d=d-next)printf(%d. %s,+i,d-name);if(d-description) printf(%s)n,d-description);else printf( (N
9、o description available)n);if(i=0)printf(n找不到指定接口.n);printf(选择设备号 (1-%d):,i);int inum;pcap_t *adhandle;unsigned char *packet;scanf(%d, &inum);if(inum i)printf(n超出范围.n);pcap_freealldevs(alldevs);for(d=alldevs, i=0; inext, i+);char chs_src_ip16 = 1.1.1.1; char chs_dest_ip16 = 2.2.2.2; unsigned long ul
10、_src_ip = 0u; unsigned long ul_dest_ip = 0u; printf(Input the src ip(1.1.1.1):); scanf(%s,chs_src_ip); ul_src_ip = inet_addr(chs_src_ip); printf(Input the dest ip(2.2.2.2):); scanf(%s,chs_dest_ip); ul_dest_ip = inet_addr(chs_dest_ip); unsigned char uchs_src_mac10 = 0;unsigned char uchs_dest_mac10 =
11、0;printf(Input source mac address(11 11 11 11 11 11):);scanf(%x%x%x%x%x%x,&uchs_src_mac0,&uchs_src_mac1,&uchs_src_mac2,&uchs_src_mac3,&uchs_src_mac4,&uchs_src_mac5);printf(Input dest mac address(22 22 22 22 22 22):);scanf(%x%x%x%x%x%x,&uchs_dest_mac0,&uchs_dest_mac1,&uchs_dest_mac2,&uchs_dest_mac3,&
12、uchs_dest_mac4, &uchs_dest_mac5); if(adhandle= pcap_open_live(d-name,65536,1,1000,errbuf) = NULL)fprintf(stderr,n无法打开指定适配器.%s 不支持WinPcapn,d-name);pcap_freealldevs(alldevs);packet = BuildArpPacket(uchs_src_mac, uchs_dest_mac, ul_src_ip, ul_dest_ip);/发送arp包的函数:参数1:网络设备、参数2:arp包、参数3:长度 pcap_sendpacket(
13、adhandle,packet,62);unsigned char* BuildArpPacket(unsigned char* source_mac, unsigned char* dest_mac,unsigned long src_ip,unsigned long dest_ip)static struct arp_packet packet;memcpy(packet.dest_mac,dest_mac,6);memcpy(packet.source_mac,source_mac,6);packet.eh_type = htons(0x0806);packet.hardware_typ
14、e = htons(0x01);packet.protocol_type = htons(0x0800);packet.add_len = 0x06;packet.pro_len = 0x04;packet.option = htons(0x0001);memcpy(packet.sour_addr,source_mac,6);packet.sour_ip = src_ip;packet.dest_ip = dest_ip;memcpy(packet.dest_addr,dest_mac,6);memset(packet.padding,0,18); return (unsigned char
15、*)&packet;实验二 解析IP数据包课程设计目的: 设计一个解析IP数据包的程序,并根据这个程序,说明IP数据包的结构及IP协议的相关问题,从而对IP层的工作原理有更好的理解和认识。课程设计要求: 本实验的目标是捕获网络中的IP数据包,解析数据包的内容,并将结果显示,并同时写入日志文件。 程序的具体要求如下: 以命令行形式运行:Ipparse其中ipparse是程序名 在标准输出中显示捕获的IP包的版本、头长度、服务类型、数据包总长度、数据包标识、分段标志、分段偏移值、生存时间、上层协议类型、头校验和、源IP地址和目的IP地址等内容。课程设计分析: 使用原始套接字或者winpcap,捕获
16、IP数据包 定义IP头部的数据结构 解析并显示数据包程序设计分析网卡设置 为了获取网络中的IP数据包,必须对网卡进行编程,在这里使用套接字(socket)进行编程。但是,在通常情况下,网络通信的套接字程序只能响应与自己硬件地址相匹配的数据包或是以广播形式发出的数据包。对于其他形式的数据包,如已到达网络接口,但却不是发送到此地址的数据包,网络接口在骓投递地址并非自身地址之后将不引起响应,也就是说应用程序无法收取与自己无关的数据包。我们要想获取网络设备的所有数据包,就是需要将网卡设置为混杂模式。使用套接字 套接字分为三种,即流套接字(Stream socket)、数据报套接字(Datagram S
17、ocket)和原始套接字(Raw Socket)。要进行IP层数据包的接收和发送,应使用原始套接字。创建原始套接字的代码如下: Socket sock: Sock=wsasocket(af_inet,sock_raw,ipproto-ip,null,0,wsa-flag-overlapped): 本设计不用考虑超时情况。 创建套接后,IP头就会包含在接收数据包中。然后,我可以设置IP头操作选项,调用setsockopt函数。其中flag设置为true,并设定IP-HDRINCL选项,表明用户可以亲自对IP头进行处理。最后使用bind()函数将socket绑定到本地网卡上。绑定网卡后,需用WSA
18、Ioctl()函数把网卡设置为混杂模式,使网卡能够接收所有的网络数据。如果接收的数据包中的协议类型和定义的原始套接字匹配,那么接收的数据就拷贝到套接字中,因此,网卡就可以接收所有经过的IP包。接收数据包 在程序中可使用recv()函数接收经过的IP包。该函数有四个参数,第一个参数接收操作所用的套接字描述符;第二个参数接收缓冲区的地址;第三个参数接收缓冲区的大小,也就是所要接收的字节数;第四个参数是一个附加标志,如果对所发送的数据没特殊要求,直接设为0。因为IP数据包的最大长度是65535B,因此缓冲区的大小不能小于65535B。设置缓冲区后,可利用循环来反复监听接收IP包,用recv()函数实
19、现接收功能。 定义IP头部的数据结构 程序需要定义一个数据结构表示IP头部。其代码如下: struct IP_HEADER unsigned short ip_version, /*IP的版本号 */ip_hdr_len; /*IP包头的长度*/ip_tos; /*IP包的服务类型*/ip_total_len; /*IP包的总长度*/ip_id; /*IP包的分段标识*/ip_flags; /*IP包的分段标志*/ip_frag_offset; /*IP包的分段偏移*/ip_ttl; /*IP包的生存时间*/ip_proto; /*IP包的高层协议*/ip_hdr_chksum; /*IP包的
20、校验和*/struct IPADDRESS ip_src_addr; /*IP包的源IP地址*/ip_dest_addr; /*IP包的目的IP地址*/ipheader; IP包的解析 解析IP包的字段有两种策略。针对长度为8位、16位和32位的字段(或子字段)时,可以利用IP-HEADER的成员直接获取。要解析长度不是8位倍数的字段(或子字段)时,可以利用C语言中的移位以人、及与、或操作完成。 协议的定义 (包含相应的头文件#include #include): DWORD dwIoControlCode=SIO_RCVALL, /*接收所有的IP包*/dwProtocol=IPPROTO_
21、IP; /*协议类型为IP*/捕获处理 1.加载 Winsock; 2.创建一个接收原始IP包的socket连接;3.绑定到一个接口;4.进行WSAIoctl设置,接收所有的IP数据包。 代码如下:if (WSAIoctl(s, dwIoControlCode, &optval, sizeof(optval),NULL, 0, &dwBytesRet, NULL, NULL) = SOCKET_ERROR)5.接着设定一个线程进行捕获:(1)创建一个接收IP包的链表头;(2)设置一个标识,为真,则不断进行IP包的捕获;(3)建立一个新的结点,将捕获的数据包加入到该结点;(4)如果链表的长度达到
22、指定的长度,创建一个线程对该链表的IP包进行解析;再设置一个在IP数据包链表不足给定的长度,而又中止IP捕获时,对链表的处理;(5)为下一个IP包链表创建一个链表头。6.建立一个进行IP包解析并显示的线程,进行解析IP数据包,然后显示IP数据包。实验结果源代码#include winsock2.h#include ws2tcpip.h#include #include#include #include #pragma comment(lib,ws2_32.lib)#pragma comment(lib,wpcap.lib)using namespace std;/定义IP头typedef st
23、ruct _IP_HEADER unionBYTE Version; /版本(前4位)BYTE HdrLen; /IHL(后4位),IP头的长度; BYTE ServiceType; /服务类型WORD TotalLen; /总长WORD ID; /标识unionWORD Flags; /标志(前3位)WORD Fragoff; /分段偏移(后13位);BYTE TimeToLive; /生命周期BYTE Protocol; /协议WORD HdrChksum;/头部校验和DWORD SrcAddr;/源地址DWORD DstAddr;/目的地址BYTE Options;/选项IP_HEADE
24、R;int main ()SOCKET sock; WSADATA wsData;ofstream outfile(G:logfile.txt,ios:out);/如果初始化失败,程序退出if(WSAStartup(MAKEWORD(2,2),&wsData) != 0)printf(WSAStartup failed!n);return -1;/建立原始socketif(sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP) = INVALID_SOCKET)printf(create socket failedn);return -1;/设置IP头操作选项,其
25、中flag设置为true,用户可以亲自对IP头进行处理BOOL flag = true;if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char*)&flag,sizeof(flag) = SOCKET_ERROR)printf(setsockopt failed!n);return -1;/获取主机名char hostName128;if(gethostname(hostName,100) = SOCKET_ERROR)printf(gethostname failed!n);return -1; /获取本地IP地址hostent *pHostIP;if(
26、pHostIP = gethostbyname(hostName) = NULL)printf(gethostbyname failedn);return -1;/填充SOCKADDR_IN结构sockaddr_in addr_in;addr_in.sin_addr = *(in_addr*)pHostIP-h_addr_list0;addr_in.sin_family = AF_INET;addr_in.sin_port = htons(6000); /把原始socket绑定到本地网卡上if(bind(sock,(PSOCKADDR)&addr_in,sizeof(addr_in) = SO
27、CKET_ERROR)printf(bind failedn);return -1; /设置SOCK_RAW为SIORCVALL,以便接收所有的确IP包 #define IO_RCVALL _WSAIOW(IOC_VENDOR,1) DWORD dwBufferLen10;DWORD dwBufferInLen = 1;DWORD dwBytesReturned = 0;if(WSAIoctl(sock,IO_RCVALL,&dwBufferInLen,sizeof(dwBufferInLen),&dwBufferLen,sizeof(dwBufferLen),&dwBytesReturned
28、,NULL,NULL) = SOCKET_ERROR)printf(ioctlsocket faildn);return -1; /设置接收数据包的缓冲区长度 #define BUFFER_SIZE 65535 char bufferBUFFER_SIZE; /监听网卡printf(开始解析经过本机的IP数据包!nn);while(true) /使用recv()函数接收经过的IP包/*第一个参数接收操作所用的套接字描述符;第二个参数接收缓冲区的地址;第三个参数接收缓冲区的大小,也就是所要接收的字节数;第四个参数是一个附加标志,如果对所发送的数据没特殊要求,直接设为0。因为IP数据包的最大长度是
29、65535B,因此缓冲区的大小不能小于65535B。设置缓冲区后,可利用循环来反复监听接收IP包,用recv()函数实现接收功能*/int size = recv(sock,buffer,BUFFER_SIZE,0);IP_HEADER ip=*(IP_HEADER *)buffer; cout-endl; cout版本:4)endl; cout头部长度:(ip.HdrLen &0x0f)*4)endl; cout服务类型:Priority5), Service1)&0x0f)endl; cout总长度:ip.TotalLenendl;cout标识符:ip.IDendl; cout标志位:15
30、)&0x01),DF= 14)&0x01),Mf=13)&0x01)endl;cout片偏移:(ip.Fragoff&0x1fff)endl; cout生存周期:(int)ip.TimeToLiveendl; cout协议:Protocol(int)ip.Protocolendl; cout头部校验和:ip.HdrChksumendl; cout原地址:inet_ntoa(*(in_addr *)&ip.SrcAddr)endl; cout目的IP地址:inet_ntoa(*(in_addr *)&ip.DstAddr)endl; outfile-endl; outfile版本:4)endl;
31、 outfile头部长度:(ip.HdrLen &0x0f)*4)endl; outfile服务类型:Priority5), Service1)&0x0f)endl; outfile总长度:ip.TotalLenendl; outfile标识符:ip.IDendl; outfile标志位:15)&0x01),DF= 14)&0x01),Mf=13)&0x01)endl; outfile片偏移:(ip.Fragoff&0x1fff)endl; outfile生存周期:(int)ip.TimeToLiveendl; outfile协议:Protocol(int)ip.Protocolendl; o
32、utfile头部校验和:ip.HdrChksumendl; outfile原地址:inet_ntoa(*(in_addr *)&ip.SrcAddr)endl; outfile目的IP地址:inet_ntoa(*(in_addr *)&ip.DstAddr)setCommand(CWD ,Direct);命令格式规范化,例如要进入根目录下的tools目录就可使命令规范化为”CWD/TOOLSrn”(其中代表一个空格)。请注意这里的字符串一个字符都不能差,更详细的说明参见RFC959文档,内有详细说明。client-sendCommand();sendCommand()即为发送规范化的命令。cl
33、ient-receiveCommand();接收应答信息并分离应答码。2) 复杂的有数据传输方式:带有数据传输的文件需要建立数据套接字来传输,建立数据套接字的方式有两种,主动模式,被动模式。简单的介绍下这两种模式:A. 主动模式:主动模式是客户端发送数据连接请求,并告知服务器端已开放的正在监听的端口,由服务器发送连接请求,建立连接。B. 被动模式:被动模式是发送被动连接模式命令,服务器由应答码返回可用的ip与端口信息,通过对应答码的分析找到端口号,用客户端连接正在监听的服务器端口。由于主动模式的局限性,即在有NAT转换时无法将信息送至正确的位置,因为经过NAT技术转换的端口号代表的是一台计算机
34、的内网编号,而不是计算机网络端口的端口号,所以无法正确送达。也因此本程序采用被动模式。当数据链路建立完成后,数据链路需要进行初始化,数据链路有两种常用的模式,ASCII方式和二进制方式,下面介绍下这两种传输方式:A. ASCII方式:ASCII方式传输数据时,数据以一个字符一个字符为单位传输,这种方式一般使用在文本文件等一些使用ASCII码来储存信息的文件传输中ASCII方式同样用于文件目录列表的传输。使用ASCII方式传输二进制文件,例如可执行文件,图片文件,动画文件,等等,会使文件损坏。B. 二进制方式:二进制文件传输数据时,数据以比特为单位传输,到达客户端后,由操作系统将其合并转换。使用
35、2进制可以传输绝大多数的文件,但是二进制方式也有其不可避免的局限性,二进制传输文件要求服务器和客户端使用相同类型的操作系统,如果不同类型下载的文件无法使用,但可以使用二进制方式传输到另一台拥有相同操作系统的机器上,文件可以使用。所以二进制方式传输同样不适用于网络传输。确定了传输类型后,就可以创建套接字,格式化命令,发送命令,并接受信息和应答码与两不同的SOCKET上。char* filename = new char512;memset(filename,NULL,512);/*PASV方式传输*/client-setCommand(PASV);client-sendCommand();client-receiveCommand();/*分析应答码取端口号*/client-getPort();/*设置传输类型初始化数据套接字*/client-setCommand(TYPE ,I);client-sendCommand();client-receiveCommand();client-interlizeDataSocket();coutfilename;memset(temp,NULL,512);/*格式化路径信息Direct*