구조체 마샬링을 통해 실제 데이터를 송수신 해본다.
네트워크로 전송될 데이터 구조체
- public enum EPkEnum
- {
- ePkFirst=10000,
- ePkCnt,
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public class CPkBase
{
// Constructor
public CPkBase(EPkEnum pEnum)
{
__mPk = pEnum;
- }
- // Property
- public EPkEnum Pk
- { get { return(__mPk); } }
- // Field
- private EPkEnum __mPk;
- }
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public class CPkFirst
{
// Constructor
public CPkFirst() : base(EPkEnum.ePkFirst)
{
// Nothing
}
// Method
public int Set(int pSomeData1, string pSomeData2)
{
__mSomeData1 = pSomeData1;
__mSomeData2 = pSomeData2;
return(0);
}
// 데이터 확인용 override
public override string ToString()
{
return(string.Format(“SomeData1 = {0}\nSomeData2 = {1}”, __mSomeData1, __mSomeData2);
}
// Property
public int SomeData1;
{ get { return(__mSomeData1); } }
public string SomeData2;
{ get { return(__mSomeData2); } }
// Field
private int __mSomeData1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=20)]
private string __mSomeData2;
- }
. CPkBase 는 packet class 의 base 클래스이며, 필드로는 해당 packet 의 속성을 나타내는 enum 값을 가지고 있다.
. CPkFirst 는 CPkBase 를 상속받은 실제 네트워크를 통해 전송될 데이터이며 여러가지 필드를 가지고 있다.
(위에 코드에 사용된 Attribute 에 대한 설명은 구조체 마샬링 포스팅을 확인바랍니다.)
실제로 전송해 보자
static void Main(string[] pArg)
{
CPkFirst aPk = new CPkFirst(); // Packet 데이터를 설정해야 겠죠.
byte[] aBuf= new byte[1024]; // 넉넉하게 잡았습니다.
aPk.Set(10, “테스트”);
- // 우선 전송할 Packet 을 먼저 생성하겠습니다. (class 를 byte 스트림으로 변환하는 과정입니다.)
- unsafe // 안전하지 않는 코드로 갑니다.
- {
- fixed(byte* aFixedBuf = aBuf)
- {
- Marshal.StructureToPtr(aPk, (IntPtr)aFixedBuf, false);
- }
- }
- // Accpet 용 소켓을 생성하고 Bind 및 Listen 을 시작합니다.
- Socket aAcceptSok = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- IPEndPoint aIPEndPoint = new IPEndPoint(IPAddress.Any, 12345);
- aAcceptSok.Bind(aIPEndPoint);
- aAcceptSok.Listen(int.MaxValue);
- // Accept 를 시작합니다.
- Socket aClient = aAccept.Accept();
- int aSendLng = aClient.Send(aBuf, Marshal.SizeOf(typeof(CPkFirst)), SocketFlags.None);
- Debug.Assert(Marshal.SizeOf(typeof(CPkFirst)) == aSendLng, ”Invalid!”); // 다 보내는 경우에 정상적인 테스트가 가능한다.
- int aRecvLng = aClient.Receive(aBuf, 0, Marshal.SizeOf(typeof(CPkFirst)), SocketFlags.None);
- Debug.Assert(Marshal.SizeOf(typeof(CPkFirst)) == aRecvLng, ”Invalid!”); // 다 받아야된다.
- CPkFirst aRecvFirst = new CPkFirst();
- unsafe
- {
- fixed(byte* aFixedBuf = aBuf)
- {
- Marshal.PtrToStructure((IntPtr)aFixedBuf, aRecvFirst);
- }
- }
- // 받은 데이터를 확인해보자
- Console.WriteLine(aRecvFirst.ToString());
- }
테스트한 코드가 기억이 잘안나 비슷하게 만들어봤는데 실제로 동작할지는 모르겠네요. ㄷㄷㄷ . 클라이언트는 받은거 그대로 돌려주면 되는 그런 코드로 이루어져있으면 테스트가 가능하겠습니다. 실제 테스트 코드는 받은 패킷을 정확하게 찾아서 해당 패킷을 처리하는 프록시져를 통해 패킷을 처리하게 까지 구성을 해봤는데. 귀차니즘으로 인해 포스팅할때는 그 기능이 빠진 아주 단순한 코드가 되어버렸습니다. 더불어 에러 처리 및 예외 처리도 전혀 되어있지 않네요. 여기서 보여주는건 네트워크로 구조체 통짜로 전송할때의 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)
이 글은 스프링노트에서 작성되었습니다.