在网络开发场景中,PCAP文件是存储网络数据包捕获结果的通用格式,很多网络分析工具都会生成这类文件。如果需要在C#程序中读取和分析PCAP文件,首先要理解PCAP文件的二进制存储结构,再按照规范逐段解析数据。

PCAP文件基础结构
PCAP文件整体分为两部分,第一部分是全局的文件头,第二部分是多个连续的数据包记录。每个数据包记录又包含自身的数据包头和数据包实际内容。
全局文件头结构
PCAP全局文件头固定占24字节,各字段含义如下:
| 字段名 | 字节长度 | 说明 |
|---|---|---|
| Magic Number | 4字节 | 标识文件类型,小端模式下为0xa1b2c3d4 |
| Major Version | 2字节 | 主版本号,通常为2 |
| Minor Version | 2字节 | 次版本号,通常为4 |
| This Zone | 4字节 | 时区偏移,通常为0 |
| Sig Figs | 4字节 | 精确时间戳,通常为0 |
| Snap Length | 4字节 | 最大捕获数据包长度 |
| Link Type | 4字节 | 链路层类型,1代表以太网 |
数据包记录结构
每个数据包记录由16字节的数据包头和不定长的数据包内容组成,数据包头字段如下:
- 时间戳秒数:4字节,数据包捕获的秒级时间戳
- 时间戳微秒数:4字节,数据包捕获的微秒级偏移
- 捕获长度:4字节,实际捕获到的数据包长度
- 原始长度:4字节,数据包在链路上的原始长度
C#解析PCAP文件的实现步骤
1. 读取全局文件头
首先打开PCAP文件,读取前24字节解析全局文件头,验证文件合法性:
using System;
using System.IO;
public class PcapGlobalHeader
{
public uint MagicNumber { get; set; }
public ushort MajorVersion { get; set; }
public ushort MinorVersion { get; set; }
public uint SnapLength { get; set; }
public uint LinkType { get; set; }
}
public class PcapParser
{
public PcapGlobalHeader ReadGlobalHeader(string filePath)
{
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fs))
{
PcapGlobalHeader header = new PcapGlobalHeader();
header.MagicNumber = br.ReadUInt32();
// 验证Magic Number,小端模式下的标准值
if (header.MagicNumber != 0xa1b2c3d4)
{
throw new InvalidDataException("不是合法的PCAP文件");
}
header.MajorVersion = br.ReadUInt16();
header.MinorVersion = br.ReadUInt16();
br.ReadUInt32(); // 跳过This Zone字段
br.ReadUInt32(); // 跳过Sig Figs字段
header.SnapLength = br.ReadUInt32();
header.LinkType = br.ReadUInt32();
return header;
}
}
}
2. 读取数据包记录
读取完全局头之后,循环读取后续的数据包记录,直到文件结束:
public class PcapPacketHeader
{
public uint TimestampSeconds { get; set; }
public uint TimestampMicroseconds { get; set; }
public uint CapturedLength { get; set; }
public uint OriginalLength { get; set; }
}
public class PcapPacket
{
public PcapPacketHeader Header { get; set; }
public byte[] Data { get; set; }
}
public List<PcapPacket> ReadAllPackets(string filePath)
{
List<PcapPacket> packets = new List<PcapPacket>();
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fs))
{
// 先跳过全局头24字节
br.ReadBytes(24);
while (fs.Position < fs.Length)
{
PcapPacket packet = new PcapPacket();
PcapPacketHeader pktHeader = new PcapPacketHeader();
// 读取数据包头16字节
pktHeader.TimestampSeconds = br.ReadUInt32();
pktHeader.TimestampMicroseconds = br.ReadUInt32();
pktHeader.CapturedLength = br.ReadUInt32();
pktHeader.OriginalLength = br.ReadUInt32();
packet.Header = pktHeader;
// 读取数据包内容
packet.Data = br.ReadBytes((int)pktHeader.CapturedLength);
packets.Add(packet);
}
}
return packets;
}
3. 解析链路层数据
如果链路层类型为以太网(LinkType=1),可以进一步解析以太网帧:
public class EthernetFrame
{
public byte[] DestinationMac { get; set; }
public byte[] SourceMac { get; set; }
public ushort EtherType { get; set; }
public byte[] Payload { get; set; }
}
public EthernetFrame ParseEthernetFrame(byte[] data)
{
if (data.Length < 14)
{
return null;
}
EthernetFrame frame = new EthernetFrame();
frame.DestinationMac = new byte[6];
Array.Copy(data, 0, frame.DestinationMac, 0, 6);
frame.SourceMac = new byte[6];
Array.Copy(data, 6, frame.SourceMac, 0, 6);
frame.EtherType = (ushort)((data[12] << 8) | data[13]);
frame.Payload = new byte[data.Length - 14];
Array.Copy(data, 14, frame.Payload, 0, frame.Payload.Length);
return frame;
}
使用示例
将上述逻辑组合起来,即可实现PCAP文件的读取和基础分析:
class Program
{
static void Main(string[] args)
{
string pcapPath = "test.pcap";
try
{
PcapParser parser = new PcapParser();
var globalHeader = parser.ReadGlobalHeader(pcapPath);
Console.WriteLine($"PCAP版本:{globalHeader.MajorVersion}.{globalHeader.MinorVersion}");
Console.WriteLine($"链路层类型:{globalHeader.LinkType}");
var packets = parser.ReadAllPackets(pcapPath);
Console.WriteLine($"共解析到{packets.Count}个数据包");
foreach (var pkt in packets)
{
DateTime timestamp = new DateTime(1970, 1, 1).AddSeconds(pkt.Header.TimestampSeconds).AddMilliseconds(pkt.Header.TimestampMicroseconds / 1000);
Console.WriteLine($"数据包时间:{timestamp}, 长度:{pkt.Header.CapturedLength}");
var ethFrame = ParseEthernetFrame(pkt.Data);
if (ethFrame != null)
{
Console.WriteLine($"源MAC:{BitConverter.ToString(ethFrame.SourceMac)},目的MAC:{BitConverter.ToString(ethFrame.DestinationMac)}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"解析失败:{ex.Message}");
}
}
}
注意事项
实际使用中需要注意几个问题,首先是字节序问题,PCAP文件默认使用小端字节序,解析时要确保BinaryReader的小端读取逻辑正确。其次是大文件场景,如果PCAP文件体积较大,不要一次性将所有数据包加载到内存,可以采用流式读取的方式逐个处理。最后是链路层类型兼容,不同抓包场景可能使用不同的链路层类型,需要根据LinkType字段调整解析逻辑。
C#PCAP文件解析网络数据包捕获packet_analysis修改时间:2026-07-15 11:27:38