之前一直做视觉相关的工作,近期接触激光雷达才发现,激光雷达传感器厂商保存数据的方式一般采用Pcap包的形式,虽然不太清楚为什么要这么做,但不管清不清楚总归只能跟着它这么搞。
一个Pcap文件包括“Pcap报头”,“数据区”两个部分,其中数据区又分成多个数据包,每个包有报头和数据两个部分,总体结构可见下图:
其中固定大小的为“Pcap报头”和数据包中的“数据报头”。
“Pcap报头”大小为24个字节,具体内容因为与本人所要内容无关,直接跳过,等有需要再补充。
“数据报头”大小为16个字节,前8个字节为时间戳数据,9~12字节为数据长度,13~16字节为离线数据长度。如果只想知道数据长度是多少,则只需要读取9~12字节,然后往后面读取其描述的长度即可。
Pcap包的简单解析c++代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <Windows.h>
#define BUFLEN 1024*1024
using namespace std;
using namespace cv;
int main()
{
uchar *data = new uchar[BUFLEN];//开辟数据区
FILE *pcapfile;
fopen_s(&pcapfile, "16.pcap","rb");
//读取pcap报头
fread_s(data, BUFLEN, 1, 24, pcapfile);
//循环读取数据包,直到文件末尾
while (true)
{
//数据包头
int val=fread_s(data, BUFLEN, 1, 16, pcapfile);
if (!val)
{
break;
}
long packetLen = data[8] | data[9] << 8 | data[10] << 16 | data[11] << 24;//读取数据区长度
//cout << packetLen << endl;
//数据区
val = fread_s(data, BUFLEN, 1, packetLen, pcapfile);
if (!val)
{
break;
}
//数据处理
}
//对象释放
fclose(pcapfile);
delete[]data;
}
在Windows上,也可以直接用WinPcap来解析,有两种写法:
写法1:
#include <stdio.h>
#include <pcap.h>
#define LINE_LEN 16
void dispatcher_handler(u_char* temp1,const struct pcap_pkthdr* header,const u_char* pkt_data)
{
//打印包信息
printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len);
打印包内容
//for (int i = 1; (i < header->caplen + 1); i++)
//{
// printf("%.2x ", pkt_data[i - 1]);
// if ((i % LINE_LEN) == 0) printf("\n");
//}
//printf("\n\n");
}
int main()
{
pcap_t* fp;
char errbuf[PCAP_ERRBUF_SIZE];
//打开Pcap包
if ((fp = pcap_open_offline("1.pcap",errbuf)) == NULL)
{
return -1;
}
//读取文件直到结尾
pcap_loop(fp, 0, dispatcher_handler, NULL);
pcap_close(fp);
return 0;
}
写法2:
#include <stdio.h>
#include <pcap.h>
#define LINE_LEN 16
int main()
{
pcap_t* fp;
char errbuf[PCAP_ERRBUF_SIZE];
//打开Pcap包
if ((fp = pcap_open_offline("1.pcap",errbuf)) == NULL)
{
return -1;
}
//读取文件直到结尾
struct pcap_pkthdr* header; //报文头
const unsigned char* pkt_data; //报文内容
while ((pcap_next_ex(fp, &header, &pkt_data)) > 0)
{
printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len);
}
pcap_close(fp);
return 0;
}
如果要获得IP信息,可以添加相应的代码如下:
#include <stdio.h>
#include <pcap.h>
#define LINE_LEN 16
/* 4 bytes IP address */
typedef struct ip_address
{
u_char byte1;
u_char byte2;
u_char byte3;
u_char byte4;
}ip_address;
/* IPv4 header */
typedef struct ip_header
{
u_char ver_ihl; // Version (4 bits) + Internet header length (4 bits)
u_char tos; // Type of service
u_short tlen; // Total length
u_short identification; // Identification
u_short flags_fo; // Flags (3 bits) + Fragment offset (13 bits)
u_char ttl; // Time to live
u_char proto; // Protocol
u_short crc; // Header checksum
ip_address saddr; // Source address
ip_address daddr; // Destination address
u_int op_pad; // Option + Padding
}ip_header;
int main()
{
pcap_t* fp;
char errbuf[PCAP_ERRBUF_SIZE];
//打开Pcap包
if ((fp = pcap_open_offline("1.pcap",errbuf)) == NULL)
{
return -1;
}
//读取文件直到结尾
struct pcap_pkthdr* header; //报文头
const unsigned char* pkt_data; //报文内容
ip_header* ih;
while ((pcap_next_ex(fp, &header, &pkt_data)) > 0)
{
ih = (ip_header*)(pkt_data + 14); //length of ethernet header
printf("%d.%d.%d.%d -> %d.%d.%d.%d\n",
ih->saddr.byte1,ih->saddr.byte2,ih->saddr.byte3,ih->saddr.byte4,
ih->daddr.byte1,ih->daddr.byte2,ih->daddr.byte3,ih->daddr.byte4);
printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len);
}
pcap_close(fp);
return 0;
}