winpcap 线程未安全关闭造成的问题————bogus savefile header

来源:互联网 发布:qt 关闭release优化 编辑:程序博客网 时间:2024/04/29 18:46

这几天愁眉不展,今天上午因为出去出差被人晃悠而得到些许的休息时间似乎让脑子有些清楚了。

先引用一段我之前的一个帖子,这反映了我的问题,但是一直无人回答,最后还是靠自己解决了,是有些感慨。

http://topic.csdn.net/u/20111130/23/41ac9eff-ef38-4ba9-8d10-874868a2f6b0.html?09082300472073257 内容如下

*********************************************************************************************

这个问题实在是一筹莫展啊,急切求助大虾了。
  这样一个项目,vc6写的一个界面与一个带网卡的fpga连的,pc向下发送一些命令,然后fpga通过网卡回复一些AD采集的数据,我需要把这些数据完整的不丢包的收下来并且保存成txt文件。
  主要在接收部分,,因为是点对点,所以也没有过滤,协议那两个字节设的是0xaa,0xbb之类的。我开了一个线程,用一个pcaploop来接收所有的包,并使用pcap_dump存到“1.txt”,按一个“停止扭”,停止接收线程关闭文件。然后解析数据包,就是之前保存的文件“1.txt”,将解出来的pkt_data内容保存到另外的txt中。这部分也是通过pcap_loop 调用一个回调函数来解包的。
  现在的问题主要是这样的,大概有50%的概率会在解析包的时候出错,即pcap_loop返回-1,用pcap_geterr获取最后的错误信息,发现是“bogus savefile header”。可这种情况不是每次都发生。我很奇怪,查了好多资料也没有什么头绪。
  主要代码如下:
  接收部分:

C/C++ code
ResetEvent(hEvent);//第二次接收时保证事件是无消息的。 recievednum=0;//重复接受时归零 fpt=fopen("1.txt","a"); …… dumpfile = pcap_dump_open(fp, "1.TXT"); if (dumpfile==NULL) { AfxMessageBox("Error opening output file"); //fprintf(stderr,"\nError opening output file\n"); return ; } // printf("\nlistening on %s... Press Ctrl+C to stop...\n", d->description); /* 释放设备列表 */ pcap_freealldevs(alldevs); int packetnum; packetnum=2; /* 开始捕获 */ hthread = CreateThread(NULL,0,main_thread,NULL,0,NULL);




mainthread 线程

