编辑:好的,我得到了一些代码。实际上是相当多的代码。它允许你向前和向后扫描数据包头。
我不能保证它没有漏洞,而且你肯定想调整缓冲区大小以查看其表现... 但是,给定你发送给我的同一文件,它至少在向前和向后扫描时显示出相同的数据包头位置 :)
在提供代码之前,我仍然建议如果可能的话,通过文件进行一次扫描并保存数据包信息的索引以备后用可能是更好的方法。
无论如何,这里是代码(包括除示例程序外没有测试):
PacketHeader.cs:
using System;
namespace Chapter10Reader
{
public sealed class PacketHeader
{
private readonly long filePosition;
private readonly ushort channelId;
private readonly uint packetLength;
private readonly uint dataLength;
private readonly byte dataTypeVersion;
private readonly byte sequenceNumber;
private readonly byte packetFlags;
private readonly byte dataType;
private readonly ulong relativeTimeCounter;
public long FilePosition { get { return filePosition; } }
public ushort ChannelId { get { return channelId; } }
public uint PacketLength { get { return packetLength; } }
public uint DataLength { get { return dataLength; } }
public byte DataTypeVersion { get { return dataTypeVersion; } }
public byte SequenceNumber { get { return sequenceNumber; } }
public byte PacketFlags { get { return packetFlags; } }
public byte DataType { get { return dataType; } }
public ulong RelativeTimeCounter { get { return relativeTimeCounter; } }
public PacketHeader(ushort channelId, uint packetLength, uint dataLength, byte dataTypeVersion,
byte sequenceNumber, byte packetFlags, byte dataType, ulong relativeTimeCounter, long filePosition)
{
this.channelId = channelId;
this.packetLength = packetLength;
this.dataLength = dataLength;
this.dataTypeVersion = dataTypeVersion;
this.sequenceNumber = sequenceNumber;
this.packetFlags = packetFlags;
this.dataType = dataType;
this.relativeTimeCounter = relativeTimeCounter;
this.filePosition = filePosition;
}
internal static PacketHeader Parse(byte[] data, int index, long filePosition)
{
if (index + 24 > data.Length)
{
throw new ArgumentException("Packet header must be 24 bytes long; not enough data");
}
ushort syncPattern = BitConverter.ToUInt16(data, index + 0);
if (syncPattern != 0xeb25)
{
throw new ArgumentException("Packet header must start with the sync pattern");
}
ushort channelId = BitConverter.ToUInt16(data, index + 2);
uint packetLength = BitConverter.ToUInt32(data, index + 4);
uint dataLength = BitConverter.ToUInt32(data, index + 8);
byte dataTypeVersion = data[index + 12];
byte sequenceNumber = data[index + 13];
byte packetFlags = data[index + 14];
byte dataType = data[index + 15];
ulong relativeTimeCounter =
(ulong)BitConverter.ToUInt32(data, index + 16) +
((ulong)BitConverter.ToUInt16(data, index + 20)) << 32;
return new PacketHeader(channelId, packetLength, dataLength, dataTypeVersion, sequenceNumber,
packetFlags, dataType, relativeTimeCounter, filePosition);
}
internal static bool CheckPacketHeaderChecksum(byte[] data, int index)
{
if (index + 24 > data.Length)
{
throw new ArgumentException("Packet header must is 24 bytes long; not enough data");
}
ushort computed = 0;
for (int i = 0; i < 11; i++)
{
computed += BitConverter.ToUInt16(data, index + i * 2);
}
return computed == BitConverter.ToUInt16(data, index + 22);
}
}
}
PacketScanner.cs:
using System;
using System.Diagnostics;
using System.IO;
namespace Chapter10Reader
{
public sealed class PacketScanner : IDisposable
{
private const int BufferSize = 1024 * 128;
private long bufferStart;
private long bufferEnd;
private long logicalPosition;
private readonly long fileLength;
private readonly FileStream stream;
private readonly byte[] buffer = new byte[BufferSize];
private PacketScanner(FileStream stream)
{
this.stream = stream;
this.fileLength = stream.Length;
}
public void MoveToEnd()
{
logicalPosition = fileLength;
bufferStart = -1;
bufferEnd = -1;
}
public void MoveToBeforeStart()
{
logicalPosition = -1;
bufferStart = -1;
bufferEnd = -1;
}
private byte this[long position]
{
get
{
if (position < bufferStart || position >= bufferEnd)
{
FillBuffer(position);
}
return buffer[position - bufferStart];
}
}
private void FillBuffer(long position)
{
long newStart;
if (position > bufferStart)
{
newStart = position;
}
else
{
newStart = Math.Max(0, position - buffer.Length + 2);
}
int bytesRead;
int index = 0;
stream.Position = newStart;
while ((bytesRead = stream.Read(buffer, index, buffer.Length - index)) > 0)
{
index += bytesRead;
}
bufferStart = newStart;
bufferEnd = bufferStart + index;
}
private void FillBuffer(long start, long end)
{
if (end - start > buffer.Length)
{
throw new ArgumentException("Buffer not big enough!");
}
if (end > fileLength)
{
throw new ArgumentException("Beyond end of file");
}
if (start >= bufferStart && end < bufferEnd)
{
return;
}
if (start >= bufferStart)
{
int shiftAmount = (int) (end - bufferEnd);
Buffer.BlockCopy(buffer, shiftAmount, buffer, 0, (int) (bufferEnd - bufferStart - shiftAmount));
stream.Position = bufferEnd;
int bytesRead;
int index = (int)(bufferEnd - bufferStart - shiftAmount);
while ((bytesRead = stream.Read(buffer, index, buffer.Length - index)) > 0)
{
index += bytesRead;
}
bufferStart += shiftAmount;
bufferEnd = bufferStart + index;
return;
}
bufferStart = -1;
bufferEnd = -1;
FillBuffer(start);
}
public PacketHeader NextHeader()
{
for (long tryPosition = logicalPosition + 1; tryPosition < fileLength - 23; tryPosition++)
{
if (this[tryPosition] == 0x25 && this[tryPosition + 1] == 0xEB)
{
FillBuffer(tryPosition, tryPosition + 24);
int bufferPosition = (int) (tryPosition - bufferStart);
if (PacketHeader.CheckPacketHeaderChecksum(buffer, bufferPosition))
{
logicalPosition = tryPosition;
return PacketHeader.Parse(buffer, bufferPosition, tryPosition);
}
}
}
logicalPosition = fileLength;
return null;
}
public PacketHeader PreviousHeader()
{
for (long tryPosition = logicalPosition - 1; tryPosition >= 0; tryPosition--)
{
if (this[tryPosition + 1] == 0xEB && this[tryPosition] == 0x25)
{
FillBuffer(tryPosition, tryPosition + 24);
int bufferPosition = (int)(tryPosition - bufferStart);
if (PacketHeader.CheckPacketHeaderChecksum(buffer, bufferPosition))
{
logicalPosition = tryPosition;
return PacketHeader.Parse(buffer, bufferPosition, tryPosition);
}
}
}
logicalPosition = -1;
return null;
}
public static PacketScanner OpenFile(string filename)
{
return new PacketScanner(File.OpenRead(filename));
}
public void Dispose()
{
stream.Dispose();
}
}
}
测试用Program.cs文件:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Chapter10Reader
{
class Program
{
static void Main(string[] args)
{
string filename = "test.ch10";
Console.WriteLine("Forwards:");
List<long> positionsForward = new List<long>();
using (PacketScanner scanner = PacketScanner.OpenFile(filename))
{
scanner.MoveToBeforeStart();
PacketHeader header;
while ((header = scanner.NextHeader()) != null)
{
Console.WriteLine("Found header at {0}", header.FilePosition);
positionsForward.Add(header.FilePosition);
}
}
Console.WriteLine();
Console.WriteLine("Backwards:");
List<long> positionsBackward = new List<long>();
using (PacketScanner scanner = PacketScanner.OpenFile(filename))
{
scanner.MoveToEnd();
PacketHeader header;
while ((header = scanner.PreviousHeader()) != null)
{
positionsBackward.Add(header.FilePosition);
}
}
positionsBackward.Reverse();
foreach (var position in positionsBackward)
{
Console.WriteLine("Found header at {0}", position);
}
Console.WriteLine("Same? {0}", positionsForward.SequenceEqual(positionsBackward));
}
}
}
EB25
,则回溯一个字节。 - L.BFastForwardReverseFileStream
。只是需要注意的是,使用Memory Mapped file
并不意味着你要将整个文件内容加载到内存中。 - L.B