应付大作业写的一个程序,先给出程序的运行界面
程序的核心内容是抓包然后分析数据,我做的最多的也是协议分析这块内容。上面首先给出的是当前网络的上传下载速度,这块内容我是参考Windows性能计数器来写的,就是PDH,直接获取相应的接口,
获取数据,这块内容直接放出代码
1 #include <Pdh.h> 2 #include <list> 3 #pragma comment(lib, "Pdh.lib") 4 5 class NetWorkSpeed 6 { 7 public: 8 NetWorkSpeed(); 9 ~NetWorkSpeed(); 10 public: 11 double getRecSpeed() {return m_RecSpeed; } 12 double getSendSpeed() {return m_SendSpeed;} 13 14 bool init(); 15 16 void update(); 17 private: 18 double m_RecSpeed; 19 double m_SendSpeed; 20 HQUERY m_hQuery; 21 22 std::list<double> m_receiveBps; 23 std::list<double> m_sendBps; 24 std::list<HCOUNTER> m_allhCounters; 25 };
1 using namespace std; 2 3 NetWorkSpeed::NetWorkSpeed() 4 { 5 6 } 7 8 NetWorkSpeed::~NetWorkSpeed() 9 { 10 m_allhCounters.clear(); 11 m_sendBps.clear(); 12 m_receiveBps.clear(); 13 14 PdhCloseQuery(m_hQuery); 15 } 16 17 bool NetWorkSpeed::init() 18 { 19 DWORD counters_len=0; //计数器的个数 20 DWORD instances_len=0; //网卡的个数 21 string counters_names; //保存计数器名称 22 string instances_names; //保存网卡名称 23 const char* networkInterface="Network Interface"; //网络接口部分 24 25 PdhEnumObjectItemsA(0,0,networkInterface,0,&counters_len,0,&instances_len,PERF_DETAIL_WIZARD,0); 26 counters_names.assign(counters_len,0); //初始化字符串 27 instances_names.assign(instances_len,0); 28 if (ERROR_SUCCESS!=PdhEnumObjectItemsA(0, //使用本地电脑不使用log日志文件 29 0, //使用本地电脑 30 networkInterface, //接口声明 31 &counters_names[0], //保存字符串 32 &counters_len, //个数 33 &instances_names[0], 34 &instances_len, 35 PERF_DETAIL_WIZARD, //四种 这里是获取所有的计数器 36 0)) //必须设置成0 37 return false; 38 39 40 41 const char* in_speed="Bytes Received/sec"; 42 const char* out_speed="Bytes Sent/sec"; 43 44 PDH_STATUS resulte=PdhOpenQuery(0,0,&m_hQuery); //打开计数器 45 if (ERROR_SUCCESS!=resulte) 46 return false; //打开失败 47 48 char* tpStr=&instances_names[0]; 49 for(;*tpStr!=0;tpStr+=(strlen(tpStr)+1)) 50 { 51 string pdh_Receive_path=string("\\")+networkInterface+"("+tpStr+")"+"\\"+in_speed; //计数器具体路径 52 string pdh_Sent_path=string("\\")+networkInterface+"("+tpStr+")"+"\\"+out_speed; 53 54 HCOUNTER tpCounter; 55 resulte=PdhAddCounterA(m_hQuery,pdh_Receive_path.c_str(),0,&tpCounter); //增加计数器 56 if (ERROR_SUCCESS!=resulte) 57 return false; 58 m_allhCounters.push_back(tpCounter); //加入 59 60 resulte=PdhAddCounterA(m_hQuery,pdh_Sent_path.c_str(),0,&tpCounter); 61 if (ERROR_SUCCESS!=resulte) 62 return false; 63 m_allhCounters.push_back(tpCounter); 64 } 65 return true; 66 } 67 68 void NetWorkSpeed::update() 69 { 70 PDH_FMT_COUNTERVALUE relustValue; 71 DWORD dType; 72 73 PDH_STATUS resulte=PdhCollectQueryData(m_hQuery); //收集数据 74 75 double in_bps=0; 76 double out_bps=0; 77 double in_avg_bps=0; 78 double out_avg_bps=0; 79 80 //遍历每个计数器 81 for(list<HCOUNTER>::iterator begin=m_allhCounters.begin();begin!=m_allhCounters.end();begin++) 82 { 83 resulte=PdhGetFormattedCounterValue(*begin,PDH_FMT_DOUBLE,&dType,&relustValue); 84 if (ERROR_SUCCESS==resulte) 85 in_bps+=relustValue.doubleValue; //增加 86 begin++; 87 resulte=PdhGetFormattedCounterValue(*begin,PDH_FMT_DOUBLE,&dType,&relustValue); 88 if (ERROR_SUCCESS==resulte) 89 out_bps+=relustValue.doubleValue; 90 } 91 92 m_receiveBps.push_back(in_bps); 93 m_sendBps.push_back(out_bps); 94 95 if (m_receiveBps.size()>10) 96 m_receiveBps.pop_front(); 97 if (m_sendBps.size()>10) 98 m_sendBps.pop_front(); 99 100 //计算平均值 101 in_avg_bps=accumulate(m_receiveBps.begin(),m_receiveBps.end(),0.0)/m_receiveBps.size(); 102 out_avg_bps=accumulate(m_sendBps.begin(),m_sendBps.end(),0.0)/m_sendBps.size(); 103 104 //转成 千字节 105 m_RecSpeed=in_avg_bps/1024; 106 m_SendSpeed=out_avg_bps/1024; 107 }
然后就是Pcap程序里面要做的第一步,选择网卡,然后打开设备,开启网卡的混杂模式,使用pcap_loop函数进行抓包,当然这里需要开启一条新的线程,具体的可以直接参考WinPcap入门文档里面的例子,这里我
先说一下我遇到的问题。开始我是每来一个包就进行分析,然后发现这种情况下是会丢包的,而且流量突然变大的时候丢包非常严重,会影响后面流量统计的精度。后来的话是打算参考WireShark的源代码,但无奈水平
不够,代码太复杂,就没看了。不过,我大概有下面一些理解,WireShark对发来的包是先存到堆里面,然后再取出分析,分析的话是采用树形的结构,确定它的父协议,然后一层层向下分析,而且它的插件开发也是向
协议树中进行注册,分析的时候就会通过插件,协议种类很多,这一块内容我还在学习,实现提供一个便捷的接口供外部注册使用。具体的分析网上很多,就不多说了。我呢,也是建立链表,将包数据存到链表里面,然后在
外部开启定时器,每隔一段时间对抓到的包进行分析,这样就能避免丢包。但是,需要注意的是,内存是有限的,所以必须定时的清理链表,释放相关的内存,当然你也可以先将数据写到文件里面,这一块需要你自己决定了。
这一块的话我就不给出相关代码了,毕竟网上太多了。
然后就是协议分析,先给出一些资料的链接, http://download.csdn.net/detail/zhoupeng39/8888363
这块我是写了两遍,毕竟看不看资料对不同协议的理解程度还是不同的,代码写的很简陋,如下
1 //头部基类 2 class Head_Top 3 { 4 public: 5 Packet_Type own_type; //记录类型 6 int own_len; //记录长度 7 8 Head_Top():next(NULL){} 9 10 Head_Top* next; //下一个 11 12 virtual void packet_analysis(u_char* data,Packet_Type& type,int remain_len)=0; //分析 13 14 virtual CString packet_own_print()=0; //打印 15 16 virtual void insertIntoTree(HTREEITEM root)=0; //形成树列表 17 }; 18 19 //以太网V2帧 20 class Head_EthernetII: public Head_Top 21 { 22 public: 23 u_char sMac[6]; //源Mac地址 24 u_char dMac[6]; //目的Mac地址 25 u_char kind[2]; //上层网络协议类型 26 27 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 28 29 CString packet_own_print(); 30 31 void insertIntoTree(HTREEITEM root); 32 }; 33 34 //以太网802.3帧 35 class Head_802_3 : public Head_Top 36 { 37 public: 38 u_char sMac[6]; //mac地址 39 u_char dMac[6]; 40 u_char kind[2]; //类型 41 42 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 43 44 CString packet_own_print(); 45 46 void insertIntoTree(HTREEITEM root); 47 }; 48 49 //使用网线连接 50 class Head_PPPoE : public Head_Top 51 { 52 public: 53 u_short version; //版本 54 u_short pppoe_type; //类型 55 u_short code; //代码 56 u_short session_id; //会话ID 保持不变 57 u_short len; //长度 58 CString nextDataType; //保存净荷域数据类型 只是分析IP包 其他类型未分析 59 60 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 61 62 CString packet_own_print(); 63 64 void insertIntoTree(HTREEITEM root); 65 }; 66 67 //地址解析协议 68 class Head_ARP : public Head_Top 69 { 70 public: 71 u_short handWareType; //硬件类型 1 表示以太网 72 u_short protocal_type; //协议类型 IP地址 73 u_short handWare_Len; //硬件长度 Mac地址 6个字节 74 u_short protocal_len; //协议长度 Ip地址 4个字节 75 u_short op_code; //操作码 1 请求 2 应答 76 u_char sMac[6]; //源Mac 77 u_char dMac[6]; //目的Mac 78 u_char sIp[4]; //源IP 79 u_char dIp[4]; //目的IP 80 81 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 82 83 CString packet_own_print(); 84 85 void insertIntoTree(HTREEITEM root); 86 }; 87 88 //IP协议 89 class Head_IPv4 : public Head_Top 90 { 91 public: 92 u_short version; //版本号 93 u_short header_len; //报头长度 *4为头部的长度 头部长度可变 94 u_short service_type; //业务类型 95 u_short len; //数据包长度 96 u_short sign; //标识符 IP包分组时候使用 同一个标识符进行重组 97 u_char df; //标记是否能分段 1 表示不能分段(不能分段 进行分段 会丢包处理) 98 u_char mf; //分段包的结束操作 0 表示分段包的最后一个包 99 u_short offset; //分段偏移 100 u_char TTL; //存活时间 101 u_short protocol; //下层协议类型 102 u_char sIp[4]; //源地址 103 u_char dIp[4]; //目的地址 104 105 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 106 107 CString packet_own_print(); 108 109 void insertIntoTree(HTREEITEM root); 110 }; 111 112 //IPV6协议 113 class Head_IPv6 : public Head_Top 114 { 115 public: 116 u_char version; //IP版本 为6 117 u_char trafficClass; //通信类别 118 u_char flowLabel[3]; //流标记 119 u_short next_len; //负载长度 包括扩展头和上层载荷 120 u_short protocol; //上层协议 121 u_short hopLimit; //跳数限制 122 u_char sIp[16]; //源MAC 123 u_char dIp[16]; //目的MAC 124 u_short ext_len; //扩展长度 125 u_short sign; //标记是否存在扩展头部 126 127 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 128 129 CString packet_own_print(); 130 131 void insertIntoTree(HTREEITEM root); 132 }; 133 134 //ICMP控制协议 135 class Head_ICMP : public Head_Top 136 { 137 public: 138 u_short ic_type; //类型 139 u_short code; //代码 140 141 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 142 143 CString packet_own_print(); 144 145 void insertIntoTree(HTREEITEM root); 146 }; 147 148 //UDP协议 149 class Head_UDP : public Head_Top 150 { 151 public: 152 u_short sPort; //源端口 153 u_short dPort; //目的端口 154 u_short len; //总长度 155 156 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 157 158 CString packet_own_print(); 159 160 void insertIntoTree(HTREEITEM root); 161 }; 162 163 164 class Head_IGMP : public Head_Top 165 { 166 public: 167 u_char version; //版本 168 u_char ig_type; //类型 169 u_char Multicast[4]; //D类 组地址 170 171 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 172 173 CString packet_own_print(); 174 175 void insertIntoTree(HTREEITEM root); 176 }; 177 178 //TCP协议 179 class Head_TCP : public Head_Top 180 { 181 public: 182 u_short sPort; //源端口 183 u_short dPort; //目的端口 184 int seq; //序号 185 int refirm_seq; //确认序号 186 u_short header_len; //首部长度 *4 187 u_char URG; //紧急指针有效 188 u_char ACK; //确认字段有效 189 u_char PSH; //推送数据 190 u_char RST; //连接复位 191 u_char SYN; //连接建立时序号同步 192 u_char FIN; //终止连接 193 u_short window_size; //窗口大小 194 u_short imppoint; //紧急指针 标志那些数据可以优先处理 195 196 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 197 198 CString packet_own_print(); 199 200 void insertIntoTree(HTREEITEM root); 201 }; 202 203 //OSPF协议 204 class Head_OSPF : public Head_Top 205 { 206 public: 207 u_char version; //版本 208 u_short ospf_type; //报文类型 209 u_short len; //长度 210 u_char routerIp[4];//路由器标识 211 u_char areaIp[4]; //区域标识 212 u_short autype; //验证类型 213 CString auData; //验证信息 214 215 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 216 217 CString packet_own_print(); 218 219 void insertIntoTree(HTREEITEM root); 220 }; 221 222 //域名解析协议 223 class Head_DNS : public Head_Top 224 { 225 public: 226 u_short sign; //唯一标识 227 u_char symbol[2]; //标志 228 u_char QR; //查询还是响应报文 229 u_short questionNum; //问题数目 230 u_short answerNum; //回答数目 231 u_short au_answer; //权威回答数目 232 u_short ex_answer; //附加回答数目 233 234 CString question; //问题 235 236 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 237 238 CString packet_own_print(); 239 240 void insertIntoTree(HTREEITEM root); 241 }; 242 243 //DHCP协议 244 class Head_DHCP : public Head_Top 245 { 246 public: 247 u_char op; //操作代码 248 u_char Htype; //硬件地址类型 1表示以太网地址 249 u_short Hlen; //硬件地址长度 250 u_short Hops; //跳数 经过一个路由器加一 251 int Transaction_ID; //事务ID 请求和回复时判断的依据 唯一 252 u_short Secs; //客户机启动时间 253 u_char Ciaddr[4]; //客户端IP地址 如果继续使用之前的IP 放在这里 254 u_char Yiaddr[4]; //服务器提供的IP地址 255 u_char Siaddr[4]; //服务器IP地址 256 u_char Giaddr[4]; //转发代理地址 跨网段获取IP时候使用 257 u_char Chaddr[6]; //客户端硬件地址 Mac地址 258 CString Sname; //服务器名称 259 CString File; //引导文件名称 260 u_short message_type; //消息类型 0x35 只是识别一种消息类型 261 u_short message_in; //具体消息 262 263 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 264 265 CString packet_own_print(); 266 267 void insertIntoTree(HTREEITEM root); 268 }; 269 270 //超文本协议 271 class Head_HTTP : public Head_Top 272 { 273 public: 274 CString HttpData; //打印信息 275 276 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 277 278 CString packet_own_print(); 279 280 void insertIntoTree(HTREEITEM root); 281 }; 282 283 //FTP协议分析 主动模式和被动模式 控制端口 数据端口 284 class Head_FTP : public Head_Top 285 { 286 public: 287 CString command; //只是打印命令 不做解析 288 289 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 290 291 CString packet_own_print(); 292 293 void insertIntoTree(HTREEITEM root); 294 }; 295 296 //简单邮件协议SMTP 297 class Head_SMTP : public Head_Top 298 { 299 public: 300 CString command; 301 302 void packet_analysis(u_char* data,Packet_Type& type,int remain_len); 303 304 CString packet_own_print(); 305 306 void insertIntoTree(HTREEITEM root); 307 }; 308 309 #endif
源文件我给出部分代码,毕竟大同小异,看看上面的资料就都会写了
1 void Head_EthernetII::packet_analysis(u_char* data,Packet_Type& type,int remain_len) 2 { 3 memcpy(dMac,data,6); 4 memcpy(sMac,data+6,6); 5 memcpy(kind,data+12,2); 6 7 int tmpKind=kind[0]<<8|kind[1]; 8 9 own_type=EthernetII; 10 own_len=14; 11 remain_len-=own_len; 12 13 switch (tmpKind) 14 { 15 case sign_ipv4: 16 next=new Head_IPv4(); 17 next->packet_analysis(data+14,type,remain_len); 18 break; 19 case sign_arp: 20 next=new Head_ARP(); 21 next->packet_analysis(data+14,type,remain_len); 22 break; 23 case sign_ipv6: 24 next=new Head_IPv6(); 25 next->packet_analysis(data+14,type,remain_len); 26 break; 27 case sign_pppoe: 28 next=new Head_PPPoE(); 29 next->packet_analysis(data+14,type,remain_len); 30 break; 31 default: 32 next=NULL; 33 type=EthernetII; 34 break; 35 } 36 } 37 38 void Head_PPPoE::packet_analysis(u_char* data,Packet_Type& type,int remain_len) 39 { 40 version=(data[0]&0xf0)>>4; 41 pppoe_type=data[0]&0x0f; 42 code=data[1]; 43 session_id=data[2]<<8|data[3]; 44 len=data[4]<<8|data[5]; 45 46 own_type=PPPoE; 47 own_len=8; 48 remain_len-=own_len; 49 50 int kind=data[6]<<8|data[7]; 51 52 if (0x0021==kind) 53 { 54 nextDataType="IP数据包"; 55 next=new Head_IPv4(); 56 next->packet_analysis(data+8,type,remain_len); 57 } 58 else 59 { 60 switch (kind) 61 { 62 case 0xc021: 63 nextDataType="链路控制数据LCP"; 64 break; 65 case 0x8021: 66 nextDataType="网络控制数据NCP"; 67 break; 68 case 0xc023: 69 nextDataType="安全性认证PAP"; 70 break; 71 case 0xc025: 72 nextDataType="LQR"; 73 break; 74 case 0xc223: 75 nextDataType="安全性认证CHAP"; 76 break; 77 } 78 next=NULL; 79 type=PPPoE; 80 } 81 } 82 83 void Head_IPv4::packet_analysis(u_char* data,Packet_Type& type,int remain_len) 84 { 85 version=(data[0]&0xf0)>>4; 86 header_len=data[0]&0x0f; 87 service_type=data[1]; 88 len=data[2]<<8|data[3]; 89 sign=data[4]<<8|data[5]; 90 df=data[6]&0x40>>6; 91 mf=data[6]&0x20>>5; 92 offset=(data[6]&0x1f)|data[7]; 93 TTL=data[8]; 94 protocol=data[9]&0xff; 95 memcpy(sIp,data+12,4); 96 memcpy(dIp,data+16,4); 97 98 own_type=IPv4; 99 own_len=header_len*4; 100 remain_len=len-own_len; 101 102 switch (protocol) 103 { 104 case TYPE_ICMP_IPV4: 105 next=new Head_ICMP(); 106 next->packet_analysis(data+header_len*4,type,remain_len); 107 break; 108 case TYPE_IGMP: 109 next=new Head_IGMP(); 110 next->packet_analysis(data+header_len*4,type,remain_len); 111 break; 112 case TYPE_TCP: 113 next=new Head_TCP(); 114 next->packet_analysis(data+header_len*4,type,remain_len); 115 break; 116 case TYPE_UDP: 117 next=new Head_UDP(); 118 next->packet_analysis(data+header_len*4,type,remain_len); 119 break; 120 default: 121 next=NULL; 122 type=IPv4; 123 break; 124 } 125 }
好了,有了协议分析的结果,后面的三个模块就很好写了,这里先给出流程图
关于进程流量这块,没有映射到具体的程序上面,做的很简陋,给出部分代码
bool CProcessDlg::GetAllProcessesInfo() { /*得到所有进程的快照*/ HANDLE hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if(INVALID_HANDLE_VALUE==hProcessSnap) return false; /*保存进程信息的结构体*/ PROCESSENTRY32 ProEntry32; memset(&ProEntry32,0,sizeof(ProEntry32)); ProEntry32.dwSize=sizeof(ProEntry32); /*得到第一个进程的信息*/ if(!Process32First(hProcessSnap,&ProEntry32)) { CloseHandle(hProcessSnap); return false; } /*显示所有的进程的相关信息*/ do { /*显示进程名称*/ if (ProEntry32.th32ProcessID!=0) //不包括系统进程 { CString name=CString(ProEntry32.szExeFile); int index; if (isContainString(name,index)) m_allProcesses[index]._processPID.push_back(ProEntry32.th32ProcessID); else { ProcessInfo tpInfo; tpInfo._processName=name; tpInfo._processPID.push_back(ProEntry32.th32ProcessID); m_allProcesses.push_back(tpInfo); } } } while(Process32Next(hProcessSnap,&ProEntry32)); CloseHandle(hProcessSnap); return true; } void CProcessDlg::GetAllPortByProcessId(DWORD dwProcessId,std::vector<int>& port) { HMODULE hModule=LoadLibraryW(L"iphlpapi.dll"); if (hModule==NULL) return; //加载失败 //Win7 Vista系统下使用 //tcp 部分 PMIB_TCPEXTABLE_VISTA pTcpExTable = NULL; PFNInternalGetTcpTable2 pInternalGetTcpTable2 = (PFNInternalGetTcpTable2)GetProcAddress(hModule, "InternalGetTcpTable2"); if (pInternalGetTcpTable2!=NULL) { if (pInternalGetTcpTable2(&pTcpExTable, GetProcessHeap(), 1)) { if (pTcpExTable) HeapFree(GetProcessHeap(), 0, pTcpExTable); FreeLibrary(hModule); hModule = NULL; return; } for (UINT i=0;i<pTcpExTable->dwNumEntries;i++) { // 过滤掉数据,只获取我们要查询的进程的 TCP Port 信息 if(dwProcessId==pTcpExTable->table[i].dwProcessId) port.push_back(ntohs(0x0000FFFF & pTcpExTable->table[i].dwLocalPort)); } if (pTcpExTable) HeapFree(GetProcessHeap(), 0, pTcpExTable); } //udp 部分 PMIB_UDPEXTABLE pUdpExTable=NULL; PFNInternalGetUdpTableWithOwnerPid pInternalGetUdpTableWithOwnerPid; pInternalGetUdpTableWithOwnerPid=(PFNInternalGetUdpTableWithOwnerPid)GetProcAddress(hModule, "InternalGetUdpTableWithOwnerPid"); if (pInternalGetUdpTableWithOwnerPid != NULL) { if (pInternalGetUdpTableWithOwnerPid(&pUdpExTable, GetProcessHeap(), 1)) { if (pUdpExTable) HeapFree(GetProcessHeap(), 0, pUdpExTable); FreeLibrary(hModule); hModule = NULL; return; } for (UINT i=0;i<pUdpExTable->dwNumEntries;i++) { if(dwProcessId == pUdpExTable->table[i].dwProcessId) port.push_back(ntohs(0x0000FFFF & pUdpExTable->table[i].dwLocalPort)); } if (pUdpExTable) HeapFree(GetProcessHeap(), 0, pUdpExTable); } FreeLibrary(hModule); hModule = NULL; } void CProcessDlg::OnTimer(UINT_PTR nIDEvent) { clearAllSpeed(); int maxSize=PData->getPackerResSize(); for(int i=m_currSize;i<maxSize;i++) { int sPort,dPort; PacketAnalysis* res=PData->getPacketResAtIndex(i); if (res->getPort(sPort,dPort)) //获取到端口 { for(int i=0;i<m_allProcesses.size();i++) { for(int j=0;j<m_allProcesses[i]._processPort.size();j++) { if (m_allProcesses[i]._processPort[j]==sPort) { m_allProcesses[i]._upSpeed+=res->getLength(); m_allProcesses[i]._upTotal+=res->getLength(); break; } if (m_allProcesses[i]._processPort[j]==dPort) { m_allProcesses[i]._downSpeed+=res->getLength(); m_allProcesses[i]._downTotal+=res->getLength(); break; } } } } } m_currSize=maxSize; //设置文本 CString strItem; for(int i=0;i<m_allProcesses.size();i++) { strItem.Format("%.2lf",m_allProcesses[i]._upSpeed/1024); m_process.SetItemText(i,2,strItem); strItem.Format("%.2lf",m_allProcesses[i]._downSpeed/1024); m_process.SetItemText(i,3,strItem); strItem.Format("%.2lf",m_allProcesses[i]._upTotal/1024); m_process.SetItemText(i,4,strItem); strItem.Format("%.2lf",m_allProcesses[i]._downTotal/1024); m_process.SetItemText(i,5,strItem); } CDialogEx::OnTimer(nIDEvent); }
然后就是最后一块,使用SQLITE数据库,简单的桌面型数据库,保存每段时间的流量,当然这里主要是绘图控件的使用,从MsChart到TeeChart,最后放弃使用这些复杂的控件,直接使用High Speed Charting Control,简单实用,具体的大家可以
看 http://www.codeproject.com/Articles/14075/High-speed-Charting-Control,网上的博客啥的写的都很浅,直接看作者给的例子,当然直接看源码是最好的了。给出部分代码
void SumPicDlg::initDlg() { //初始化数据类型 m_dataType.InsertString(0,"网络速度"); m_dataType.InsertString(1,"月流量"); m_dataType.InsertString(2,"日流量"); m_dataType.InsertString(3,"时流量"); m_dataType.SetCurSel(0); initSpeedSelect(); //网络速度统计图初始化 m_chartCtrl.EnableRefresh(false); m_chartCtrl.SetEdgeType(EDGE_SUNKEN); //凹陷边框 m_chartCtrl.SetBorderColor(RGB(0,180,0)); //边框颜色 m_chartCtrl.SetBackColor(RGB(0,50,0)); //设置背景颜色 m_chartCtrl.GetLegend()->SetVisible(true); //解释文本可见 m_chartCtrl.GetTitle()->AddString(_T("网络速度")); //标题内容 CChartFont titleFont; titleFont.SetFont(_T("Arial Black"),140,true,false,true); m_chartCtrl.GetTitle()->SetFont(titleFont); //设置字体 m_chartCtrl.GetTitle()->SetColor(RGB(200,0,10)); //设置颜色 //创建坐标轴 CChartStandardAxis* pLeftAxis=m_chartCtrl.CreateStandardAxis(CChartCtrl::LeftAxis); pLeftAxis->GetLabel()->SetText(_T("速度(K/S)")); pLeftAxis->SetAutomatic(true); pLeftAxis->GetGrid()->SetColor(RGB(0,180,0)); CChartStandardAxis* pBottomAxis=m_chartCtrl.CreateStandardAxis(CChartCtrl::BottomAxis); pBottomAxis->SetMinMax(0,30); pBottomAxis->SetDiscrete(true); pBottomAxis->SetTickIncrement(false,1.0); pBottomAxis->GetGrid()->SetColor(RGB(0,180,0)); CChartCrossHairCursor* pCursor=m_chartCtrl.CreateCrossHairCursor(); //交叉线光标 m_chartCtrl.ShowMouseCursor(false); //隐藏光标 //初始化数据 for(int i=0;i<30;i++) { m_chartX[i]=i; m_recvChartY[i]=0; m_sendChartY[i]=0; } m_chartCtrl.RemoveAllSeries(); //去除所有的序列 //创建折线数据 CChartBarSerie* pBarSerie=m_chartCtrl.CreateBarSerie(); CChartLineSerie* pRecvSerie=m_chartCtrl.CreateLineSerie(); CChartLineSerie* pSendSerie=m_chartCtrl.CreateLineSerie(); pRecvSerie->AddPoints(m_chartX,m_recvChartY,30); pRecvSerie->SetColor(RGB(255,0,0)); pRecvSerie->SetName("接收速度"); pRecvSerie->EnableShadow(true); pRecvSerie->SetShadowColor(RGB(128,255,128)); pRecvSerie->SetWidth(2); pSendSerie->AddPoints(m_chartX,m_sendChartY,30); pSendSerie->SetColor(RGB(255,255,0)); pSendSerie->SetName("发送速度"); pSendSerie->EnableShadow(true); pSendSerie->SetWidth(2); pBarSerie->SetColor(RGB(255,255,0)); pBarSerie->SetGradient(RGB(255,10,10),gtVertical); pBarSerie->SetName(""); m_chartCtrl.EnableRefresh(true); m_toInitChart=false; //不需要再初始化 SetTimer(UPDATE_SPEED,1000,NULL); } void SumPicDlg::addDataFromSQL(int maxNum,bool isLine,bool isBar,CChartBarSerie* pBar,CChartLineSerie* pLine) { CString sql; m_chartCtrl.GetBottomAxis()->SetMinMax(0,maxNum+1); double* yValue=new double[maxNum]; double maxValue=0; for(int i=0;i<maxNum;i++) { switch (m_dataSrcIndex) { case 1: sql.Format("select sum(FlowSum) from Flow where Year=%d and Month=%d",m_selectYear,i+1); break; case 2: sql.Format("select sum(FlowSum) from Flow where Year=%d and Month=%d and Day=%d",m_selectYear,m_selectMonth,i+1); break; case 3: sql.Format("select sum(FlowSum) from Flow where Year=%d and Month=%d and Day=%d and Hour=%d",m_selectYear,m_selectMonth,m_selectDay,i+1);break; } PData->m_sqlInterFace.selectData(sql); if ((yValue[i]=PData->m_sqlInterFace.getDataAtIndex(0))==-1) yValue[i]=0; if (yValue[i]>maxValue) maxValue=yValue[i]; } m_chartCtrl.GetLeftAxis()->SetMinMax(0,maxValue+maxValue/4); ((CChartStandardAxis*)m_chartCtrl.GetLeftAxis())->SetTickIncrement(false,(int)(maxValue/6)); for(int i=0;i<maxNum;i++) { if (isLine) { sql.Format("%.1lf",yValue[i]); pLine->AddPoint(i+1,yValue[i]); pLine->CreateBalloonLabel(i,TChartString(sql)); } if (isBar) pBar->AddPoint(i+1,yValue[i]); } delete[] yValue; } void SumPicDlg::OnBnClickedButtonCheck() { m_chartCtrl.EnableRefresh(false); //获得 序列 CChartLineSerie* pLine1=(CChartLineSerie*)m_chartCtrl.GetSerie(1); CChartBarSerie* pBar=(CChartBarSerie*)m_chartCtrl.GetSerie(0); pLine1->ClearSerie(); pBar->ClearSerie(); //获取 图表类型 bool isLine=m_checkLine.GetCheck(); bool isBar=m_checkBar.GetCheck(); #pragma region 图表初始化部分 if (m_toInitChart) //需要初始化 图表 { pLine1->SetName("流量"); pBar->SetName("流量"); CChartLineSerie* pLine2=(CChartLineSerie*)m_chartCtrl.GetSerie(2); pLine2->ClearSerie(); pLine2->SetName(""); CChartStandardAxis* pLeft=(CChartStandardAxis*)m_chartCtrl.GetLeftAxis(); pLeft->GetLabel()->SetText(_T("流量(Kb)")); pLeft->SetAutomatic(false); pLeft->SetMinMax(0,100); pLeft->SetTickIncrement(false,1); switch (m_dataSrcIndex) { case 1: m_chartCtrl.GetTitle()->RemoveAll(); m_chartCtrl.GetTitle()->AddString(_T("月流量")); break; case 2: m_chartCtrl.GetTitle()->RemoveAll(); m_chartCtrl.GetTitle()->AddString(_T("日流量")); break; case 3: m_chartCtrl.GetTitle()->RemoveAll(); m_chartCtrl.GetTitle()->AddString(_T("时流量")); } m_toInitChart=false; } #pragma endregion if (((CButton*)GetDlgItem(IDC_RADIO_LASTDAY))->GetCheck()) { //暂时没写 } else { switch (m_dataSrcIndex) { case 1:addDataFromSQL(m_maxMonthNum,isLine,isBar,pBar,pLine1); break; case 2:addDataFromSQL(m_maxDayNum,isLine,isBar,pBar,pLine1); break; case 3:addDataFromSQL(m_maxHourNum,isLine,isBar,pBar,pLine1); break; } } m_chartCtrl.EnableRefresh(true); }
差不多了,程序的源码在后面我会放出,当然只是入门级别的资料,程序写的很简陋。