计算机网络课程设计---监控IP数据包流量

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

课程设计书
学院计算机学院
专业计算机科学与技术班级
题目监控IP数据包流量教师
学生
课程设计小组成员及分工
本小组成员:
xxx yyy zzz
分工:
xxx 做本课程设计的目的,本课程设计的要求及本课程设计的一些内容
yyy 做本课程设计的一些基础知识,本课程设计的分析,本课程设计的结果,本课程设计的总结
zzz 做本课程设计的代码,本课程设计的框架设计,本课程设计的结果验证,本课程设计的逻辑分析。

目录
一、课程设计目的和要求 (1)
1.课程设计目的: (1)
2.课程设计要求: (1)
二、课程设计的内容 (2)
三、课程设计核心知识 (4)
四、课程设计框架 (6)
五、课程设计详细分析 (8)
六、实验源代码 (16)
七、课程设计总结 (31)
一、课程设计目的和要求
1.课程设计目的:
随着internet技术的发展,基于IP协议的网络应用成为网络技术研究与软件开发的一个重要基础,因此学习网络层的基本概念,了解IP协议的基本内容,对于掌握TCP/IP协议的主要内容和学习网络课程是十分重要的。

通过本次课程设计,有助于熟悉IP包格式和加深对IP协议的理解。

2.课程设计要求:
编制程序,监控网络,捕获一段时间内网络上的IP数据包,按IP数据包的源地址统计出在该时间段内发出的IP包的个数,将其写入日志文件中并用图形表示出来。

程序的具体要求如下:
1.在图形窗口界面下,根据用户输入的捕获时间和选择的网卡输出该段时间内的IP数据包流量分析
2.能够将捕获的信息写入日志文件
二、课程设计的内容
1.课程设计的内容:
1)使用Winpcap,Winpcap的主要功能在于独立于主机协议(如TCP/IP)发送和接受原始数据包;
2)列出网卡列表,让用户选择可用的网卡;
3)使用过滤器捕获IP包,别的包都过滤掉;
4)捕获IP包并按包的源地址进行统计(用链表结构进行实现)。

5)将统计结果输出到图形窗口界面或输出到日志文件
程序流程如图8-1所示。

2.一些必要的基础知识
1.熟悉IP首部格式
2.使用Winpcap工具
WinPcap是一个基于Win32平台的,用于捕获网络数据包并进行分析的开源库.
WinPcap产生的目的,就是为Win32应用程序提供这种访问方式;
WinPcap提供了以下功能
a)捕获原始数据包,无论它是发往某台机器的,还是在其他设备
(共享媒介)上进行交换的
b)在数据包发送给某应用程序前,根据用户指定的规则过滤数据

c)将原始数据包通过网络发送出去
d)收集并统计网络流量信息
使用Winpcap工具主要需要三个步骤:
1).安装底层驱动:在Winpcap官网()下载并
安装Winpcap.exe即可
2).导入库文件:下载wpdpack开发包,并在开发环境(VC)中导
入Lib和Include 文件夹路径。

VC导入步骤(工具->选项->目录,分别在Include Files和Library Files中加
入…/wpdpack/Include和…/wpdpack/Lib)
3).在应用程序中导入需要的DLL和头文件:
方法一:VC下:工程->设置->链接在Object/Library modules下添加ws2_32.lib wpcap.lib(注意中间的空格)
方法二:在源文件中添加命令处理:
#pragma comment(lib, “ws2_32.lib”)
#pragma comment(lib, “wpcap.lib”) 本程序将会用到的Winpcap功能有:
获取本机所有适配器信息 pcap_findalldevs
以混杂模式打开适配器pcap_open_live
编译并设置过滤器pcap_compile pcap_setfilter
捕获数据包 pcap_next_ex
释放打开的适配器信息pcap_freealldevs
3.VC++图形窗口界面编程
包括按钮,编辑框,列表框,列表控件等控件的使用
4.其他相关知识,如文件操作,链表操作等
三、课程设计核心知识
1.获取本机网卡信息
本课程设计要实现对数据包的捕获,首先就要获取并列出本机上的所有网卡信息,这个功能是通过Winpcap提供的pcap_findalldevs来实现的。

该函数原型如下
int pcap_findalldevs(pcap_if_t** alldevs, char* errbuf)
pcap_if_t是一个用于描述网卡信息的结构体。

里面包含了网卡的名字,描述等信息。

通过这个函数可以得到一个网卡信息组成的链表。

函数错误则返回-1。

2.打开用户选定的网卡
得到网卡信息之后,可以通过列表框等形式显示出来,在用户选择后,通过pcap_open_live打开指定网卡:
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int
to_ms, char *ebuf)
函数功能:获得用于捕获网络数据包的数据包捕获描述字。

