PCAP文件:一个pcap文件由文件头(pacp header)和 多个 (packet header && packet data) 组成,其中 (packet header && packet data) 的数量 就是pcap文件中包含的packet数量。
本列子是通过https接口请求api获取二进制数据流 接口响应数据如下
具体实现代码DEMO如下:
// DownloadDataPackets 下载数据包
func DownloadDataPackets(ctx context.Context, baseURI, user, pass, filter, beginTime, endTime, certPath string) (filaname string, err error) {
var params = map[string]interface{}{
//请求接口需要的数据
}
// 请求接口判断是否有数据,如果有数据再进行后面数据下载请求
var respData []byte
req_times := 0
for {
req_times += 1
respData, err = DownloadDataPacketsRal(baseURI, params)
if err != nil {
return "", err
}
if dataPacketsIsExist(respData) {
break
}
time.Sleep(time.Duration(1) * time.Second)
if req_times > 60 {
return "", nil
}
}
fileName := "./packets.pcap"
// 创建文件
file, err := os.Create(fileName)
if err != nil {
return "", err
}
defer file.Close()
write := bufio.NewWriter(file)
// 1.写pacap header
writePcapHeader(write)
// 循环判断是否存在句柄
for {
// 下载句柄handle 4B
handle := binary.BigEndian.Uint32(respData[4:8])
// moreData 是否还有数据 1B
moreData := respData[8:9][0]
// 如果存在数据 就继续通过后handle请求获取下一页数据
params["handle"] = handle
//处理当前请求的数据包内容
packetAllDataWritePcap(write, respData)
if moreData <= 0 || handle <= 0 {
break
}
respData, err = DownloadDataPacketsRal(baseURI, params)
if err != nil {
return "", err
}
}
return fileName, nil
}
// packetAllDataWritePcap 获取数据包数据 并写入pcap文件中
func packetAllDataWritePcap(w *bufio.Writer, respData []byte) {
// 响应数据包头部固定长度
inteHeaderLen := 9
// 响应数据长度
dataLen := len(respData)
var size int
size += dataLen
// 循环判断,并根据数据包下载片段重新定义inteHeaderLen,与响应长度进行比较
for inteHeaderLen < dataLen {
realData := respData[inteHeaderLen:]
// 2.写packet header
// 响应信息中实际的数据包内容 写入实际数据包内容,该数据长度是capLen
capLen := writePacketHeader(w, realData)
// 3.写packet body
// 数据长度就是capLen个Byte,在此之后是一个新的Packet Header,新的Packet Data,如此循环
writePacketData(w, realData[17:capLen+17])
w.Flush()
inteHeaderLen = inteHeaderLen + 17 + int(capLen)
}
}
// writePcapHeader 写pacap头文件
func writePcapHeader(w *bufio.Writer) {
/*
pcap文件头:24字节(固定)
Magic(4Byte):标记文件开始,并用来识别文件自己和字节顺序
Major(2Byte): 当前文件主要的版本号
Minor(2Byte): 当前文件次要的版本号
ThisZone(4Byte):当地的标准时间,如果用的是GMT则全零,一般都直接写 0000 0000
SigFigs(4Byte):时间戳的精度
SnapLen(4Byte):最大的存储长度
LinkType(4Byte):链路类型
*/
fileHeader := []byte("\xD4\xC3\xB2\xA1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x01\x00\x00\x00")
w.Write(fileHeader)
}
// writePacketHeader 写packet头文件
func writePacketHeader(w *bufio.Writer, realData []byte) uint32 {
/*
存在多个packet header
Timestamp(4Byte):被捕获时间的高位,精度为seconds
Timestamp(4Byte):被捕获时间的低位,精度为microseconds
Caplen(4Byte):当前数据区的长度,即抓取到的数据帧长度,不包括Packet Header本身的长度,单位是 Byte ,由此可以得到下一个数据帧的位置。
Len(4Byte):离线数据长度:网络中实际数据帧的长度,一般不大于caplen,多数情况下和Caplen数值相等。
*/
var packatHeaderData []byte
// timeNanosecond 时间戳8个字节(q)- 大端序
timeNanosecond := binary.BigEndian.Uint64(realData[1:9])
// pktLen 数据包长度是4字节(I) - 大端序
pktLen := binary.BigEndian.Uint32(realData[9:23])
// capLen 捕获长度是4字节(I),网络序 - 大端序
capLen := binary.BigEndian.Uint32(realData[13:17])
// 数据包头的秒时间戳
timeSec := int(timeNanosecond / 1000000000)
timeMsec := int64((int(timeNanosecond) - timeSec*1000000000) / 1000)
// 获取高位时间戳
timeMsec32 := make([]byte, 4)
binary.LittleEndian.PutUint32(timeMsec32, uint32(timeMsec))
packatHeaderData = append(packatHeaderData, timeMsec32...)
packatHeaderData = append(packatHeaderData, timeMsec32...)
// 数据包长度,表示所抓获的数据包保存在pcap文件中的实际长度
capLen32 := make([]byte, 4)
binary.LittleEndian.PutUint32(capLen32, uint32(capLen))
packatHeaderData = append(packatHeaderData, capLen32...)
// 数据包实际长度,所抓获的数据包的真实长度,如果文件中保存不是完整的数据包,则这个值可能要比前面的数据包长度值大
pktLen32 := make([]byte, 4)
binary.LittleEndian.PutUint32(pktLen32, uint32(pktLen))
packatHeaderData = append(packatHeaderData, pktLen32...)
w.Write(packatHeaderData)
return capLen
}
// writePacketData 写packet文本信息
func writePacketData(w *bufio.Writer, packData []byte) {
// 写packet data
w.Write(packData)
}
// dataPacketsIsExist 查询数据包是否有数据
func dataPacketsIsExist(respData []byte) bool {
if len(respData) == 0 {
return false
}
if len(respData) == 9 && respData[8] != 1 {
return false
}
return true
}