资源描述
网络编程技术实验指导书李笑平 编淮阴工学院计算机工程学院2010年1月目 录实验1:基于NetBIOS的网络通信程序设计1实验2:基于socket的简单网络程序设计2实验3:流式套接字程序设计10实验4:数据报式套接字程序设计12实验5:原始套接字程序设计15实验6:基于WinPcap的程序开发18实验7:基于CAsyncSocket类网络通信程序设计3449实验1:基于NetBIOS的网络通信程序设计1 实验目的与要求掌握NetBIOS方式的网络编程模式。理解并能应用NetBIOS实现C/S会话通信程序设计。2 实验设备与实验环境连接到互联网上的IBM PC机两台。计算机硬件要求:Intel Pentium5 处理器、256MB以上内存,Ethernet网卡,网线若干。计算机软件要求:MS Windows 9x/2000/XP操作系统,TCP/IP协议,Visual c+6.0/.net系统。3 实验内容与步骤利用NetBIOS编写C/S会话通信程序以实现如下程序设计要求,客户端与服务器建立会话连接之后,客户端向服务器发送一个简单的四则算式(只含一个算符),服务器收到这个算式后,对其进行计算,并将计算结果回送给客户端,客户端将计算结果显示出来。注意:整个程序执行过程要求是基于NetBIOS命令实现的。程序的详细设计过程可参考教材。实践题1)在完成上面这个实验的基础上,将服务器和客户端分别放在两台不同的机器上进行测试。2)在以上程序的基础上进行改进,使服务器能提供对复杂四则算式的求值服务。即客户端发送一个复杂的四则算式,服务器收到此算式后对其求值并将结果回送给客户端。4 实验小结1)要求理解掌握NetBIOS的工作原理。5 实验报告本次交实验报告,内容包括:1 实验目的与要求;2 实验设备与实验环境;3 实验内容与实验步骤(实验步骤、源代码);4 实验心得(可以是实验总结、难点讨论、意见建议等)。实验2:基于socket的简单网络程序设计1 实验目的与要求:初步掌握TCP和UDP方式的网络编程模式。能运用Winsock提供的API函数接口进行网络程序的编写。2 实验设备与实验环境:连接到互联网上的IBM PC机两台。计算机硬件要求:Intel Pentium5 处理器、256MB以上内存,Ethernet网卡,网线若干。计算机软件要求:MS Windows 9x/2000/XP操作系统,TCP/IP协议,Visual c+6.0/.net系统。3 实验内容与步骤:3.1 工程的创建在VC6里边新建一个工程,选择:Win32 Console Application,输入工程名。具体方法:启动VC6.0,进入【文件】-【新建】,然后在【新建工程】的对话框中选择:Win32 Console Application,输入一个工程名及保存路径,点击【确定】,如图1、图2所示图1图23.2 Winsock的初始化图3图4在Windows环境下进行网络程序设计时,所有的Winsock函数都是从ws2_32.dll导出的,我们可以通过#pragma comment(lib, ws2_32.lib)语句链接到该库文件。但在使用Winsock函数之前还必须调用WSAStartup函数对库资源进行初始化工作,使用完毕后,在退出程序之前,我们还必须调用WSACleanup函数来释放库资源。为了便于程序的设计,我们先设计一个CInitSock类来管理WinSock库。在工程中添加新类的过程步骤如下:1)在VC6.0环境中点击【insert】-【new a class】菜单,如图3所示;2)在弹出的对话框中,我们选定class type为Generic Class,在Name输入框中输入类的名称CInitSock,然后点击确定即可,具体如图4所示。我们在InitSock.h和InitSock.cpp添加代码,详细代码如下所示。/ initSock.h /#if !defined(AFX_INITSOCK_H_70EFFE09_9598_4C98_A067_29100702ACE8_INCLUDED_)#define AFX_INITSOCK_H_70EFFE09_9598_4C98_A067_29100702ACE8_INCLUDED_#if _MSC_VER 1000#pragma once#endif / _MSC_VER 1000#pragma comment(lib, ws2_32.lib)#include winsock2.hclass CInitSock public:CInitSock();virtual CInitSock();#endif / !defined(AFX_INITSOCK_H_70EFFE09_9598_4C98_A067_29100702ACE8_INCLUDED_)/ InitSock.cpp: implementation of the CInitSock class. /#include InitSock.h/ Construction/Destruction/CInitSock:CInitSock()WSADATA wsaData;WORD sockVersion = MAKEWORD(2, 2);if(:WSAStartup(sockVersion, &wsaData) != 0)exit(0);CInitSock:CInitSock():WSACleanup();然后我们在工程中新建一个.cpp的源文件,在这个源文件中填写我们的main()主函数代码(代码逻辑见后续小节),并且在我们的.cpp源文件的开始部分包含如下头文件:”initSock.h”和”stdio.h”,还申明一个CInitSock类对象,具体代码可如下所示:#include InitSock.h#include stdio.hCInitSock initSock;/ 初始化Winsock库3.3 基于TCP的编程模式服务器端:函数具体说明请参考MSDN或者讲义或者教材。1)创建socketSocket函数的原型为:SOCKET socket(int family, int type, int protocol);创建一个流式套接字可以如下所示:SOCKET _socket=socket(AF_INET,SOCK_STREAM,0);创建一个数据报式套接字可以如下所示:SOCKET _socket=socket(AF_INET,SOCK_DGRAM,0);在面向TCP的应用中我们应该创建一个流式套接字。2)绑定bindbind函数的原型:int bind (SOCKET sockfd, const struct sockaddr *myaddr, socklen_t addrlen)绑定是将一个套接字和一个套接字地址绑定在一起,在调用bind之前还必须设定服务器地址。如下可以设定一个服务器地址:struct sockaddr_in _sockaddr_in; /定义地址结构体_sockaddr_in.sin_addr.S_un.S_addr=htonl(INADDR_ANY); /服务器地址取本机任何可用的IP地址_sockaddr_in.sin_family=AF_INET;_sockaddr_in v.sin_port=htons(6000); /服务器端口使用6000下面的语句将套接字和地址addrSrv绑定在一起:bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR);htonlhost to network long,就是把主机的字节顺序转化成网络上的字节顺序,参数为long int型;同理htonshost to network short,只不过参数为short int型。地址族必须为AF_INET,端口为6000,INADDR_ANY表示可以用本机的任何IP地址。bind命令绑定本地刚才创建好的socket。格式如上。3)监听listenlisten函数的原型为:int listen(SOCKET sockfd, int queue_length);如下语句可以对一个套接字sockSrv进行监听:listen(sockSrv,5);4)接受连接accept,接收/发送数据send/recvstruct sockaddr_in addrClient;int len=sizeof(SOCKADDR);char sendBuf = “TCP Server Demo!rn”;while(1)SOCKET sockConnect=accept(sockSrv, (SOCKADDR*)&addrClient, &len); /接受客户端连接if(sockConnect = INVALD_SOCKET)printf(“Failed accept!”);continue;printf(接受到一个连接:%s, inet_ntoa(addrClient.sin_addr); /显示客户端的IPsend(sockConnect,sendBuf,strlen(sendBuf)+1,0);/关闭连接套接字,终止通信closesocket(sockConnect);inet_ntoa函数转换Internet网络地址为点分十进制格式。5)关闭监听套接字,终止服务器closesocket(sockSrv);客户端:1)创建连接套接字SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);2)设置通信地址struct sockaddr_in addrSrv;addrSrv.sin_addr.S_un.S_addr=inet_addr(127.0.0.1);/表示连接本地IPaddrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);3)向服务器提出连接请求:int con;con = connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR);/连接服务器if(con != 0) /连接服务器失败printf(cannot connect to servern);return 0;4)连接成功后,接收服务器端发送来的信息char recvBuf100;int nRecv;nRecv = recv(sockClient,recvBuf,100,0); /接收服务端的数据if(nRecv 0)recvBufnRecv = 0;printf(接收到的数据:%sn,recvBuf);5)关闭套接字,终止通信closesocket(sockClient);3.4 基于UDP的编程模式服务器端:1)创建一个数据报套接字SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);2)设置并绑定服务器地址struct sockaddr_in addrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);addrSrv.sin_port=htons(6000);bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(addrSrv);3)接收来自客户端的信息struct sockaddr_in addrClient;int len=sizeof(SOCKADDR);char recvBuf1024;while(1)int nRecv = recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len);/接收客户端的数据if(nRecv 0)recvBufnRecv = 0;printf(%sn,recvBuf);4)关闭套接字,终止服务器closesocket(sockSrv);客户端:1)创建一个数据报套接字SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);2)设置远程的服务器地址sockaddr_in addrSrv;addrSrv.sin_addr.S_un.S_addr=inet_addr(127.0.0.1);addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);3)向服务器发送信息char sendBuf100;strcpy(sendBuf,Hello);sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrSrv,sizeof(addrSrv); /向服务端发送“hello”4)关闭套接字,终止通信closesocket(sockClient);inet_addr函数转换点分十进制格式的IP地址为适合IN_ADDR structure的格式.3.5 实践题1)在本机上测试通过的基础上,两台电脑测试连接对方的程序,ip地址可以通过命令ipconfig获取。4 实验小结1)要求掌握流式和数据报socket的编程模式和实现。2)要求能熟练使用MSDN在线帮助。3)winsock实现的函数名和该函数的作用要记住,具体的格式可以查询MSDN或者教材。5 实验报告本次交实验报告,内容包括:1 实验目的与要求;2 实验设备与实验环境;3 实验内容与实验步骤(实验步骤、源代码);4 实验心得(可以是实验总结、难点讨论、意见建议等)。实验3:流式套接字程序设计1 实验目的与要求进一步掌握TCP和UDP方式的网络编程模式。能运用Winsock提供的API函数接口进行网络程序的编写。理解并能应用流式套接字实现C/S通信。2 实验设备与实验环境连接到互联网上的IBM PC机两台。计算机硬件要求:Intel Pentium5 处理器、256MB以上内存,Ethernet网卡,网线若干。计算机软件要求:MS Windows 9x/2000/XP操作系统,TCP/IP协议,Visual c+6.0/.net系统。3 实验内容与步骤编写一个基于流式套接字的C/S通信程序,程序要求:客户端与服务器建立连接之后,客户端向服务器发送一个简单的四则算式(只含一个算符),服务器收到这个算式后,对其进行计算,并将计算结果回送给客户端,客户端将计算结果显示出来。3.1工程的创建(同实验1)3.2 Winsock的初始化(同实验1)3.3程序的工作原理程序的工作原理基本同实验1的基于TCP的通信,在本实验中,服务器与客户端应先设计好应用程序通信协议(即收发数据的数据格式),可以将一个简单算式的数据类型定义如下:typedef struct arithmeticint operator; /运算类型:0(加)、1(减)、2(乘)、3(除)。其他int data_1; /操作数1int data_2; /操作数2 Arithmetic;客户端若提出服务请求时,可将一个算式用如上的结构进行封装,例如要计算56,可封装这个算式为:Arithmetic a;a.operator = 2;a.data_1 = 5;a.data_2 = 6;然后客户端调用send函数将数据a发送给服务器,示例代码可如下所示:Send(sClient, (void*)&a, sizeof(a);而服务器收到这个数据后,用Arithmetic结构解析这个数据,并根据运算符的类型作相应的运算,最后将运算结果回送给客户端。注意:我们须将数据类型struct arithmetic放在一个头文件中说明,且必须将这个头文件分别包含在客户端和服务器的工程中。程序的详细设计过程略。3.4 实践题1)在完成上面这个实验的基础上,将服务器和客户端分别放在两台不同的机器上进行测试。2)在以上程序的基础上进行改进,使服务器能提供对复杂四则算式的求值服务。即客户端发送一个复杂的四则算式,服务器收到此算式后对其求值并将结果回送给客户端。4 实验小结1)要求理解掌握Select I/O模型的工作原理,使其能实现非阻塞的通信。5 实验报告本次交实验报告,内容包括:1 实验目的与要求;2 实验设备与实验环境;3 实验内容与实验步骤(实验步骤、源代码);4 实验心得(可以是实验总结、难点讨论、意见建议等)。实验4:数据报式套接字程序设计1 实验目的与要求进一步掌握TCP和UDP方式的网络编程模式。能运用Winsock提供的API函数接口进行网络程序的编写。理解广播程序的工作原理,利用数据报式套接字编写一个广播发送与接收程序。在局域网中实现广播。2 实验设备与实验环境连接到互联网上的IBM PC机两台。计算机硬件要求:Intel Pentium5 处理器、256MB以上内存,Ethernet网卡,网线若干。计算机软件要求:MS Windows 9x/2000/XP操作系统,TCP/IP协议,Visual c+6.0/.net系统。3 实验内容与步骤3.1工程的创建(同实验1)3.2 Winsock的初始化(同实验1)3.3 广播程序的工作原理及实现过程一、广播信息的发送端1)创建一个数据报套接字用于发送广播信息创建一个数据报式套接字可如下所示:SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);2)设置广播选项,使SO_BROADCAST选项有效我们可以利用setsockopt函数来设置套接字选项,设置广播选项的具体级别与选项名称分别为:SOL_SOCKET、SO_BROADCAST。例如可如下设置一个套接字sBroadCast的广播选项。BOOL bBroadcast = true;setsockopt(sBroadCast, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof(BOOL);3)设置广播地址及广播端口号,这时的接收方地址应该设为地址INADDR_BROADCAST。如下所示(另还需要设置其它域):SOCKADDR_IN bcast;bcast.sin_addr.s_addr = INADDR_BROADCAST; / :inet_addr(255.255.255.255);4)发送广播,调用sendto函数发送广播,可如下程序所示:printf( 开始向网络中发送广播数据. n n);char sz = This is just a test. rn;while(TRUE)sendto(s, sz, strlen(sz), 0, (sockaddr*)&bcast, sizeof(bcast);Sleep(5000);5)关闭套接字closesocket (s);二、广播信息的接收端1)创建一个数据报套接字用于接收广播信息2)绑定一个本地地址,指明广播端口号(要和发送方设置端口号的相同)作为接收端口。3)接收广播,调用recvfrom函数接收广播:printf( 开始接收广播数据. nn);SOCKADDR_IN addrRemote;int nLen = sizeof(addrRemote);char sz256;while(TRUE)int nRet = :recvfrom(s, sz, 256, 0, (sockaddr*)&addrRemote, &nLen);if(nRet 0)sznRet = 0;printf(sz);4)程序结束前,关闭套接字closesocket (s);3.4 实践题1)在本机上测试通过的基础上,利用多台电脑,将其中一台电脑作为广播信息的发送端,其它电脑作为广播信息的发送端,以测试该广播程序。4 实验小结1)进一步熟悉掌握数据报socket的编程模式和实现。2)进一步理解广播的工作原理。3)在VC6.0环境下实现广播程序的设计。5 讨论在本次实验中,我们利用数据报套接字来发送和接收广播信息,试讨论:不利用数据报套接字而改用流套接字,我们是否也能实现广播信息的发送和接收?若能的话,应如何实现?6 实验报告本次交实验报告,内容包括:1 实验目的与要求;2 实验设备与实验环境;3 实验内容与实验步骤(实验步骤、源代码);4 实验心得(可以是实验总结、难点讨论、意见建议等)。实验5:原始套接字程序设计1 实验目的与要求能运用Winsock提供的API函数接口进行网络程序的编写。理解原始套接字的工作原理。在局域网能使用原始套接字进行相关程序设计。在局域网中使用原始套接字对数据报进行捕获、监听。2 实验设备与实验环境连接到互联网上的IBM PC机两台。计算机硬件要求:Intel Pentium5 处理器、256MB以上内存,Ethernet网卡,网线若干。计算机软件要求:MS Windows 9x/2000/XP操作系统,TCP/IP协议,Visual c+6.0/.net系统。3 实验内容与步骤3.1工程的创建(同实验1)3.2 Winsock的初始化(同实验1)3.3 TCP/IP协议数据结构的说明3.1.1 在工程中新建一个protoinfo.h头文件,在这个头文件中根据TCP/IP协议中各协议的格式、说明TCP、IP、ICMP、UDP、IGMP、ARP协议的头部结构,以及以太帧的帧头结构。各数据类型分别为:以太帧头部类型及指针:ETHeader, *PETHeader;ARP报文头部类型及指针:ARPHeader, *PARPHeader;IP报文头部类型及指针:IPHeader, *PIPHeader; TCP报文头部类型及指针:TCPHeader, *PTCPHeader;UDP报文头部类型及指针:UDPHeader, *PUDPHeader;ICMP报文头部类型及指针:ICMPHeader, *PICMPHeader;3.1.2 在工程的主程序文件中包含protoinfo.h头文件:#include “protoinfo.h”3.4 包含mstcpip.h头文件,并加入Advapi32.lib库在主程序文件用预编译指令#pragma comment(lib, Advapi32.lib)加入Advapi32.lib库;包含mstcpip.h头文件的过程如下:在工程主程序文件中包含mstcpip.h头文件,在VC6.0中没有该头文件,你可以到相关网站下载该头文件,或者自己编辑一个mstcpip.h头文件,方法如下:1、 在工程中新建一个头文件,文件名为mstcpip.h;2、 在mstcpip.h中添加如下内容:#ifndef _MSTCPIP_INC#define _MSTCPIP_INC/ Copyright (C) Microsoft Corporation, 1996-1999#if _MSC_VER 1000#pragma once#endif/* Argument structure for SIO_KEEPALIVE_VALS */struct tcp_keepalive u_long onoff;u_long keepalivetime;u_long keepaliveinterval; / New WSAIoctl Options#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)#define SIO_RCVALL_MCAST _WSAIOW(IOC_VENDOR,2)#define SIO_RCVALL_IGMPMCAST _WSAIOW(IOC_VENDOR,3)#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)#define SIO_ABSORB_RTRALERT _WSAIOW(IOC_VENDOR,5)#define SIO_UCAST_IF _WSAIOW(IOC_VENDOR,6)#define SIO_LIMIT_BROADCASTS _WSAIOW(IOC_VENDOR,7)#define SIO_INDEX_BIND _WSAIOW(IOC_VENDOR,8)#define SIO_INDEX_MCASTIF _WSAIOW(IOC_VENDOR,9)#define SIO_INDEX_ADD_MCAST _WSAIOW(IOC_VENDOR,10)#define SIO_INDEX_DEL_MCAST _WSAIOW(IOC_VENDOR,11) #endif3、 在工程主程序文件中包含该头文件:#include “mstcpip.h”3.5 网络监听的工作原理3.5.1 TCP报文的解析处理用TCP报文头部指针指向传递来的TCP报文,对其TCP头部各个域进行解析。3.5.2 IP报文的解析处理用IP报文头部指针指向传递来的IP报文,对其IP头部各个域进行解析。3.5.3 在主程序中,将套接字设为混杂模式,然后捕获数据包,将捕获到的数据包进行IP解析处理,具体的工作流程:a 创建一个原始套节字b 获取本地IP地址c 绑定套接字d 对原始套接字设置SIO_RCVALL控制代码,以便接收所有的IP包,将套接字设为混杂模式。(调用ioctlsocket函数)e 开始接收封包f. 进行IP解析g 退出程序前关闭套接字。3.6 编译、调试、链接,生成可执行文件。3.7 实践题在局域网中运行所生成的程序,测试其监听结果。4 实验报告本次交实验报告,内容包括:1 实验目的与要求;2 实验设备与实验环境;3 实验内容与实验步骤(实验步骤、源代码);4 实验心得(可以是实验总结、难点讨论、意见建议等)。实验6:基于WinPcap的程序开发1 实验目的与要求:掌握WinPcap方式的网络编程模式。理解并能应用WinPcap实现网络数据包的捕获程序设计。2 实验设备与实验环境:连接到互联网上的IBM PC机两台。计算机硬件要求:Intel Pentium5 处理器、256MB以上内存,Ethernet网卡,网线若干。计算机软件要求:MS Windows 9x/2000/XP操作系统,TCP/IP协议,WinPcap430,Visual c+6.0/.net系统。3 实验内容与步骤:一、WinPcap介绍1、WinPcap简介大多数Unix操作系统提供了一套允许应用程序直接与网络相互联系的系统调用。这些指令对于那些需要通过网络捕获连续的包数据而不用内核进行过多的干预的包捕获应用程序非常有用。在Windows环境下,WinPcap就是这样一类工具。WinPcap是一个在Windows操作系统下的免费、公开的用于直接访问网络的开发工具包(编程API)。大多数Windows网络应用程序都是通过Winsock API(Windows套接口)这类高级编程接口访问网络的。这种方法允许在网络上进行简单的数据传送,因为操作系统的TCP/IP协议栈实现软件会处理底层细节(协议操作、流程重组等等),并提供一个类似于读写文件的函数接口。然而,有时候“简便方法”并不能满足实际需要。有些程序希望绕过TCP/IP协议栈,直接处理底层网络中的通信数据,它们需要对网络进行底层进行直接访问,即在没有类似协议栈(TCP/IP协议栈)的实体介入条件下对网络进行原始访问。基于Winsock API编程,应用程序是通过调用操作系统提供的编程接口访问TCP/IP协议栈实现网络通信的。基于WinPcap编程,网络程序实际上是绕开操作系统的TCP/IP协议栈直接通过底层网络发送数据,因此,网络程序可以实现一些更低级、更灵活的功能。2、WinPcap的目的和用途开发WinPcap的目的是为Win32应用程序提供一种直接访问底层网络的能力。通过WinPcap,网络应用程序可以实现如下功能:1) 捕获原始数据包,包括发送到本主机以及在共享网络上的数据包。2) 数据过滤。在将数据包发送给应用程序之前按照用户的规定对捕获的数据包进行过滤。3) 发送原始数据包。向网络发送原始数据包。4) 数据包统计。对网络通信进行统计。上述功能都通过一个设备驱动(这个驱动程序安装在Win32内核的网络部分)和一组动态连接库(DLL)获得。所有这些功能部件都通过一个强大的编程接口来实现,应用程序能易于开发并且能移植到各种操作系统中。3、基于WinPcap可以开发的网络应用程序基于WinPcap可以开发很多网络应用程序,典型的包括:5) 网络和协议分析软件6) 网络监听软件7) 网络通信量记录软件8) 网络数据生成软件9) 用户机网桥和路由器10) 网络入侵探测系统11) 网络扫描软件12) 网络安全工具4、WinPcap的局限性WinPcap可以独立于主机的协议(如TCP/IP协议)进行接收和发送数据包。这意味着WinPcap不能阻塞、过滤或处理本机上其它程序产生的数据。它仅仅能嗅探在网线上传输的数据包。因此,WinPcap不能在traffic shapers、QoS schedulers和个人防火墙这类应用程序中使用。5、WinPcap的开发和运行环境目前,winpcap主要的开发和运行环境是windows NT/2000/XP。由于winpcap的用户中只有很小一部分使用windows 95/98/Me,并且微软也已经放弃了对win9x的开发和支持。实际上,winpcap中的面向9x系统的概念和NT系统的非常相似,只是在某些实现上有点差异,比如说9x只支持ANSI编码,而NT系统则提倡使用Unicode编码。 简单来讲,WinPcap典型的开发和运行环境是windows NT/2000/XP。当然,早期的WinPcap也支持windows 95/98/Me,但是,我们不推荐在windows 95/98/Me下开发和运行基于WinPcap的网络应用。二、WinPcap的体系结构分析1、WinPcap的组成与结构如图2.1,WinPcap由一个数据包监听设备驱动程序(NPF)、一个底层的动态连接库(packet.dll)和一个高层的不依赖于操作系统的静态库(wpcap.dll)共三个部分构成。这里,NPF在操作系统的内核级,packet.dll、wpcap.dll在用户级。图2.1 WinPcap的组成和结构应用程序wpcap.dllpacket.dllNPFDevice Driver用户层核心层网络层数据包1)数据包监听设备驱动程序技术实现上,为了实现抓包,系统必须绕过操作系统的协议栈来访问在网络上传输的原始数据包(raw packet)。这就要求WinPcap的一部分运行在操作系统核心内部,直接与网络接口驱动交互。由于这个部分是系统依赖(system dependent)的,在Winpcap的解决方案中它被视为是一个设备驱动,称作NPF(Netgroup Packet Filter)。Winpcap开发小组针对Windows95,Windows98,WindowsME,Windows NT 4,Windows2000和WindowsXP提供了不同版本的驱动(在Windows95/98/ME中是VXD文件,在WindowsNT/2000中是SYS文件)。这些驱动不仅提供了基本的特性(例如抓包、发送原始数据包注入数据包),还有更高级的特性(例如可编程的过滤器系统和监视引擎)。前者可以被用来约束一个抓包会话只针对网络通信中的一个子集(例如,只捕获特殊主机产生的ftp通信数据包),后者提供了一个强大而简单的统计网络通信量的机制(例如,获得网络负载或两个主机间的数据交换量)。概括地讲,数据包监听设备驱动程序直接从数据链路层抓取网络数据包并过滤,将数据包不加修改地传递给运行在用户层的应用程序。它在不同的WINDOWS系统下是不同。数据包监听设备驱动程序支持BPF过滤机制,可以灵活地设置过滤规则。2)底层的动态连接库(packet.dll)和高层静态库(wpcap.dll)为了方便编程,WinPcap必须提供一个编程接口(API),这就是WinPcap的底层的动态连接库(packet.dll)和高层静态库(wpcap.dll)。这里,packet.dll提供了一个底层API,伴随着一个独立于Microsoft操作系统的编程接口,这些API可以直接用来访问驱动的函数;wpcap.dll导出了一组更强大的与libpcap一致的高层抓包函数库(capture primitives),这些函数使得数据包的捕获以一种与网络硬件和操作系统无关的方式进行。底层动态链接库运行在用户层,它将应用程序和数据包监听设备驱动程序隔离开来,使得应用程序可以不加修改地在不同的WINDOWS系统上运行。高级的静态链接库和应用程序编译在一起,它使用低级动态链接库提供的服务,向应用程序提供完善的监听接口。概括来讲,WinPcap包含了一个最优化的内核模式驱动称作Netgroup Packet Filter(NPF),和一套与libpcap兼容的用户级函数库。WinPcap使Unix平台下的应用程序能方便的移植到Win32平台下,并且它能使一套很大的Unix函数库只需通过简单的重新编译就立刻在Win32平台下使用。而且,由于网络监听的重要性,WinPcap还为此提供了特殊的系统调用函数。实际上,Windows环境下的WinPcap与Unix环境下的Berkeley Packet Filter(BPF)在体系架构上是基本一致的。在Unix环境下,程序员只需要了解Libpcap提供的编程接口即可,Libpcap将应用程序与操作系统内核之间的相互作用隐藏起来,提供了一套与用户程序联系的函数和强大的捕获数据包的抽象接口。在Windows环境下,程序员主要基于wpcap.dll提供的编程接口开发应用程序。当然,程序要也可以使用packet.dll、NPF提供的编程接口,只是后者提供的API更加底层。2、WinPcap的技术细节1)NPF驱动网络数据包过滤器(Netgroup Packet Filter,简称NPF)是Winpcap的核心部分,它是Winpcap完成困难工作的核心组件。NPF负责处理网络上传输的数据包,并向上层提供各种服务,包括数据包的捕获(capture)、发送(injection)和分析性能(analysis capabilities)。2)NPF和NDISNDIS(Network Driver Interface Specification)是Windows环境下的一个定义网络适配器(或者说成是管理网络适配器的驱动程序)与协议驱动(例如TCP/IP的实现)之间通信的规范,如图2.2。基于NDIS,顶层应用(例如TCP/IP协议)可以发送和接收网络(LAN或WAN)上的数据包而不必关心特定的适配器硬件或特定的Win32操作系统版本。网卡硬件网卡驱动程序NDISTCP/IP协议栈实现图2.2 NDIS的作用和地位NDIS支持三种类型的网络驱动: 网络接口卡或NIC驱动(Network interface card or NIC drivers)。NIC驱动直接管理着网络接口卡(NIC)。NIC驱动接下边与硬件连接,从上边表现为一个接口,该接口允许高层发送数据包到网络上,处理中断,重置NIC,停止NIC,查询和设置驱动的运行特征。NIC驱动可以是小端口(miniport)或完全的NIC驱动(full NIC driver)。Miniport驱动仅仅实现了管理NIC的必要操作,包括在NIC上发送和接收数据。对于所有最底层的NIC驱动的操作由NDIS提供,例如同步(synchronization)。小端口(miniport)不直接调用操作系统函数,它们对于操作系统的接口是NDIS。小端口仅仅是向上传递数据包给NDIS并且NDIS确保这些数据包被传递给正确的协议。 完全NIC驱动(Full NIC driver)完成硬件细节的操作和所有由NDIS完成的同步和查询操作。例如,完全NIC驱动维持接收到的数据的绑定信息。 中间层驱动(Intermediate drivers)中间层驱动位于高层驱动(例如协议驱动)和小端口之间。对于高层驱动,中间层驱动看起来像是小端口;对于小端口,中间层驱动看起来像协议驱动。一个中间层协议驱动可以位于另一个中间层驱动之上,尽管这种分层可能对系统性能带来负面影响。开发中间层驱动的一个关键原因是在现存的遗留协议驱动(legacy protocol driver)和小端口之间形成媒体的转化。例如,中间层驱动可以将LAN协议转换成ATM协议。中间层驱动不能与用户模式的应用程序通信,但可以与其他的NDIS驱动通信。 传输驱动或协议驱动(Transport drivers or protocol drivers)协议驱动实现了网络协议栈,例如IPX/SPX或TCP/IP,在一个或多个网络接口卡上提供它的服务。在协议驱动的上面,它为应用层客户程序服务;在它的下面,它与一个或多个NIC驱动或中间层NDIS驱动连接。3)NPF在Windows系统中的位置图2.3 NPF在Windows系统中的位置基于Winsock的应用程序NPFTCP/IP协议实现NIC Driver(NDIS3.0或更高)核心层网络层数据包基于Wincap的应用程序如图2.3,NPF是一个协议驱动。从性能方面来看,这不是最好的选择,但是它合理地独立于MAC层并且有权使用原始通信(raw traffic)。可以看出,NPF在NDIS之上,它与TCP/IP协议栈的实现在同一层次。因此,应用程序不通过Winsock也可以实现数据的发送和接受。3、WinPcap的基本原理抓包是WinPcap的基本功能,也是NPF最重要的操作。在抓包的时候,驱动(例如NIC Driver)使用一个网络接口监视着数据包,并将这些数据包完整无缺地投递给用户级应用程序。如图2.4,WinPcap的NPF抓包主要依靠两个组件。1)数据包过滤器(filter)。数据包过滤器决定是否接收进来的数据包并把数据包拷贝给监听程序。数据包过滤器是一个有布尔输出的函数。如果函数值是true,抓包驱动拷贝数据包给应用程序;如果是false,数据包将被丢弃。NPF数据包过滤器更复杂一些,因为它不仅决定数据包是否应该被保存,而且还决定要保存的字节数。被NPF驱动采用的过滤系统来源于BSD Packet Filter(BPF),一个虚拟处理器可以执行伪汇编书写的用户级过滤程序。应用程序采用用户自定义的过滤器并使用wpcap.dll将它们编译进BPF程序。然后,应用程序使用BIOCSETF IOCTL写入核心态的过滤器。这样,对于每一个到来的数据包该程序都将被执行,而满足条件的数据包将被接收。与传统解决方案不同,NPF不解释(interpret)过滤器,而是执行(execute)它。由于性能的原因,在使用过滤器前,NPF提供一个JIT编译器将它转化成本地的80x86函数。当一个数据包被捕获,NPF调用这个本地函数而不是调用过滤器解释器,这使得处理过程相当快。 2)循环缓冲区(Buffer)。NPF的循环缓冲区用来保存数据包以免丢失(如果一个包符合过滤器的要求,就被复制到循环缓冲区)。一个保存在缓冲区中的数据包有一个头,它包含了一些主要的信息,例如时间戳和数据包的大小,注意:它不是协议头。另外,循环缓冲区以队列插入的方式来保存数据包,提高数据的存储效率。程序员可以以组的方式将数据包从NPF缓冲区拷贝到应用程序,这样就提高了性能,因为它降低了读的次数。如果一个数据包到来的时候缓冲区已经满了,那么该数据包将被丢弃,这时就发生了丢包现象。3)Network Tap是一个用于探听网络中所有数据流的函数。4)数据统计如图2.4,为了提高数据处理的速度,WinPcap将统计和监听功能移到内核中,这样避免了将任何数据都传递给用户。WinPcap通过使用从NPF中得到的过滤器来执行一个内核级的可编统计模块,这使其变成一个强大的分级引擎,而不只是个简单的包过滤器。应用程序可以构造这个模块来监听网络活动的任意方面(例如:网络负荷、两台主机间的流量、每秒web请求的次数等等),并在预定的时间间隔内接收内核传来的数据。图2.4 Wincap的内部结构和原理基于Winpcap的监控程序packet.dllwpcap.dllNIC Driver(NDIS3.0或更高)核心层网络层数据包基于Winpcap的应用程序1Filter1Filter2Filter3Buffer1Buffer2统计引擎Network TapTCP/IP协议栈其他协议栈实现User- Buffer1User- Buffer2基于Winpcap的应用程序2NPF调用packet.dllAPI的程序直接访问NPF的程序用户层统计模式避免了复制数据包并且执行0-copy机制(当包仍存放在NIC(网络接口卡)驱动的内存中时开始进行统计,随后丢弃这个包)。而且,环境转换的次数可以保持最低,这是因为结果通过一次系统调用就可以返回给用户。它不需要缓冲区(内核或用户),因此当监听开始时不用为它分配内存。可见,统计模式是一种很有效的网络监听方式,在高速网络中利用libpcap来工作也没任何问题。WinPcap为程序员提供了一套系统调用和高层函数来进行网络监听,这使得已经知道libpcap API的程序员能很容易使用。5)构造数据包BPF和NPF都提供了构造包的函数,使用户可以将原始数据包发送到网络中。然而,Unix程序员一般不用libpcap提供的这些函数,因为在Unix平台上,应用程序可以使用原始套接字来发送伪造的数据包。在Windows环境下,只有Windows2000提供了原始套接字,而且非常有限。因此在Windows环境下,WinPcap就成为首选的构造数据包的函数库,它提供了一套标准稳定的函数。另外,NPF增加了一些新的函数,这些函数可以使数据包通过一次用户和内核模式之间的转换就发送几次。数据复制到内核中,然后通过调用一次NDIS将包发送到网络中。尽管WinPcap提供了一套新的函数来开发这些特性,但它没有提供那些强大的创建数据包的抽象函数,这需要通过其它现有的工具来实现。程序员可以利用著名的Libnet Packet Assembly Library的Windows版本实现,这个函数库增加了数据包结构层并在WinPcap上构造数据包。三、基于WinPcap的网络编程1、编程API接口的选择如前所述,WinPcap由三部分模块组成NPF、packet.dll和wpcap.dll。编程方面,我们主要使用packet.dll和wpcap.dll提供的编程接口。packet.dll提供了Win32平台下的捕获包的驱动普通接口。实际上,不同版本的Windows都提供了不同的内核模块和应用程序编
展开阅读全文