参数说明:device参数为指定打开的网络设备名。

snaplen参数定义捕获数据的最大字节数。

promisc指定是否将网络接口置于混杂模式。

to_ms 参数指*定超时时间(毫秒)。

ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消息。

这些参数中最重要的就是promisc参数,通过它可以将网卡监听设置为混杂模式,混杂模式是指不管网卡接收到的数据包是不是发给自己的,都向应用程序上传。

这样可以捕获一些广播信息。

本程序将监听模式设为混杂模式。

3.编译并设置过滤器
在打开网卡并得到网卡描述符后,可以编译并设置过滤器。

编译过滤器:
int pcap_compile(pcap_t* p,struct bpf_program* fp,
char* str,int optimize, bpf_u_int32 netmask) 编译一个数据包过滤器,将一个能被核心态(kernel-level)过滤器引擎解
释的程序中的高层过滤表达式(filtering expression)进行转化。

pcap_compile()被用来将字符串str编译进过滤器程序(fp),程序(fp)是一个指向bpf_program结构体并被pcap_compile()赋值的指针。

optimize 控制是否对目标代码(resulting code)的性能进行优化。

Netmask表明IPv4掩码,它仅在检查过滤器程序中的IPv4广播地址的时候被使用。

返回-1表示发生了错误,此时,pcap_geterr()将被用来显示错误信息。

设置过滤器
int pcap_setfilter(pcap_t* p,struct bpf_program* fp)
把一个过滤器同一次抓包关联起来。

pcap_setfilter被用来指定一个过滤器程序。

fp是一个指向bpf_program结构体的指针,通常是
pcap_compile()执行的结果。

当失败时返回-1,此时,pcap_geterr()被用来显示错误信息;返回0表示成功。

4.抓包:
接下来就是最后一步抓包,Winpcap提供的抓包接口主要有
pcap_next_ex和pcap_loop两种
pcap_loop直接由数据包捕获驱动所调用,它在底层抓取到包时,直接调用用户传入的处理函数
本程序使用另一个函数pcap_next_ex:
pcap_next_ex(pcap_t* p,struct pcap_pkthdr** pkt_header,const
u_char** pkt_data)
功能: 从interface或离线记录文件获取一个报文
参数: p: 已打开的捕捉实例的描述符
pkt_header: 报文头
pkt_data: 报文内容
返回值: 1: 成功0: 获取报文超时
-1: 发生错误 -2: 获取到离线记录文件的最后一个报文
四、课程设计框架
1.核心流程图:
本程序是VC下基于对话框的窗口程序。

界面如下:
图1
主要设计流程如下
1.在OnInitialDialog中获取并显示网卡信息
2.在用户点击开始监听按钮时:
a)验证用户有效输入(验证用户是否输入有效的监控时间,是否
选择合适的过滤器,以及是否已选择网卡)
b)以混杂模式打开网卡
c)根据用户在列表框中的选择编译并设置过滤器
d)开始抓取报文,将结果添加到统计链表。

并随时检测是否超时。

