소켓통신으로 객체를 마샬링하여 통째로 전달하여 보자.
우리가 소켓통신을 사용하다 보면 배열이나 구조체를 통째로
날리면 편하겠다 라는 생각을 많이 하게 된다.
먼저 우리가 알아두어야 할 개념이 있다.
MarshalAsAttribute의 설명
이 특성은 매개 변수, 필드 또는 반환 값에 적용될 수 있습니다.
각 데이터 형식에 기본 마샬링 동작이 있으므로 이 특성은 선택적입니다. 이 특성은 주어진 형식을 여러 형식으로 마샬링할 수 있는 경우에만 필요합니다. 예를 들어, 문자열은 LPStr, LPWStr, LPTStr 또는 BStr로 관리되지 않는 코드로 마샬링할 수 있습니다. 기본적으로 공용 언어 런타임에서는 문자열 매개 변수를 BStr로 COM 메서드로 마샬링합니다. MarshalAsAttribute 속성을 개별 필드나 매개 변수에 적용하여 특정 문자열이 BStr 대신 LPStr로 마샬링되도록 할 수 있습니다. 형식 라이브러리 내보내기(Tlbexp.exe)는 사용자의 마샬링 기본 설정을 공용 언어 런타임에 전달합니다.
일부 매개 변수 및 반환 값은 COM interop 또는 플랫폼 호출과 함께 사용할 경우 기본 마샬링 동작이 다릅니다. 기본적으로 런타임에서는 문자열 매개 변수 및 값 형식의 필드를 LPStr로 플랫폼 호출 메서드 또는 함수로 마샬링합니다. 자세한 내용은 기본 마샬링 동작을 참조하십시오.
대부분의 경우 이 특성은 다음 C# 시그니처와 같이 UnmanagedType 열거형을 사용하는 관리되지 않는 데이터의 형식을 쉽게 식별합니다.
void MyMethod([MarshalAs(LPStr)] String s);
일부 UnmanagedType 열거형 멤버에는 추가 정보가 필요합니다. 예를 들어, UnmanagedType 이 LPArray 일 때에는 추가 정보가 필요합니다. 이 특성을 배열에 사용하는 방법에 대한 자세한 내용은배열에 대한 기본 마샬링을 참조하십시오.
형식 라이브러리 가져오기(Tlbimp.exe)는 이 특성을 매개 변수, 필드 및 반환 값에도 적용하여 입력 형식 라이브러리의 데이터 형식이 해당 관리되는 데이터 형식의 기본 형식이 아님을 나타냅니다. Tlbimp.exe는 입력 형식 라이브러리에 지정된 형식에 상관없이 명확함을 기하기 위해 String 및 Object 형식에 항상 MarshalAsAttribute를 적용합니다.
예제를 보자.
[C#]
//Applied to a parameter.
public void M1 ([MarshalAs(UnmanagedType.LPWStr)]String msg);
//Applied to a field within a class.
class MsgText {
[MarshalAs(UnmanagedType.LPWStr)] Public String msg;
}
//Applied to a return value.
[return: MarshalAs(UnmanagedType.LPWStr)]
public String GetMessage();
이 마샬링의 개념이 정리가 되었으면 이제 관리되지 않는 메모리 블록의 데이터를
관리되는 개체로 마샬링하는 메서드를 정리 해보도록 하자.
Marshal.PtrToStructure 메서드사용 예제
UCOMITypeInfo typeInfo = ...;
IntPtr ptr = IntPtr.Zero;
typeInfo.GetTypeAttr(ref ptr);
TYPEATTR attr = (TYPEATTR)Marshal.PtrToStructure(ptr,
typeof(TYPEATTR));
실전 객체 전달 프로그래밍
using System.Runtime.InteropServices; //관리되지않는 코드 (포인터 사용및 구조체를 시퀜셜하게 만들기위해)
using System.Text;
using System.Net.Sockets;
using System.Diagnostics;
// 클래스(구조체) 샘플.
[StructLayout(LayoutKind.Sequential)]
public class TempPacket
{
public byte Command;
public byte Version;
public ushort Length;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=10)] public byte[] Data; //바이트 배열....
}
public class Sample
{
public void Text()
{
TempPacket tmpPacket = new TempPacket();
tmpPacket.Command = 0x10;
tmpPacket.Version = 0x01;
tmpPacket.Length = 10;
tmpPacket.Data = Encoding.ASCII.GetBytes("0123456789");
//객체를 보내보자.
byte[] buffer = new byte[Marshal.SizeOf(tmpPacket)];
unsafe
{
fixed(byte* fixed_buffer = buffer)
{
Marshal.StructureToPtr(tmpPacket, (IntPtr)fixed_buffer, false);
}
}
//소켓열고
TcpClient tcpClient = new TcpClient();
tcpClient.Connect("localhost", 8296);
NetworkStream networkStream = tcpClient.GetStream();
networkStream.Write(buffer, 0, Marshal.SizeOf(tmpPacket));
//객체받고
TempPacket tmpPacket2 = new TempPacket();
networkStream.Read(buffer, 0, Marshal.SizeOf(tmpPacket));
unsafe
{
fixed(byte* fixed_buffer = buffer)
{
Marshal.PtrToStructure((IntPtr)fixed_buffer, tmpPacket2);
}
}
string data;
data = Encoding.ASCII.GetString(tmpPacket2.Data, 0, 10);
Debug.WriteLine(data);
networkStream.Close();
tcpClient.Close();
}
}
작성자 : HOONS(박경훈) |