구조체 마샬링을 통해 실제 데이터를 송수신 해본다.


네트워크로 전송될 데이터 구조체



  1. public   enum   EPkEnum
  2. {
  3. ePkFirst=10000,
  4. ePkCnt,

  5. }


    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]



  6. public class  CPkBase



  7. {



  8. // Constructor



  9. public   CPkBase(EPkEnum pEnum)



  10. {



  11. __mPk   = pEnum;


  12. }
  13. // Property
  14. public   EPkEnum   Pk
  15. {   get { return(__mPk); }    }
  16. // Field
  17. private   EPkEnum      __mPk;
  18. }

  19. [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]


    public class   CPkFirst



  20. {



  21. // Constructor



  22. public   CPkFirst() : base(EPkEnum.ePkFirst)



  23. {



  24. // Nothing



  25. }



  26. // Method



  27. public   int   Set(int pSomeData1, string pSomeData2)


    {


    __mSomeData1   = pSomeData1;


    __mSomeData2   = pSomeData2;


    return(0);


    }



  28. // 데이터 확인용 override



  29. public   override   string   ToString()



  30. {


    return(string.Format(“SomeData1 = {0}\nSomeData2 = {1}”, __mSomeData1, __mSomeData2);



  31. }




  32. // Property



  33. public   int      SomeData1;



  34. {   get { return(__mSomeData1); }   }



  35. public   string   SomeData2;



  36. {   get { return(__mSomeData2); }   }



  37. // Field



  38. private   int      __mSomeData1;



  39. [MarshalAs(UnmanagedType.ByValTStr, SizeConst=20)]



  40. private   string   __mSomeData2;


  41. }

. CPkBase 는 packet class 의 base 클래스이며, 필드로는 해당 packet 의 속성을 나타내는 enum 값을 가지고 있다.


. CPkFirst 는 CPkBase 를 상속받은 실제 네트워크를 통해 전송될 데이터이며 여러가지 필드를 가지고 있다.


(위에 코드에 사용된 Attribute 에 대한 설명은 구조체 마샬링 포스팅을 확인바랍니다.)


실제로 전송해 보자



  1. static   void   Main(string[] pArg)



  2. {



  3. CPkFirst     aPk = new CPkFirst();   // Packet 데이터를 설정해야 겠죠.



  4. byte[]       aBuf= new byte[1024];   // 넉넉하게 잡았습니다.



  5. aPk.Set(10, “테스트”);



  6. // 우선 전송할 Packet 을 먼저 생성하겠습니다. (class 를 byte 스트림으로 변환하는 과정입니다.)
  7. unsafe   // 안전하지 않는 코드로 갑니다.
  8. {
  9. fixed(byte* aFixedBuf = aBuf)
  10. {
  11. Marshal.StructureToPtr(aPk, (IntPtr)aFixedBuf, false);
  12. }
  13. }
  14. // Accpet 용 소켓을 생성하고 Bind 및 Listen 을 시작합니다.
  15. Socket   aAcceptSok      = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  16. IPEndPoint   aIPEndPoint = new IPEndPoint(IPAddress.Any, 12345);
  17. aAcceptSok.Bind(aIPEndPoint);
  18. aAcceptSok.Listen(int.MaxValue);
  19. // Accept 를 시작합니다.
  20. Socket   aClient  = aAccept.Accept();
  21. int   aSendLng    = aClient.Send(aBuf, Marshal.SizeOf(typeof(CPkFirst)), SocketFlags.None);
  22. Debug.Assert(Marshal.SizeOf(typeof(CPkFirst)) == aSendLng,      ”Invalid!”);   // 다 보내는 경우에 정상적인 테스트가 가능한다.
  23. int   aRecvLng    = aClient.Receive(aBuf, 0, Marshal.SizeOf(typeof(CPkFirst)), SocketFlags.None);
  24. Debug.Assert(Marshal.SizeOf(typeof(CPkFirst)) == aRecvLng,      ”Invalid!”);   // 다 받아야된다.
  25. CPkFirst   aRecvFirst   = new CPkFirst();
  26. unsafe
  27. {
  28. fixed(byte* aFixedBuf = aBuf)
  29. {
  30. Marshal.PtrToStructure((IntPtr)aFixedBuf, aRecvFirst);
  31. }
  32. }
  33. // 받은 데이터를 확인해보자
  34. Console.WriteLine(aRecvFirst.ToString());
  35. }

테스트한 코드가 기억이 잘안나 비슷하게 만들어봤는데 실제로 동작할지는 모르겠네요. ㄷㄷㄷ . 클라이언트는 받은거 그대로 돌려주면 되는 그런 코드로 이루어져있으면 테스트가 가능하겠습니다.  실제 테스트 코드는 받은 패킷을 정확하게 찾아서 해당 패킷을 처리하는 프록시져를 통해 패킷을 처리하게 까지 구성을 해봤는데. 귀차니즘으로 인해 포스팅할때는 그 기능이 빠진 아주 단순한 코드가 되어버렸습니다. 더불어 에러 처리 및 예외 처리도 전혀 되어있지 않네요. 여기서 보여주는건 네트워크로 구조체 통짜로 전송할때의 Attribute 설정법 및 실제 마샬링을 하는 부분을 보여주기 위해서 해봤습니다. 도움이 되실분이 있으려나? ㄷㄷㄷ


 실제로 마샬링에 사용된 두가지의 함수의 기능은 모든 기능을 설명하기가 머해서 MSDN 한글 페이지를 링크 시켜드리겠습니다. (선무당이 사람잡는거 보단 좋죠? ^^)


. Marshal.StructureToPtr (http://msdn.microsoft.com/ko-kr/library/system.runtime.interopservices.marshal.structuretoptr.aspx)


. Marshal.PtrToStructure (http://msdn.microsoft.com/ko-kr/library/system.runtime.interopservices.marshal.ptrtostructure.aspx)


이 글은 스프링노트에서 작성되었습니다.

Post to Twitter