e)抓取完成之后,将结果输出到列表控件界面
f)在用户关闭窗口或者开始再次监控时提示是否将结果输入日
志文件(在用户再次选择监控时,需要清空已有数据)
g)用户关闭窗口时,提示保存日志并释放打开的所有适配器信息
五、课程设计详细分析
一、相关数据结构:
1.IP首部结构体IP_HEADER
typedef struct ip_header
{
unsigned char ver_ihl; //版本号以及首部长度
unsigned char tos; //区分服务
unsigned short totallen; //总长度
unsigned short id; //表示
unsigned short flags_fragoffset; //标志和片偏移
unsigned char ttl; //生存时间
unsigned char proto; //协议类型
unsigned short crc; //CRC校验
DWORD srcaddr; //源IP地址
DWORD dstaddr; //目的IP地址
unsigned int op_pad; //填充字段
}IP_HEADER;
2.统计的数据包信息结点CIPNode: 该类对象记录一个类型的数据
包并且负责管理其包的数量
class CIPNode
{
public:
unsigned char GetProtocol();
void AddCount(); //增加本节点计数
long GetCount(); //获取本节点计数
long GetIPAddress();//得到本节点IP地址
CIPNode(long sourceIP, unsigned char proto);
virtual ~CIPNode();
CIPNode* pNext;
private:
long m_lIPAdress; //源IP地址
long m_lcount; //节点计数
unsigned char m_proto;//协议类型
};
3.统计信息链表类CstatisticsList 该类维护抓取到的数据包链表
class CStatisticsList
{
public:
void Init(); //初始化头尾指针为空
CIPNode* GetHead(); //得到头指针
void AddNode(long ip, unsigned char proto); //添加一个结点如//果该结点存在则增加其计数如果不存在创建并添加该节点
CStatisticsList(); //创建一个头尾指针均为空的链表
virtual ~CStatisticsList();//释放自身维护的链表
private:
CIPNode* m_pHead; //头指针
CIPNode* m_pTail; //尾指针
};
void CStatisticsList::AddNode(long ip, unsigned char proto)
{
if (m_pHead == NULL)
{ //如果当前链表为空创建一个节点初始化头尾指针
m_pHead = m_pTail = new CIPNode(ip, proto);
m_pTail->pNext = NULL;
}
else
{ //如果链表不为空
BOOL bFind = FALSE;
CIPNode* p = NULL;
//遍历链表查找传入的ip
for (p=m_pHead; p; p=p->pNext)
{
//如果该ip已经存在则增加其计数
if (p->GetIPAddress() == ip && proto == p->GetProtocol())
{
bFind = TRUE;
p->AddCount();
break;
}
}
//如果不存在该ip 则添加节点更新尾指针
if (bFind == FALSE)
{
m_pTail->pNext = new CIPNode(ip, proto);
m_pTail = m_pTail->pNext;
m_pTail->pNext = NULL;
}
}
}
CStatisticsList::~CStatisticsList()
{
CIPNode* pTemp;
//遍历链表依次释放节点
if (m_pHead)
{
pTemp = m_pHead;
m_pHead = m_pHead->pNext;
delete pTemp;
}
m_pHead = NULL;
m_pTail = NULL;
}
二、主界面类CIPPackCap逻辑:
1.主要类成员:
class CIPPackCapDlg : public CDialog
{
public:
void ResetData(); //在用户重新开始监控时重新初始化数据
void SaveToFile(); //将最近一次统计结果保存到指定文件
void AskSaveToFile(); //询问用户是否保存文件
void EnableSomeWindow(BOOL bEnable); //在监控过程中使输入控件无效
void UpdateOutput(); //更新统计列表输出
BOOL StartListenning(); //开始抓取数据包
void InitStatisticsListCtrl(); //初始化统计列表控件设置
void SetTipsInfo(LPCTSTR pszformat, ...); //格式化并显示提示消息
BOOL SetListenFilter(); //设置监控过滤器
BOOL OpenSelDev(); //打开用户选定的适配器
BOOL FindAndShowAllDevs(); //查找并显示所有的适配器
private:
BOOL m_bHadListend; //标志用户是否是第一次监控
time_t m_startTime; //最近一次监控的开始时间
CStatisticsList m_statisticsList; //存放监控内容的链表
pcap_if_t* m_pCurSelDev; //用户选中的适配器
pcap_t* m_fpSelDev; //用户选中的适配器描述符
int m_iDevsCount; //适配器数目
pcap_if_t* m_pAllDevs; //所有适配器指针
ofstream m_filestream; //文件流用于保存记录
};
2.初始化:初始化工作在OnInitialDialog中完成: 主要代码:
// 查找并显示所有适配器如果失败则结束程序
if(!FindAndShowAllDevs())
EndDialog(IDOK);
//初始化统计列表控件设置
InitStatisticsListCtrl();
//设置默认过滤器
((CComboBox*)GetDlgItem(IDC_COMBO_FILTER))->SetCurSel(0);
3.用户选择开始: 在响应函数OnBtnStart()中
void CIPPackCapDlg::OnBtnStart()
{
//如果之前有监听记录询问是否保存记录到日志文件
//并且重置必要数据(统计链表)
if (m_bHadListend)
{
AskSaveToFile();
ResetData();
}
UpdateData(TRUE);
//验证是否输入定时
if(m_uListenTime == 0)
{
MessageBox("请输入你要监控的时间");
return;
}
//是否选择有效过滤器
if (m_strFilter == "")
{
MessageBox("输入你的过滤器");
return;
}
//获取并打开用户选择的适配器
if (!OpenSelDev())
return;
//设置监听过滤器
if (!SetListenFilter())
return;
//输出提示信息
SetTipsInfo("正在监听:%s", m_pCurSelDev->name);
if(StartListenning())
{
//标记已经监听过
m_bHadListend = TRUE;
GetDlgItem(IDC_TIPS)->SetWindowText("监听正常结束");
}
//更新输出以及界面
UpdateOutput();
}
OnBtnStart是程序的核心部分,其他模块函数都为其服务,当用户
非第一次监控时,会调用AskSaveToFile函数,该函数判断用户是
否在日志文件编辑框中输入文件名,如果是,则修正该文件名为.txt
格式并弹出提示框提醒用户是否将上一次结果保存到日志文件
图2.保存日志提示框
如果用户选择是,则在AskSaveToFile中调用SaveToFile函数,将记录输出到指定日志文件
/*
* 询问是否保存日志文件
如果选择是调用SaveToFile保存记录到文件
*/
void CIPPackCapDlg::AskSaveToFile()
{
//如果日志文件一栏不为空
if(m_strOutputFile != "")
{
//修正用户文件格式
CString strFileExt = m_strOutputFile.Right(4);
if (strFileExt != ".txt" && strFileExt != ".TXT")
m_strOutputFile+=".txt";
//询问用户是否保存
CString strTip;
strTip.Format("是否将输出保存到你的日志文件: %s", m_strOutputFile);
if (IDYES == MessageBox(strTip, "是否保存?", MB_YESNO))
{
SaveToFile();
}
}
}
下图3为生成的日志文件:
图3.生成的日志文件
如果用户选择再次监控,在保存日志文件后,还需要清空已收集的报文
包记录和显示界面。