C/C++ code
DWORD WINAPI main_thread(LPVOID lpParameter){ int code; code=pcap_loop(fp, 0, packet_handler, (unsigned char *)dumpfile);//第二个参数为cnt //其中cnt 参数为负值时pcap_loop()函数将始终循环运行,除非出现错误, //cnt=0 表示处理所有数据包,读取到EOF时结束 if (code == -1)//MessageBox(NULL, "pcap_loop调用出错 ", "信息 ",MB_OK); AfxMessageBox("pcap_loop调用出错 "); // if (code == -2)//MessageBox(NULL, "pcap_loop正常中断 ", "信息 ",MB_OK); // AfxMessageBox("未收到包,就停止了 "); pcap_close(fp); return 0;}



C/C++ code
void packet_handler(u_char *param, const pcap_pkthdr *header, const u_char *pkt_data)//第三个参数是包的内容{ /*hEvent为一通知Loop过程退出的事件,事件由结束抓包按钮发出*/ if (WaitForSingleObject(hEvent,0) != WAIT_TIMEOUT) { /*退出loop过程*/ pcap_breakloop(fp); return ; } pcap_dump((unsigned char *)dumpfile, header, pkt_data);}

停止接收和解析数据包的部分
C/C++ code
SetEvent(hEvent);//设置终止抓包事件 CloseHandle(hthread); if (!fclose(fpt)) { //AfxMessageBox("关闭文件成功!"); } else { AfxMessageBox("没有进行接收"); return; }//*******************************************开始解析**************************************** char source[PCAP_BUF_SIZE]; /* 根据新WinPcap语法创建一个源字符串 */ if ( pcap_createsrcstr( source, // 源字符串 PCAP_SRC_FILE, // 我们要打开的文件 NULL, // 远程主机 NULL, // 远程主机端口 "1.TXT", // 我们要打开的文件名 errbuf // 错误缓冲区 ) != 0) { MessageBox("cannot creat a source string!"); return; } /* 打开捕获文件 */ if ( (fp= pcap_open(source, // 设备名 65536, // 要捕捉的数据包的部分 // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容 PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式 1000, // 读取超时时间 NULL, // 远程机器验证 errbuf // 错误缓冲池 ) ) == NULL) { MessageBox("cannot open the file 1.txt"); return ; } filepath=MY_CURRENT_PATH(); m_filename="data"; filepath=filepath+m_filename+".txt"; // 读取并解析数据包,直到EOF为真 // MessageBox("hehe"); CFile file; file.Open(filepath,CFile::modeCreate); file.Close(); int code; code=pcap_loop(fp, 0, dispatcher_handler, NULL); if (code!=0) { AfxMessageBox("数据解析完成出错:error"); CString error; error=pcap_geterr(fp); AfxMessageBox(error); return; } else AfxMessageBox("解析数据成功"); CString str; str.Format("收到%d个有效包,结束接收回调函数",recievednum); AfxMessageBox(str); recievednum=0;//清零


解析包的回调函数
C/C++ code
void dispatcher_handler(u_char *temp1, const struct pcap_pkthdr *header, const u_char *pkt_data)//这个header是参数pcap_pkthdr//表示捕获到的数据包基本信息,包括时间,长度等信息.由pcap_loop自己来填充{ u_int i=0; CString temp; CString data; data=""; if ((pkt_data[12]==0xaa)&&(pkt_data[13]==0xbb))//(pkt_data[12]==0xaa)&&(pkt_data[13]==0xbb) { /* 打印数据包 */ for (i=15; (i < header->caplen + 1 ) ; i++) { //printf("%.2x ", pkt_data[i-1]); temp.Format("%.2x ", pkt_data[i-1]); data+=temp; if ( (i % LINE_LEN) == 0) //printf(" "); data+=""; //取消空格 } // printf("\n\n"); data+="\n"; //AfxMessageBox(data); SAVE_TO_TEXT(filepath,m_filename,data); recievednum++; }}




************************************************************************************

从源码里找到了关于“bogus savefile header”的内容:

C/C++ code
if (hdr->caplen > buflen) { /* * This can happen due to Solaris 2.3 systems tripping * over the BUFMOD problem and not setting the snapshot * correctly in the savefile header. If the caplen isn't * grossly wrong, try to salvage. */ static u_char *tp = NULL; static size_t tsize = 0; if (hdr->caplen > 65535) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "bogus savefile header"); return (-1); } if (tsize < hdr->caplen) { tsize = ((hdr->caplen + 1023) / 1024) * 1024; if (tp != NULL) free((u_char *)tp); tp = (u_char *)malloc(tsize); if (tp == NULL) { tsize = 0; snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BUFMOD hack malloc"); return (-1); } } amt_read = fread((char *)tp, 1, hdr->caplen, fp); if (amt_read != hdr->caplen) { if (ferror(fp)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "error reading dump file: %s", pcap_strerror(errno)); } else { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %u captured bytes, only got %lu", hdr->caplen, (unsigned long)amt_read); } return (-1); } /* * We can only keep up to buflen bytes. Since caplen > buflen * is exactly how we got here, we know we can only keep the * first buflen bytes and must drop the remainder. Adjust * caplen accordingly, so we don't get confused later as * to how many bytes we have to play with. */ hdr->caplen = buflen; memcpy((char *)buf, (char *)tp, buflen); }

这种情况怎么避免啊,是我处理方式的问题不行还是什么?
网上各处基本找不到类似的东西
我怀疑可能是dumpfile的时候存错了,可如果是这样,怎么避免呢?

************************************************************


winpcap的网上资料倒是有,但是基本都是manual里的内容,而手册中的东西最多就是些函数说明和简单介绍,和一系列的例子,我也没有研究核心npf,具体细节很多不清楚。一直以为这个问题与winpcap有关。

因为是解包的时候出现bogus savefile header的问题,而查sourcefile的解释又让人很难理解。以为是数据速率太快,而造成的pcap_dump错误。甚至考虑过用据说效率更高的pcap_live_dump改写。

问题的解决感觉得益于物理实验方法。

虽然过程中经历了不少歧途,但经过了大规模的实验和测试总结出了错误的规律,还是最终找到了解决问题的方向。

今天回来看到一篇分析堆文件格式的文章,然后打开我实验存下的数据,终于找到了问题的核心所在。

堆文件格式的文章地址为:http://blog.sina.com.cn/s/blog_61509be10100eq8v.html

仔细一分析,发现我错误的堆文件报错的地方一般打时间戳会打两遍,很多本该收到一次的命令包,竟然会隔几包之后再次收到,再辅以一些其他的数据问题,最终想到似乎是线程未安全关闭,而造成两个甚至两个线程同时接收一样的数据,却要存到一个文件中,这样就造成了内容的重叠和错误。

   之后研究了一下线程关闭的问题,修改了代码,终于解决了这问题。(本人菜鸟)

添加的代码如下:

DWORD dwExitCode;GetExitCodeThread(hthread,&dwExitCode);if (dwExitCode==STILL_ACTIVE){TerminateThread(hthread,dwExitCode);//可能没有关闭成功线程。}CloseHandle(hthread);

这样就确保了每次停止接收分析数据之后,线程安全关闭了。

当然没有明确地看到出错时两个线程同时工作,而且我也没弄明白两个名字相同的线程怎么能同时运行,这方面的理论知识需要好好补一补了。

不过我基本确定就是这个问题造成的,之后进行了长达1个小时的测试都没有发现这样的问题,并且同时发现了一个更顺理成章的现象,连续接收两次,不处理数据,是必定会错的。

必须恶补基础知识了。

原创粉丝点击