C# ↔ C++의 이기종 통신을 위한 마샬링에 대해서 살펴보겠습니다.
여기서 언급되는 컨셉은 JAVA ↔ C# , JAVA ↔ C++ 등으로 확장될수도 있습니다.
실제로 프로젝트에 활용하기 위해 작성하였으며, 아직도 이러한 방식으로 운영되고 있습니다.
C# Base Class
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; //관리되지않는 코드 (포인터 사용및 구조체를 시퀜셜하게 만들기위해) using System.Web.Script.Serialization; [StructLayout(LayoutKind.Sequential, Pack = 1)] // #pragma pack(1) 과 동일한 효과.. public class ConvertPacket { // 받은 패킷 바이트를 구조체화함 public void SetBuffer(byte[] pbyte) { unsafe // c#에서는 포인터연산을 통한 메모리복사를 안전하지 않는 코드로 판단, unsafe블록안에 감싸줘야함 { try { fixed (byte* fixed_buffer = pbyte) { Marshal.PtrToStructure((IntPtr)fixed_buffer, this); } } catch (Exception e) { LogUtil.Instance.writeLog(LOGLEVELTYPE.WARN, "SYSTEM", "LSSOCK", string.Format("ParserError {0} - {1}", e.Message, e.Source) , "ConvertPacket::SetBuffer"); } } } // 보낼패킷 구조체를 byte에 복사함 public void PutBuffer(byte[] pbyte) { int mycount = Marshal.SizeOf(this); unsafe { fixed (byte* fixed_buffer = pbyte) { Marshal.StructureToPtr(this, (IntPtr)fixed_buffer, true); for (int i = 0; i < mycount; i++) { pbyte[i] = fixed_buffer[i]; } } } } public int GetSize() { return Marshal.SizeOf(this); } public byte[] GetBuffer() { byte[] resultObj = new byte[ GetSize() ]; PutBuffer( resultObj ); return resultObj; } public override string ToString() //이것은 디버깅을위해 패킷을 까보는 활동의 코드입니다. { return new JavaScriptSerializer().Serialize(this); } }
사용예 C++로 데이터전송
using System.Runtime.InteropServices; //관리되지않는 코드 (포인터 사용및 구조체를 시퀜셜하게 만들기위해) using System.Text; //문자열을 byte[]배열에 넣기 위해 using System.Net.Sockets; //소켓 통신을위해 using System.Diagnostics; //디버깅을 위해 // 클래스(구조체) 샘플. [StructLayout(LayoutKind.Sequential, Pack = 1)] public class CK_KEEPALIVE : ConvertPacket { public char cRes; public int nRes; public CK_KEEPALIVE() { } } public class Sample { public void Text() { CK_KEEPALIVE tmpPacket = new CK_KEEPALIVE(); tmpPacket.nRes = 1234; //원하는 값을 채운다. //소켓연결 TcpClient tcpClient = new TcpClient(); tcpClient.Connect("localhost", 8296); NetworkStream networkStream = tcpClient.GetStream(); //보내기 networkStream.Write(tmpPacket.GetBuffer(), 0, tmpPacket.GetSize() ); //받기 CK_KEEPALIVE tmpPacket2 = new CK_KEEPALIVE(); byte[] buffer = new byte[ tmpPacket.GetSize() ]; networkStream.Read(buffer, 0, tmpPacket.GetSize() ); tmpPacket.SetBuffer(buffer); //받은 바이너리 패킷을 구조체에 적용함 networkStream.Close(); tcpClient.Close(); } }