这是通过ResetData()来完成的。

该函数遍历并释放m_statisticsList所有节点,然后清空列表控件显示。

实现参见附源代码之后便开始打开网卡,设置过滤器,然后开始一次新的监听,监听代码如下:
// 开始在该适配器上不断抓包并将捕获信息填入到统计链表
BOOL CIPPackCapDlg::StartListenning()
{
//使开始按钮和其他输入框不再响应输入
EnableSomeWindow(FALSE);
//获取开始时间
time_t begin_time;
time_t now_time;
::time(&begin_time);
m_startTime = begin_time;
time_t left_time;
struct pcap_pkthdr* ppktheader;
unsigned char* ppkt_data;
BOOL bNormalExit = FALSE;
int ret;
while((ret = pcap_next_ex(m_fpSelDev, &ppktheader, &ppkt_data)) >= 0)
{
//计算并显示剩余时间
::time(&now_time);
left_time = m_uListenTime - (now_time-begin_time);
CString strLeftTime;
strLeftTime.Format("%d秒", left_time);
GetDlgItem(IDC_TIME_LEFT)->SetWindowText(strLeftTime);
if (left_time <= 0)
{
bNormalExit = TRUE;
break;
}
//超时继续进行下一次抓包
if(ret == 0)
continue;
//在接收到的数据中得到IP首部
IP_HEADER* pheader;
pheader = (IP_HEADER*)(ppkt_data+14);
m_statisticsList.AddNode(pheader->srcaddr, pheader->proto);
}
return bNormalExit;
}
该函数通pcap_next_ex统计在一段时间内抓取到的报文到m_statisticsList。

完成统计之后,通过UpdateOutput()函数更新列表控件m_statisticsCtrl来显示链表中的统计结果。

UpdateOutput通过遍历统计链表中的结果,并获取其中的信息输出到表项。

通过inet_ntoa将long型IP地址转换为点分十进制的字符串。

通过一个switch-case分支来解析节点中的m_proto字段属于何种协议。

图4为解析过程界面
图5为过滤器为udp时的运行结果:
4.关闭窗口时的处理流程:
当用户选择关闭窗口时,在对话框关闭响应函数OnClose中:
1.首先检测变量m_bHadListend如果为TRUE。

说明用户已经至少
监控过一次,调用AskSaveToFile函数。

该函数首先判断用户是否输入了日志文件,如果是,则修正文件格式为.txt 之后弹出消息框提示保存
并负责调用SaveToFile保存。

如果用户未输入日志文件名,则什么也不做。

2.调用pcap_freealldevs释放m_pAllDevs中保存的所有适配器信息
OnClose函数代码如下:
void CIPPackCapDlg::OnClose()
{
//询问是否保存最后一次结果
if (m_bHadListend)
{
AskSaveToFile();
}
pcap_freealldevs(m_pAllDevs);
CDialog::OnClose();
}
5.其余模块函数可参见附源码。

