이 글에서는 2004 년에 이진 스트림을 .NET 메모리 구조로 변환하는 데 사용할 수있는 옵션 중 일부에 대해 설명했습니다. 이전 블로그 사이트가 더 이상 존재하지 않아서 새 블로그에 게시했습니다.
를 사용하여 .NET 마샬링을 필요로 세 가지 옵션 C#에서
- 를 사용하여 C++ 스타일의 메모리 포인터를 복사 바이트를 관리되지 않는 메모리에 저장 한 다음 Marshal.PtrToStructure를 사용하여 데이터를 관리 힙으로 다시 마샬링하여 구조에 매핑합니다.
- BinaryReader를 사용하여 바이트 스트림을 수동으로 읽고 구조에 데이터를 압축합니다. 개인적으로 이것은 내가 선호하는 옵션이었습니다.
옵션을 고려할 때 바이트 순서가 사용자에게 미치는 영향도 고려해야합니다.
예를 들어 게시 당시 Raw TCP 패킷으로 작업 중이기 때문에 IP 헤더를 예로 사용합니다.
이진 데이터가 매핑되는 .NET 구조를 정의해야합니다. 예를 들어, IP 헤더는 다음과 같습니다. StructLayout 속성은 처음 두 옵션이 필요합니다, 물론 서버에서 연재되고있는 구조에 대한 적절한 포장을 설정해야 함을
는 [StructLayout(LayoutKind.Sequential, Pack = 1)]
struct IpHeader
{
public byte VerLen;
public byte TOS;
public short TotalLength;
public short ID;
public short Offset;
public byte TTL;
public byte Protocol;
public short Checksum;
public int SrcAddr;
public int DestAddr;
}
참고.
C/C++에서 C/C++ 구조에 매핑되는 데이터 바이트가 들어있는 메모리 블록에 대한 포인터가 있으면 다음 코드를 사용하여 데이터 블록을 구조체 조각으로 볼 수 있습니다. 메모리, 여기서 패킷은 메모리에 대한 바이트 *입니다.
IpHeader *pHeader = (IpHeader*)packet;
동일한 작업을 수행하는 것은 C#/unsafe 옵션을 사용하고 위에 정의 된 구조체는 다음 코드를 사용합니다.
IpHeader iphdr;
unsafe
{
fixed (byte *pData = packet)
{
iphdr = *(IpHeader*)pData;
}
}
//Use iphdr...
마샬링 옵션은 다음
IntPtr pIP = Marshal.AllocHGlobal(len);
Marshal.Copy(packet, 0, pIP, len);
iphdr = (IpHeader)Marshal.PtrToStructure(pIP, typeof(IpHeader));
Marshal.FreeHGlobal(pIP);
과 같을 것이다 그리고 마지막으로 당신은 완전히 관리되는 코드에서이 작업을 수행 할 BinaryReader를 사용할 수 있습니다.
MemoryStream stm = new MemoryStream(packet, 0, len);
BinaryReader rdr = new BinaryReader(stm);
iphdr.VerLen = rdr.ReadByte();
iphdr.TOS = rdr.ReadByte();
iphdr.TotalLength = rdr.ReadInt16();
iphdr.ID = rdr.ReadInt16();
iphdr.Offset = rdr.ReadInt16();
iphdr.TTL = rdr.ReadByte();
iphdr.Protocol = rdr.ReadByte();
iphdr.Checksum = rdr.ReadInt16();
iphdr.SrcAddr = rdr.ReadInt32();
iphdr.DestAddr = rdr.ReadInt32();
앞서 언급했듯이 바이트 순서를 고려해야 할 수도 있습니다.예를 들어, IpHeader가 ReadInt16에 의해 가정 된 것과 동일한 바이트 순서를 사용하지 않기 때문에 위의 코드는 정확하지 않습니다. ReadInt32 등 위의 솔루션으로 문제를 해결하는 것은 IPAddress.NetworkToHostOrder를 사용하는 것만 큼 간단합니다.
iphdr.VerLen = rdr.ReadByte();
iphdr.TOS = rdr.ReadByte();
iphdr.TotalLength = IPAddress.NetworkToHostOrder(rdr.ReadInt16());
iphdr.ID = IPAddress.NetworkToHostOrder(rdr.ReadInt16());
iphdr.Offset = IPAddress.NetworkToHostOrder(rdr.ReadInt16());
iphdr.TTL = rdr.ReadByte();
iphdr.Protocol = rdr.ReadByte();
iphdr.Checksum = IPAddress.NetworkToHostOrder(rdr.ReadInt16());
iphdr.SrcAddr = IPAddress.NetworkToHostOrder(rdr.ReadInt32());
iphdr.DestAddr = IPAddress.NetworkToHostOrder(rdr.ReadInt32());
감사합니다. 나는 가서 BinaryReader 방법을 시도했고, 왜 숫자가 잘못 나오는지에 대해 완전히 당혹스러워했다. 다른 관리되는 대안이없는 것 같아서 스트림에서 직접 읽고 클래스를 채울 것입니다. – cpf