Creative Commons License

Microsoft .NET

닷넷!시작하기
닷넷! Ver 2.0~
닷넷!스킬업
웹개발
윈폼개발
실용모듈개발
Tip & Tech
하루 한 문법

Microsoft .NET 개발자들을 위한 공간입니다. 기초강의에서 부터 고급 기술 정보 및 팁등을 다루도록 하겠습니다.

.

닷넷!스킬업

닷넷 기술을 조금 더 깊이 다루고자 합니다. 특정 주제를 정하지 않고 이슈 발생 시 마다 체계적으로 정리하여 공유하겠습니다. 이전 자료를 옮겨온 곳이기도 합니다.

소켓 통신 시 구조체 사용하기

작성자 : 박종명
최초 작성일 : 2009-07-28 (화요일)
최종 수정일 : 2009-07-28 (화요일)
조회 수 : 11629

이번에는 소켓 통신 시 자주 사용되는 구조체를 주고 받는 것에 대해 질/답 형태의 글을 기록합니다
이전에 윈폼카페의 질문에 대한 답변입니다 (2008.01.03일 자료,  http://cafe.naver.com/winform/49)
------------------------------------------------------------------------------------------------------------------------

질문>>>>>

제목도 뭐라고 써야할지 좀 난감 ㅋ
요즘 C#으로 네떡좀 만져볼라는데 상당히 거시기합니다.

C/C++에서 다음과 같은 내용이 있습니다.

typedef struct Data{
        int a;
        char b;
        char str[30];
};

Data data;
...
char buffer[256];

memcpy(buffer, &data, sizeof(data));

이렇게 마지막에 memcpy를 해주면 구조체가 갖고있는 메모리를 그대로 복사해올 수 있죠.
혹은

void OnReceivePacket(char* packet){
    Data* recvData = (Data*)packet;
}

이 처럼 형 변환을 통해 간단히 Data 구조체를 얻어낼 수도 있구요.

그런데 아직까지 제가 찾아본 결과로는...
C#에서는 하나씩 변환해서 넣어주어야 하는 듯 합니다.

    public class Packet_CS_Hello {
        public Packet_CS_Hello(byte[] from)
        {
            size = BitConverter.ToUInt16(from, 0);
            id = BitConverter.ToUInt16(from, 2);

            data = BitConverter.ToUInt32(from, 4);
        }

        public UInt32 data;
        public ushort size;
        public ushort id;
    }

이 방법 말고 좀 더 깔끔한 (unsafe 말구 ^^) 방법은 없을까요?


답변 >>>>>

소켓 통신 시 구조체를 많이 주고 받기는 합니다
그 방법에 대한 질문 같은데요.....

샘플을 가지고 이야기 해 보겠습니다

다음과 같은 구조체를 정의 합니다

 [Serializable]
 [StructLayout(LayoutKind.Sequential)]
 struct Data
 {
      [MarshalAs(UnmanagedType.U2, SizeConst = 2)]
      public ushort size;

      [MarshalAs(UnmanagedType.U2, SizeConst = 2)]
       public ushort id;

       [MarshalAs(UnmanagedType.U4, SizeConst = 4)]
       public UInt32 data;
  };

이 구조체를 바이트 배열로 변환 하여 네트워크 저 넘어로 전달하고 전달받은 곳에서는 다시 구조체로 변환하여
사용하고자 합니다

일단 동군님처럼 BitConvert 를 이용해서 하나하나 끊어서 값을 찾아 낼 수 도 있습니다

 ushort size = BitConverter.ToUInt16(bytesForWrite, 0);
 ushort id = BitConverter.ToUInt16(bytesForWrite, 2);
 UInt32 data = BitConverter.ToUInt32(bytesForWrite, 4);

 Data receiverData1;
 receiverData1.size = size;
 receiverData1.id = id;
 receiverData1.data = data;

string result1 = String.Format("Size:{0},ID:{1},Data:{2}", receiverData1.size, receiverData1.id, receiverData1.data);
Console.WriteLine(result1);

물론 이 방법은 구조체 멤버들의 정확한 크기에 맞는 적절한 Converting 을 해 주어야 하는 불편함이 있습니다
또한 구조체 멤버의 종류가 다양하면 캐안습(?) 일 수 도 있습니다 ㅋㅋ

그 다음으로는 BinaryFormatter 를 이용할 수 도 있습니다
NetworkStream 에 구조체를 Serialize 해서 수신측에서는 다시 Deserialize 하여 구조체를 얻는 방법입니다
즉, 마샬링을 이용하는 방법입니다

 BinaryFormatter formatter = new BinaryFormatter();
 Data receiverData = (Data) formatter.Deserialize(networkStream);

물론 unsafe 를 이용한 방법도 가능합니다

Data receiveData;
unsafe
{
    fixed (byte* ptr = bytes)
    {
        receiveData= (Data )Marshal.PtrToStructure((IntPtr)ptr, typeof(Data ));
     }
}            
return receiveData; 

만일 Data가 구조체가 아니라 클래스라면 아래와 같이 해도 됩니다
Data receiveData;
 unsafe
 {
      fixed (byte* ptr = bytesForWrite)
      {
           Marshal.PtrToStructure((IntPtr)ptr, (Data)receiveData);
       }
  }

여기서 제시된 샘플 프로젝트를 첨부 하였습니다. 다운로드 하여 실행 해 보신 후 참고하시길 바랍니다

그리고 마지막으로 덧붙이자면,닷넷 환경에서 소켓을 굳이 고집할 필요는 없습니다
리모팅이나 닷넷 3.0 의 WCF 와 같은 닷넷 환경에 진보된 형태의 분산어플리케이션 구축도 고려해 볼 만 합니다
그러면 이와 같은 문제(?) 상황은 덜 하리라 보입니다

그럼.. 즐 ~ 프 하삼요~~

이름
비밀번호
홈페이지
VW <- 왼쪽의 문자를 오른쪽 박스에 똑같이 입력해 주세요