2010-04-16 2 views
2

C++로 작성된 서버 프로그램 용 클라이언트를 작성하고 있습니다. 일반적이지 않은 것처럼 모든 네트워킹 프로토콜은 패킷이 C++ 구조 (1 바이트 패킷 코드, 다음 패킷 유형별로 다른 배열)에 쉽게 memcopied 될 수있는 형식으로되어 있습니다.Object to Network 직렬화 - 기존 프로토콜 사용

C#에서 동일한 작업을 수행 할 수 있지만 데이터가 많은 경우 고정 길이 char 배열을 사용하여 문자열로 재생할 때 특히 편리합니다. 아니면 그냥 그것을 빨아해야하고 필요에 따라 유형을 변환해야합니까? ISerializable 인터페이스를 사용하여 보았지만 낮은 수준으로 보이지 않습니다.

답변

2

이 글에서는 2004 년에 이진 스트림을 .NET 메모리 구조로 변환하는 데 사용할 수있는 옵션 중 일부에 대해 설명했습니다. 이전 블로그 사이트가 더 이상 존재하지 않아서 새 블로그에 게시했습니다.

http://taylorza.blogspot.com/2010/04/archive-structure-from-binary-data.html

는 기본적으로 메모리가 관리되지 않는 블록을 할당하려면/안전 스위치

  • 를 사용하여 .NET 마샬링을 필요로 세 가지 옵션

    C#에서
    1. 를 사용하여 C++ 스타일의 메모리 포인터를 복사 바이트를 관리되지 않는 메모리에 저장 한 다음 Marshal.PtrToStructure를 사용하여 데이터를 관리 힙으로 다시 마샬링하여 구조에 매핑합니다.
    2. 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()); 
    
  • +0

    감사합니다. 나는 가서 BinaryReader 방법을 시도했고, 왜 숫자가 잘못 나오는지에 대해 완전히 당혹스러워했다. 다른 관리되는 대안이없는 것 같아서 스트림에서 직접 읽고 클래스를 채울 것입니다. – cpf