六、实验源代码
/////////////////////////////////////////////
//IPNode.h
////////////////////////////////////////////
#if !defined(AFX_IPNODE_H__796BA726_424A_4BD2_AB97_8D188B55AA98__INCLU DED_)
#define
AFX_IPNODE_H__796BA726_424A_4BD2_AB97_8D188B55AA98__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CIPNode
{
public:
unsigned char GetProtocol();
void AddCount(); //增加本节点计数
long GetCount(); //获取本节点计数
long GetIPAddress();//得到本节点IP地址
CIPNode(long sourceIP, unsigned char proto);
virtual ~CIPNode();
CIPNode* pNext;
private:
long m_lIPAdress; //源IP地址
long m_lcount; //节点计数
unsigned char m_proto;//协议类型
};
#endif
// !defined(AFX_IPNODE_H__796BA726_424A_4BD2_AB97_8D188B55AA98__INCLUD ED_)
////////////////////////////////////////////////////////////
//IPNode.cpp
////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "IPPackCap.h"
#include "IPNode.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CIPNode::CIPNode(long sourceIP, unsigned char proto)
{
m_lIPAdress = sourceIP;
m_lcount = 1;
m_proto = proto;
}
CIPNode::~CIPNode()
{
}
long CIPNode::GetIPAddress()
{
return m_lIPAdress;
}
long CIPNode::GetCount()
{
return m_lcount;
}
void CIPNode::AddCount()
{
m_lcount++;
}
unsigned char CIPNode::GetProtocol()
{
return m_proto;
}
////////////////////////////////////////////
//StatisticsList.h
////////////////////////////////////////////
#if !defined(AFX_STA TISTICSLIST_H__B06D35E9_FCAB_48E5_88FF_BDE53D80FADA __INCLUDED_)
#define
AFX_STATISTICSLIST_H__B06D35E9_FCAB_48E5_88FF_BDE53D80FADA__INCLUD ED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "IPNode.h"
class CStatisticsList
{
public:
void Init(); //初始化头尾指针为空
CIPNode* GetHead(); //得到头指针
void AddNode(long ip, unsigned char proto); //添加一个结点如果该结点存在则增加其计数
//如果不存在创建并添加该节点
CStatisticsList();
virtual ~CStatisticsList();
private:
CIPNode* m_pHead; //头指针
CIPNode* m_pTail; //尾指针
};
#endif
// !defined(AFX_STATISTICSLIST_H__B06D35E9_FCAB_48E5_88FF_BDE53D80FADA_ _INCLUDED_)
////////////////////////////////////////////
//StatisticsList.cpp
////////////////////////////////////////////
#include "stdafx.h"
#include "IPPackCap.h"
#include "StatisticsList.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CStatisticsList::CStatisticsList()
{
m_pHead = NULL;
m_pTail = NULL;
}
CStatisticsList::~CStatisticsList()
{
CIPNode* pTemp;
//遍历链表依次释放节点
if (m_pHead)
{
pTemp = m_pHead;
m_pHead = m_pHead->pNext;
delete pTemp;
}
m_pHead = NULL;
m_pTail = NULL;
}
void CStatisticsList::AddNode(long ip, unsigned char proto)
{
if (m_pHead == NULL)
{ //如果当前链表为空创建一个节点初始化头尾指针
m_pHead = m_pTail = new CIPNode(ip, proto);
m_pTail->pNext = NULL;
}
else
{ //如果链表不为空
BOOL bFind = FALSE;
CIPNode* p = NULL;
//遍历链表查找传入的ip
for (p=m_pHead; p; p=p->pNext)
{
//如果该ip已经存在则增加其计数
if (p->GetIPAddress() == ip && proto == p->GetProtocol())
{
bFind = TRUE;
p->AddCount();
break;
}
}
//如果不存在该ip 则添加节点更新尾指针
if (bFind == FALSE)
{
m_pTail->pNext = new CIPNode(ip, proto);
m_pTail = m_pTail->pNext;
m_pTail->pNext = NULL;
}
}
}
CIPNode* CStatisticsList::GetHead()
{
return m_pHead;
}
void CStatisticsList::Init()
{
m_pHead = NULL;
m_pTail = NULL;
}
////////////////////////////////////////////
//IPPackCapDlg.h
////////////////////////////////////////////
#if !defined(AFX_IPPACKCAPDLG_H__682FD3D7_3E3A_4785_B228_62D96973AC09__ INCLUDED_)
#define
AFX_IPPACKCAPDLG_H__682FD3D7_3E3A_4785_B228_62D96973AC09__INCLUDED _
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <fstream>
using namespace std;
#include "PCAP.H"
#include "StatisticsList.h" // Added by ClassView
/////////////////////////////////////////////////////////////////////////////
// CIPPackCapDlg dialog
class CIPPackCapDlg : public CDialog
{
public:
void ResetData(); //在用户重新开始监控时重新初始化数据
void SaveToFile(); //将最近一次统计结果保存到指定文件
void AskSaveToFile(); //询问用户是否保存文件
void EnableSomeWindow(BOOL bEnable); //在监控过程中使一些输入控件无效void UpdateOutput(); //更新统计列表输出
BOOL StartListenning(); //开始抓取数据包
void InitStatisticsListCtrl(); //初始化统计列表控件设置
void SetTipsInfo(LPCTSTR pszformat, ...); //格式化并显示提示消息
BOOL SetListenFilter(); //设置监控过滤器
BOOL OpenSelDev(); //打开用户选定的适配器
BOOL FindAndShowAllDevs(); //查找并显示所有的适配器
CIPPackCapDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DA TA(CIPPackCapDlg)
enum { IDD = IDD_IPPACKCAP_DIALOG };
CListCtrl m_statisticsListCtrl;
CListBox m_listboxAllDevs;
UINT m_uListenTime;
CString m_strOutputFile;
CString m_strFilter;
//}}AFX_DA TA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CIPPackCapDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CIPPackCapDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnBtnStart();
afx_msg void OnDestroy();
afx_msg void OnClose();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
BOOL m_bHadListend; //标志用户是否是第一次监控
time_t m_startTime; //最近一次监控的开始时间
CStatisticsList m_statisticsList; //存放监控内容的链表
pcap_if_t* m_pCurSelDev; //用户选中的适配器
pcap_t* m_fpSelDev; //用户选中的适配器描述符
int m_iDevsCount; //适配器数目
pcap_if_t* m_pAllDevs; //所有适配器指针
ofstream m_filestream; //文件流用于保存记录
};
//{{AFX_INSERT_LOCA TION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif
// !defined(AFX_IPPACKCAPDLG_H__682FD3D7_3E3A_4785_B228_62D96973AC09__I NCLUDED_)
////////////////////////////////////////////
//IPPackCap.cpp
////////////////////////////////////////////
// IPPackCapDlg.cpp : implementation file
//
#include "stdafx.h"
#include "IPPackCap.h"
#include "IPPackCapDlg.h"
#include "IPNode.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DA TA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DA TA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DA TA_INIT(CAboutDlg)
//}}AFX_DA TA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DA TA_MAP(CAboutDlg)
//}}AFX_DA TA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CIPPackCapDlg dialog
CIPPackCapDlg::CIPPackCapDlg(CWnd* pParent /*=NULL*/)
: CDialog(CIPPackCapDlg::IDD, pParent)
{
//{{AFX_DA TA_INIT(CIPPackCapDlg)
m_uListenTime = 0;
m_strOutputFile = _T("");
m_strFilter = _T("");
//}}AFX_DA TA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_pAllDevs = NULL;
m_iDevsCount = 0;
m_fpSelDev = NULL;
m_pCurSelDev = NULL;
m_bHadListend = FALSE;
}
void CIPPackCapDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DA TA_MAP(CIPPackCapDlg)
DDX_Control(pDX, IDC_LIST_STATISTICS, m_statisticsListCtrl);
DDX_Control(pDX, IDC_LIST_ALLDEVS, m_listboxAllDevs);
DDX_Text(pDX, IDC_EDIT_LISTENTIME, m_uListenTime);
DDX_Text(pDX, IDC_EDIT_OUTPUTFILE, m_strOutputFile);
DDV_MaxChars(pDX, m_strOutputFile, 256);
DDX_CBString(pDX, IDC_COMBO_FILTER, m_strFilter);
//}}AFX_DA TA_MAP
}
BEGIN_MESSAGE_MAP(CIPPackCapDlg, CDialog)
//{{AFX_MSG_MAP(CIPPackCapDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_START, OnBtnStart)
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CIPPackCapDlg message handlers
BOOL CIPPackCapDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARA TOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// 查找并显示所有适配器如果失败则结束程序
if(!FindAndShowAllDevs())
EndDialog(IDOK);
//初始化统计列表控件设置
InitStatisticsListCtrl();
//设置默认过滤器
((CComboBox*)GetDlgItem(IDC_COMBO_FILTER))->SetCurSel(0);
return TRUE;
}
void CIPPackCapDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
void CIPPackCapDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
HCURSOR CIPPackCapDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
BOOL CIPPackCapDlg::FindAndShowAllDevs()
{
char szErrBuf[PCAP_ERRBUF_SIZE] = {0};
if(pcap_findalldevs(&m_pAllDevs, szErrBuf) == -1)
{
CString strErr(_T("Err In pcap_findalldevs: \r\n"));
strErr += szErrBuf;
MessageBox(strErr);
return FALSE;
}
for (pcap_if_t* pDev = m_pAllDevs; pDev; pDev = pDev->next)
{
m_listboxAllDevs.InsertString(m_iDevsCount, pDev->name);
++m_iDevsCount;
}
if (m_iDevsCount == 0)
{
MessageBox("No Adapter Find !");
return FALSE;
}
return TRUE;
}
void CIPPackCapDlg::OnBtnStart()
{
//如果之前有监听记录询问是否保存记录到日志文件
//并且重置必要数据(统计链表)
if (m_bHadListend)
{
AskSaveToFile();
ResetData();
}
UpdateData(TRUE);
//验证是否输入定时
if(m_uListenTime == 0)
{
MessageBox("请输入你要监控的时间");
return;
}
//是否选择有效过滤器
if (m_strFilter == "")
{
MessageBox("输入你的过滤器");
return;
}
//获取并打开用户选择的适配器
if (!OpenSelDev())
return;
//设置监听过滤器
if (!SetListenFilter())
return;
//输出提示信息
SetTipsInfo("正在监听:%s", m_pCurSelDev->name);
if(StartListenning())
{
//标记已经监听过
m_bHadListend = TRUE;
GetDlgItem(IDC_TIPS)->SetWindowText("监听正常结束");
}
//更新输出以及界面
UpdateOutput();
}
/*
* 获取并以混杂模式打开用户选择的适配器
*/
BOOL CIPPackCapDlg::OpenSelDev()
{
//获取适配器列表框当前选中的条目
int index = m_listboxAllDevs.GetCurSel();
//如果条目无效提示用户重新选择
if (index<0 || index>=m_iDevsCount)
{
MessageBox("请选择你要监控的适配器");
return FALSE;
}
//根据选择的条目索引得到用户选择的适配器的pcap_if_t结构指针
int i=0;
for (pcap_if_t* pDevice=m_pAllDevs; i<=index; pDevice=pDevice->next, i++)
{
m_pCurSelDev = pDevice;
}
//以混杂模式打开该适配器得到该适配器的描述符
char szErrBuf[PCAP_ERRBUF_SIZE] = {0};
if ((m_fpSelDev=pcap_open_live(m_pCurSelDev->name, 65535, 1, 1000, szErrBuf)) == NULL)
{
CString strErr(_T("Error In pcap_open_live:\r\n"));
strErr += szErrBuf;
return FALSE;
}
return TRUE;
}
/*
* 编译并设置抓取过滤器
*/
BOOL CIPPackCapDlg::SetListenFilter()
{
//获取子网掩码
UINT netmask = 0;
if (m_pCurSelDev->addresses != NULL)
{
netmask = ((struct sockaddr_in*)(m_pCurSelDev->addresses->netmask))->sin_addr.S_un.S_addr;
}
else
{ //默认为C类地址
netmask = 0xffffff;
}
struct bpf_program bpfCode;
//编译过滤器
if (pcap_compile(m_fpSelDev, &bpfCode, m_strFilter.GetBuffer(5), 1, netmask)<0)
{
MessageBox("编译捕获过滤器失败!");
return FALSE;
}
//设置过滤器
if (pcap_setfilter(m_fpSelDev, &bpfCode) < 0)
{
MessageBox("设置捕获过滤器失败! ");
return FALSE;
}
return TRUE;
}
/*
* 格式化输出提示信息
*/
void CIPPackCapDlg::SetTipsInfo(LPCTSTR pszformat, ...)
{
va_list arglist;
va_start(arglist, pszformat);
char szTip[1024];
_vstprintf(szTip, pszformat, arglist);
va_end(arglist);
GetDlgItem(IDC_TIPS)->SetWindowText(szTip);
}
/*
* 初始化统计报表设置
*/
void CIPPackCapDlg::InitStatisticsListCtrl()
{
//添加列和列名
m_statisticsListCtrl.InsertColumn(0, "源IP地址");
m_statisticsListCtrl.InsertColumn(1, "数据包类型");
m_statisticsListCtrl.InsertColumn(2, "数据包数目");
//设置各列宽度
CRect rectCtrl;
m_statisticsListCtrl.GetClientRect(&rectCtrl);
m_statisticsListCtrl.SetColumnWidth(0, rectCtrl.Width()/2);
m_statisticsListCtrl.SetColumnWidth(1, rectCtrl.Width()/4);
m_statisticsListCtrl.SetColumnWidth(2, rectCtrl.Width()/4);
}
/*
* 开始在该适配器上不断抓包并将主要信息填入到统计链表
*/
BOOL CIPPackCapDlg::StartListenning()
{
//使开始按钮和其他输入框不再响应输入
EnableSomeWindow(FALSE);
//获取开始时间
time_t begin_time;
time_t now_time;
::time(&begin_time);
m_startTime = begin_time;
time_t left_time;
struct pcap_pkthdr* ppktheader;
unsigned char* ppkt_data;
BOOL bNormalExit = FALSE;
int ret;
while((ret = pcap_next_ex(m_fpSelDev, &ppktheader, &ppkt_data)) >= 0) {
//计算并显示剩余时间
::time(&now_time);
left_time = m_uListenTime - (now_time-begin_time);
CString strLeftTime;
strLeftTime.Format("%d秒", left_time);
GetDlgItem(IDC_TIME_LEFT)->SetWindowText(strLeftTime);
if (left_time <= 0)
{
bNormalExit = TRUE;
break;
}
//超时继续进行下一次抓包
if(ret == 0)
continue;
//在接收到的数据中得到IP首部
IP_HEADER* pheader;
pheader = (IP_HEADER*)(ppkt_data+14);
m_statisticsList.AddNode(pheader->srcaddr, pheader->proto);
}
return bNormalExit;
}
/*
* 更新输出控件界面显示
*/
void CIPPackCapDlg::UpdateOutput()
{
CIPNode* pNode = NULL;
int nIndex = 0;
for (pNode=m_statisticsList.GetHead(); pNode; pNode=pNode->pNext) {
//显示源IP地址
long lIP = pNode->GetIPAddress();
m_statisticsListCtrl.InsertItem(nIndex, inet_ntoa(*(in_addr*)&lIP));
//解析并显示协议
CString strProto;
switch(pNode->GetProtocol())
{
case PROTOCOL_ICMP:
strProto = "ICMP";
break;
case PROTOCOL_TCP:
strProto = "TCP";
break;
case PROTOCOL_UDP:
strProto = "UDP";
break;
case PROTOCOL_IGP:
strProto = "IGP";
break;
case PROTOCOL_OSPF:
strProto = "OSPF";
break;
case PROTOCOL_EGP:
strProto = "EGP";
break;
case PROTOCOL_IGMP:
strProto = "IGMP";
break;
default:
strProto = "UNKNOWN";
break;
}
m_statisticsListCtrl.SetItemText(nIndex, 1, strProto);
//显示数量
CString strCount;
strCount.Format("%d", pNode->GetCount());
m_statisticsListCtrl.SetItemText(nIndex, 2, strCount);
nIndex++;
}
//恢复必要控件可用
EnableSomeWindow(TRUE);
//将"开始监控"按钮变为"再次监控"
GetDlgItem(IDC_BTN_START)->SetWindowText("再次监控");
}
/*
* 在监控期间使开始按钮和其他输入控件不可用
* (在单线程中已形成死循环因此用户不可操作仅更新按钮不可用给用户直观显示)
*/
void CIPPackCapDlg::EnableSomeWindow(BOOL bEnable)
{
((CButton*)GetDlgItem(IDC_BTN_START))->EnableWindow(bEnable);
}
/*
* 询问是否保存日志文件
如果选择是调用SaveToFile保存记录到文件
*/
void CIPPackCapDlg::AskSaveToFile()
{
//如果日志文件一栏不为空
if(m_strOutputFile != "")
{
//修正用户文件格式
CString strFileExt = m_strOutputFile.Right(4);
if (strFileExt != ".txt" && strFileExt != ".TXT")
m_strOutputFile+=".txt";
//询问用户是否保存
CString strTip;
strTip.Format("是否将输出保存到你的日志文件: %s", m_strOutputFile);
if (IDYES == MessageBox(strTip, "是否保存?", MB_YESNO))
{
SaveToFile();
}
}
}
/*
* 将得到的结果存入m_strOutputFile指定的文件
*/
void CIPPackCapDlg::SaveToFile()
{
//打开指定文件
m_filestream.open(m_strOutputFile.GetBuffer(0), ios::out|ios::app);
if (!m_filestream)
{
MessageBox("打开文件错误,已取消保存");
return;
}
//格式化当前时间和开始监听时间
CTime curTime;
CString strTime;
curTime = CTime::GetCurrentTime();
strTime.Format("%d-%d-%d %d:%d:%d", curTime.GetYear(), curTime.GetMonth(), curTime.GetDay(),
curTime.GetHour(), curTime.GetMinute(), curTime.GetSecond());
CString strStartTime;
CTime startTime = CTime(m_startTime);
strStartTime.Format("%d-%d-%d %d:%d:%d", startTime.GetYear(), startTime.GetMonth(), startTime.GetDay(),
startTime.GetHour(), startTime.GetMinute(), startTime.GetSecond());
//生成日志头信息
m_filestream<<"------------------------------"<<strTime.GetBuffer(0)<<"---------------------------------------\n";
m_filestream<<"日志生成时间: "<<strTime.GetBuffer(0);
m_filestream<<"开始监听时间: "<<strStartTime.GetBuffer(0)<<'\n';
m_filestream<<"监听时长: "<<m_uListenTime<<"秒\n";
m_filestream<<"监听适配器: "<<m_pCurSelDev->name<<'\n';
m_filestream<<"--------------------监控记录----------------------------\n";
//从统计列表控件中获取信息并生成记录
int count = m_statisticsListCtrl.GetItemCount();。

相关文档
最新文档