반응형

C++ Socket : 고정 구조체

알게된 배경

 기존 소켓 교재들 대부분이 C로 되어 있는것을 접하다가 C++로 옮기고 나서 소켓 프로그램을 작성할때에 구조체의 크기가 C와 같이 연속적인 형태로 메모리에 저장되지 않는다는 것을 알게 되었기에 기록으로 남긴다.


C Style Struct?

 이제는 고유 키워드가 되어 버렸다. C만 기본적으로 메모리에 직접적으로 올라가는 듯 하다. 혹은 라이브러리로 혹은 이후 표준으로 확장이 되는지 앞으로 확인이 필요할 듯하다.

 이유는 메모리에 사용자가 로드하는 대로 하지 않는 이유는 성능을 향상시키기 위해서 하는 작업이기 때문이다. 성능을 중요시한다면, 후에라도 지원이 될 듯 하지만.. C의 언어 철학은 프로그래머를 믿으라는 것이 모토이기 때문에 안될 가능성도 있다.


키워드

각 컴파일별로 선언하는 방식이 다르다. GCC 컴파일러 기준으로는 언더바로 확장된 키워드를 사용한다. __attribute__((__packed__))를  struct 와 이름 사이에 넣으면 된다.

GCC 예시)

// Header.hpp
struct __attribute__((__packed__)) SHeader
{
    char name[4];
    int32_t number;
    int32_t body_sz;
};



메모리 로드

 만약 키워드가 없을 경우에는 문자열과 숫자형을 구분하여 8바이트 단위로 메모리에 로드하게 된다. 


 예제 예시로 설명하자면, 편의상 8바이트 단위를 row라고 하면, row1에 name[4]가 로드되고 row2에 number, body_sz가 로드되어서 row가 2개이므로 총 16바이트를 차지하게 된다.

 따라서 __attribute__ 를 사용해서 실제 내부 총량이 메모리에 로드되는 것과 동일하게 하면, 12바이트가 된다(소켓 프로그램에서는 이렇게 되어야 한다).


이러한 이유로 일반적인 PC간의 소켓 통신을 할경우 헤더같이 struct로 자료형을 만들 경우 가능하면 8바이트로 딱떨어지는 형태가 되는 것이 성능저하가 적을 것이라 예측이 되는 요소이다(대부분 운영체제는 현시점에서 8바이트 형태로 최적화 되어 있는 듯 하다. 물론 이는 절대적이지 않다).


(MVC++(Microsoft Visual C++)컴파일러의 경우 다른 방법이 있는 것으로 알고 있는데, 정리할 때 메모부분을 분실해서 추후에 추가하기로 기약한다.)


여담

 C++98 이후 부터 성능을 올리기 위해서 도입이 된 것같다.

 이런식으로 언더바(_) 두개를 이용한 새로운 키워드로 기능을 확장을 하고 있는 추세이기 때문에 변수 선언시 언더바 사용을 자제할 것을 권장하고 있다. C#에서는 클래스 멤버변수를 선언시 언더바를 아직 사용하는 경향이 있지만, C++에서는 언더바 사용을 안하고 반만 헝가리식으로 소문자 m을 붙이는 경향도 있다. 어찌 되었든 권장이니 꼭 따를 필요는 없고 프로젝트 규약에 주의하자.


참고자료

스택오버 플로워


반응형
반응형

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