반응형

C# Socket : 고정 구조체 만들기

알게된 배경

 C++로 작성된 서버 혹은 PC와 통신을 하기 위해서는 일반적으로 고정된 크기의 Head와 고정 혹은 가변의 Body로 나누어서 통신을 하는 경우가 많다. C/C++에서는 Struct를 고정된 크기로 할당하는 것이 가능한 반면 C#에서는 별도의 코딩이 필요로 하다.

 이는 기본적으로 C#에서는 배열을 new 키워드를 통해서 정의 및 초기화를 했기때문에 struct에서는 사용이 불가능하다(반면 클래스에서는 사용가능하다).


C/C++의 고정 Struct

 그냥 배열로 선언을 해주면 된다. 아래의 SHead의 크기는 대략 40바이트가 된다. C에서는 그냥 선언해주면 되지만, C++의 경우에도 컴파일에 따라서 추가 선언을 해줘야 한다. C++에 대해서 추후 링크를 추가한다.

// head.hpp
struct SHead
{
    char name[8];
    unsigned char headType;
    char hostname[31];
};



C# unsafe

지금은 폐지된 방법이다. 초기에 불편하자 급조한 티가 난다. 참조문서는 지금도 가이드라인에 있다. 폐지된 만큼 가이드 라인에 맞게 작성해도 에러로 표시된다. 폐지된 방법은 아니고 컴파일 옵션에서 안전하지 않은 코드 컴파일 설정을 바꿔서 실행할 수 있다. C 스타일의 코딩을 자주 해야 한다면, 컴파일 옵션을 풀고 작성하는 것이 코드가 더욱 간결하다. 하지만, 키워드에서 알 수 있듯이 안전하지 않다. (그리고 마소에서 쓰지 않도록 하려고 일부러 쓰기 어렵게 만들어 놓은 방법이기도 하다)


 unsafe가 위험한 이유는 GC가 unsafe로 선언된 곳은 메모리 해제를 하지 않는다.

namespace sock
{
    public struct unsafe SHead
    {
        public Byte fixed name[8];
        public Byte headType;
        public Byte fixed hostname[31];
    }
}




C# Marshal

권장하는 방법이다. 시그니처를 이용해서 배열의 크기를 정해주는 방식이다. 사용하기 위해서는 using System.Runtime.InteropServices; 를 선언하거나 전부 적어주면 된다. pack 옵션 값은 메모리 처리 방식을 할때 단위를 1바이트로 하겠다는 의미이다.

 이러한 키워드를 추가 하지 않으면, 시스템마다 차이는 있지만, 기본적으로는 8바이트를 기준으로 처리를 한다.

namespace sock
{
    [Structlayout(LayoutKind.Sequential, pack=1)]
    public struct SHead
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public Byte[] name;
        public Byte headType;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)]
        public Byte[] hostname;
    }
}



 여기서 C#에서 취급하는 특징을 보면 메모리에 배열처럼 연속으로 올릴 경우를 안전하지 않은 방법으로 취급하기 때문에 여러 번거로움이 있다. 이렇게 번거롭게 만든 이유는 가능하면 사용하지 말라는 의도가 있다는 것으로 보인다. 그래도 소켓 프로그램 쪽에서는 어쩔 수 없이 사용해야 하는 경우가 있다.

잡설

 따지고 보면 C#에서 제공하는 두가지 방법들 모두 해당 자료가 안전하지 않다는 것을 명시해주는 것이다. 속도 같은 효율도 떨어진다는 건 덤.. 이러한 이유로 C로 프로그램을 작성할 경우 시스템 이해도와 잘하는 사람과 못 하는 사람간의 차이가 심하다.


참조자료

가이드 라인 unsafe

가이드 라인 unsafe 모드 컴파일

MASN문서 Marshal Array

MSDN문서 LayoutKind

블로그 :고정크기 사용방법




반응형

+ Recent posts