'공부합시다'에 해당되는 글 70건

  1. 2009.07.21 [펌] 함수 포인터 및 클래스 멤버함수의 함수포인터화
  2. 2009.07.21 함수 const
  3. 2009.07.21 인라인(inline) 함수
  4. 2009.07.21 클래스 내 static 함수
  5. 2009.07.21 클래스내 static 변수 초기화 방법
  6. 2009.07.21 VFW 영상 처리 함수
  7. 2009.07.15 비트맵 헤더 분석
  8. 2009.07.15 비트맵 파일의 구조
  9. 2009.07.09 [소켓프로그래밍] WSAAsyncSelect
  10. 2009.07.08 [소켓프로그래밍] setsockopt
2009. 7. 21. 12:04

[펌] 함수 포인터 및 클래스 멤버함수의 함수포인터화

< 함수 포인터 >

먼저 이 글은 포인터에 대한 이해를 필요로 한다.

포인터에 대한 기본지식이 있다고 가정하고 글을 쓰도록 하겠다.


int GetAreaEx( int x, int y )
{
    return x * y;
}


우선 이런 간단한 함수가 있다. 우리는 이 함수를 호출하기 위해 명시적으로

GetAreaEx( x, y );

이런식으로 기술해야 한다.

하지만 예를 들어 GetArea2, GetArea3, ..., GetAreaN 이런식으로 비슷한 함수가 존재하고

이를 상황에따라 다르게 호출해야 한다면 이 방식으로는 관리도 어려울 뿐더러 효율성도 떨어지고 코드량도 많이질 것이다.

또한 외부(스크립트 등)에서 어떤 특정한 함수를 호출하려 할때도 방법이 묘연할 것이다.


int (*GetArea)( int, int );

이 선언은 무엇일까?

언뜻보기에는 함수를 선언하는 것 같다.

이 선언은 함수에 대한 포인터를 선언한 것이다.

변수의 주소를 담는 포인터와 마찬가지로 함수포인터는 함수의 주소를 담는다.

GetArea = GetAreaEx; // 함수포인터 GetArea에 GetAreaEx()의 주소를 담는다
int nArea = (*GetArea)( x, y ); // (*GetArea)( x, y ); 로 GetAreaEx()함수를 호출하고 리턴받은 값을 nArea에 대입


이런식으로 GetAreaEx를 호출할 수 있다.

유의할점은 *GetArea를 꼭 ()로 감싸주어야 한다는 사실이다.

빼먹으면 컴파일러가 함수포인터를 통한 호출로 인식하지 못한다.


int (*GetArea[])( int, int ) = { GetAreaEx, GetArea2, GetArea3, ..., GetAreaN };

이것은 함수포인터 배열을 정적으로 선언한 것이다. 이렇게 배열로 기능이 비슷한 함수들을 묶어놓았다.

void CallFunc( int nState, int x, y )
{
    int nResult = (*GetArea[nState])( x, y );
}


그리고 그 함수들을 상황에 맞게 호출한다.

만약 함수포인터를 쓰지 않는다면

void CallFunc( int nState, int x, int y )
{
    int nResult;
    switch( nState )
    {
         case STATE_EX:
              nResult = GetAreaEx( x, y );
         break;
         case STATE_2:
              nResult = GetArea2( x, y );
         break;
         case STATE_3:
              nResult = GetArea3( x, y );
         break;
    }
}

위와 같이 기술해야 할 것이다.

두 방식의 차이점과 함수포인터의 이점을 알 수 있겠는가

그렇다면 함수포인터 배열을 동적으로 할당하는 방법은 없을까?

다음과 같은 방법으로 할당할 수 있다.

int (**GetArea)( int, int ); // 함수포인터의 포인터
GetArea = new (int (*[N])( int, int )); // N은 배열의 크기


그리고 다음과 같이 사용하면 된다.

GetArea[0] = GetAreaEx;
GetArea[1] = GetArea2;
GetArea[2] = GetArea3;
...

int nResult = (*GetArea[nState])( x, y );


물론 사용후 delete [] GetArea; 해서 해제하는것을 잊으면 안된다.



< 클래스 멤버함수의 함수포인터화 >

함수포인터는 함수의 주소값을 담는다고 했다.

그렇다면 클래스 멤버함수의 주소값도 단순히 함수포인터에 담아서 호출할 수 있지 않을까?

int (*func)();
func = CFunc::GetArea;


하지만 이 방법은 GetArea()멤버함수가 static으로 선언되었을 때만 가능하다.

static으로 선언되지 않은 멤버함수(멤버변수를 건들여야 하는 멤버함수)를 이 방법으로 담으려 한다면 컴파일 에러가 뜰 것이다.

여기에 다음과 같은 해결방법이 있다.

첫번째 방법은

class CFunc
{
public:
    static int GetArea( CFunc * cls, int x, int y );
};


위와 같이 선언하고 호출할때 해당 인트턴스의 포인터를 넘겨줘서

int GetArea( CFunc * cls, int x, int y )
{
    int a = cls->GetZ();
}


이런식으로 멤버변수를 읽거나 쓸수 있겠지만 이 방식으로는 한계가 있다.

Get, Set 같은 public 외부함수로 억세스하지 않으면 private나 protected안에 선언되어 있는

멤버변수는 건드릴 수 없다.

두번째는 멤버함수의 소속을 명시화하는 방법이다.

int (CFunc::*func)( int, int );
func = CFunc::GetArea;
CFunc A;
(A.*func)( x, y );


위와 같은 방법으로 해결가능하다. 물론 호출할 인스턴스가 명확해야 한다.

세번째는 클래스 안에 함수포인터를 멤버변수로 두고 별도의 함수포인터를 컨트롤하는 멤버함수를 만드는 방법이 있다.

이 방법이 멤버함수 관리가 가장 쉬우며 효율적이다.

class CFunc
{
public:
    int (CFunc::*pFunc)( int, int );
    int GetArea( int x, int y );
    void CallFunc( void ) { (this->*pFunc)( x, y ); } // CallFunc 함수호출시 자체 오버헤드를 줄이기 위해 inline
    CFunc();
    ~CFunc() {}
};

CFunc::CFunc()
{
    pFunc = GetArea;
}
int CFunc::GetArea( int x, int y )
{
    return x * y;
}

위와 같다면 CallFunc(); 로 GetArea 호출이 가능해진다.

지금은 단순히 한개의 멤버함수 호출만 할뿐 의미가 없다. 이제 실제 효율적으로 쓰이게 배열을 써보자.

class CFunc
{
public:
    int (CFunc::**pFunc)( int, int );
    int GetArea( int x, int y );
    void CallFunc( int nState, int x, int y ) { (this->*pFunc[nState])( x, y ); }
    CFunc();
    ~CFunc();
};

CFunc::CFunc()
{
    // init
    pFunc = new (int (CFunc::*[10])( int, int )); // 동적할당, 10에는 원하는 멤버함수 갯수만큼
    // 0번은 남겨둔다.

    pFunc[1] = GetArea;
    pFunc[2] = GetAreaEx;
    pFunc[3] = GetArea2;
    pFunc[4] = GetArea3;
    ...
    pFunc[9] = GetArea9;
}
CFunc::~CFunc()
{
    delete [] pFunc; // 해제
}
int CFunc::GetArea( int x, int y )
{
    return x * y;
}

자, 이제 함수하나의 호출로 상황에따라 여러 멤버함수를 호출할 수 있는 기반이 마련되었다.

CFunc A;
A.CallFunc( nState, x, y );


이렇게...

어떠한가. 함수포인터의 위력이 느껴지는가?




< STL을 이용한 함수포인터 관리 >

우리는 지금까지 함수포인터를 동적으로 배열을 할당해서 써왔다.

함수 포인터를 STL(Standard Template Library)을 써서 관리해보자.

클래스의 멤버함수의 함수포인터화에서 3번째 방법을 조금 개선시켜 보겠다.



단순히 인덱스(숫자)를 이용한 관리라면 deque정도가 괜찮을듯 싶으나,

만약 함수의 이름을 문자열로 호출하고 싶다면 map을 써볼 수 있다.

(만약 FuncCall( "GetArea", x, y ); 이런식으로 멤버함수를 호출하고 싶다면)

map은 내부적으로 트리구조를 가지고 있다.

그래서 따로 정적/동적으로 배열을 할당하지 않아도 입력된 값을 비교해서 스스로 자신의 크기를 늘린다.

mapValue["GetArea"] = 99;

이런식으로 []안에 숫자 뿐만아니라 비교할 수 있는 모든 것이 들어갈 수 있다.

먼저 map을 사용하기 위해

#include < map >
using namespace std;


를 선언한다. map은 표준 네임스페이스를 사용하므로 std의 이름공간을 활용한다.

map< []안에 들어갈 타입, 입력될 데이터타입, 비교 논리 > mapFunctor;

선언방법은 이렇게 되는데 비교 논리는 첫번째 인수가 클래스이고 안에 비교오퍼레이터가 있다면 생략가능하다

자, 이제 해보자.



struct ltstr
{
    bool operator() ( const char * s1, const char * s2 ) const
    {
         return strcmp( s1, s2 ) < 0;
    }
};

class CFunc
{
public:
    typedef int (CFunc::*_Func)( int, int );
    map< const char *, _Func, ltstr > mapFunctor;
    int GetArea( int x, int y );
    void CallFunc( const char * szFuncName, int x, int y )
    {
         (this->*mapFunctor[szFuncName])( x, y );
    }

    CFunc();
    ~CFunc();
};

CFunc::CFunc()
{
    // init
    mapFunctor["GetArea"] = GetArea;
    mapFunctor["GetAreaEx"] = GetAreaEx;
}
CFunc::~CFunc()
{
    // map 클리어
    mapFunctor.clear();
}
int CFunc::GetArea( int x, int y )
{
    return x * y;
}

char * 대신 string을 사용한다면 string안에 내부적으로 비교 오퍼레이터함수가 있기 때문에

map< string, _Func > mapFunctor;

이렇게 선언하고 사용할 수 있을 것이다.

이제 A.CallFunc( "GetAreaEx", x, y ); 란 호출로 GetAreaEx를 호출할 수 있다.

이 방식은 여러가지로 응용가능한데 스킬명에 의한 화면효과 호출이라던지

C로 미리 작성된 내부 함수를 외부 스크립트로 호출한다던지 할때 유용하게 쓰일 수 있다.

(스크립트 호출일 경우 함수이름을 인덱스화 해서 deque를 쓰는게 속도상 더 유리할 듯 하다)



< Caution >

- 귀차니즘의 관계로 클래스내에 GetAreaEx, GetArea2 등과 같은 멤버함수를 모두 기술하지는 않았습니다.
- 예제 소스는 컴파일해보지 않은 소스들이므로 오타나 잘못된 점이 있을 수도 있습니다. 지적 바랍니다.
- VS 2005 에서 에러가 납니다만, 몇가지를 수정해주시면 제대로 됩니다.
- 이 강좌는 제가 그동안 겪고 배우고 또 여기저기서 수집한 자료를 바탕으로 쓴 강좌입니다. 틀린부분이 있을 수도 있으니
그런 부분은 지적 바랍니다.
- 글의 이동은 자유지만 출처는 명시해 주시기 바랍니다.

출처: zeph's Third Story

'공부합시다 > C++' 카테고리의 다른 글

C++에서의 동적할당  (1) 2009.07.22
임시 객체  (0) 2009.07.21
[펌] 함수 포인터 및 클래스 멤버함수의 함수포인터화  (0) 2009.07.21
함수 const  (0) 2009.07.21
인라인(inline) 함수  (0) 2009.07.21
클래스 내 static 함수  (0) 2009.07.21
Trackback 0 Comment 0
2009. 7. 21. 11:35

함수 const




함수 선언 뒤에 const를 붙이게 되면 이 함수는 클래스 멤버들의 값을 변경할 수 없게 된다.
이러한 const 함수가 필요한 경우는 위와 같이 const 객체를 사용해야 할 때 이다.

const 객체는 const 함수밖에 사용할 수 없다.

'공부합시다 > C++' 카테고리의 다른 글

임시 객체  (0) 2009.07.21
[펌] 함수 포인터 및 클래스 멤버함수의 함수포인터화  (0) 2009.07.21
함수 const  (0) 2009.07.21
인라인(inline) 함수  (0) 2009.07.21
클래스 내 static 함수  (0) 2009.07.21
클래스내 static 변수 초기화 방법  (0) 2009.07.21
Trackback 0 Comment 0
2009. 7. 21. 11:12

인라인(inline) 함수

다음과 같은 매크로 함수를 보자.



\는 줄을 붙여서 만든다는 말이다. 그리고 변수마다 괄호를 친 이유는 지금의 소스에는 관계없지만 복잡해질경우 우선순위가 꼬일 수 있기 때문에 괄호를 친다.

매크로 함수는 C에서 많이 쓰였었지만 매크로 함수에는 심각한 문제점이 있는데 그것은 변수의 형을 검사하지 않는다는 것이다. 따라서 C++에서는 매크로 함수를 사용하지 않는 것을 권장하고 있다.

하지만 매크로 함수에도 장점이 있는데 일반 함수는 스택을 사용하지만 매크로는 그대로 코드가 삽입되는 것이므로 훨씬 빠를 수 있다. 따라서 이러한 매크로 함수의 장점을 대안하기 위해 C++은 인라인 함수라는 것을 제공한다.




inline 함수는 컴파일시 헤더파일에 포함되게 된다.
inline 함수는 보통 함수처럼 호출이 되면 메모리 주소로 점프하는 것이 아니라 매크로 함수처럼 소스코드가 통채로 삽입되게 된다.

'공부합시다 > C++' 카테고리의 다른 글

임시 객체  (0) 2009.07.21
[펌] 함수 포인터 및 클래스 멤버함수의 함수포인터화  (0) 2009.07.21
함수 const  (0) 2009.07.21
인라인(inline) 함수  (0) 2009.07.21
클래스 내 static 함수  (0) 2009.07.21
클래스내 static 변수 초기화 방법  (0) 2009.07.21
Trackback 0 Comment 0
2009. 7. 21. 10:48

클래스 내 static 함수




static 함수는 객체를 생성하지 않아도 호출할 수 있다.
그렇기 때문에 static 변수가 아닌 변수들은 객체가 생성되지 않으면 없기 때문에 static 함수에서 사용할 수 없도록 해놓았다. 따라서 static 함수는 오로지 static 변수만을 사용할 수 있다.

'공부합시다 > C++' 카테고리의 다른 글

임시 객체  (0) 2009.07.21
[펌] 함수 포인터 및 클래스 멤버함수의 함수포인터화  (0) 2009.07.21
함수 const  (0) 2009.07.21
인라인(inline) 함수  (0) 2009.07.21
클래스 내 static 함수  (0) 2009.07.21
클래스내 static 변수 초기화 방법  (0) 2009.07.21
Trackback 0 Comment 0
2009. 7. 21. 10:37

클래스내 static 변수 초기화 방법

클래스 내 static 변수의 초기 방법




static 변수는 생성자에서 초기화 할 수 없다. 왜냐하면 객체를 생성할 때 마다 static 변수가 초기화 된다면, 전역변수로서의 의미가 없어지기 때문이다. 따라서 클래스 밖에서 초기화를 해주어야 한다.

'공부합시다 > C++' 카테고리의 다른 글

임시 객체  (0) 2009.07.21
[펌] 함수 포인터 및 클래스 멤버함수의 함수포인터화  (0) 2009.07.21
함수 const  (0) 2009.07.21
인라인(inline) 함수  (0) 2009.07.21
클래스 내 static 함수  (0) 2009.07.21
클래스내 static 변수 초기화 방법  (0) 2009.07.21
Trackback 0 Comment 0
2009. 7. 21. 08:42

VFW 영상 처리 함수

비디오 캡쳐를 하기 위해서 먼저 vfw32.lib 를 프로젝트에서 포함 해주어야 하며 작성 소스 상단에 Vfw.h를

include 해주어야 한다.

 

비디오 캡쳐를 위한 작업 순서를 간단히 다음과 같다.

 

- 캡쳐 윈도우를 생성

- 윈도우와 캡쳐 드라이버를 연결

- 캡쳐된 비디오 프레임을 캡쳐 윈도우를 통해서 출력

캡쳐된 비디오 프레임을 캡쳐 윈도우에 보여주기 위해서는 한 프레임이 캡쳐 될 때마다 특정 함수를 호출하도록 한 다음에 호출된 함수에서 그 프레임을 화면에 출력한다.

이러한 작업을 수행하기 위해서는 VFW 라이브러리 중에서 다음과 같은 함수들을 사용해야 한다.

capGetDriverDescription()

CapCreateCaptureWindow()

capDriverConnect()

capPreviewRate()

capSetVideoFormat()

capDriverDisconnect()

capSetCallbackOnFrame()

 

 - 함수의 원형

 

HWND VFWAPI capCreateCaptureWindowW (
        LPCWSTR lpszWindowName,
        DWORD dwStyle,
        int x, int y, int nWidth, int nHeight,
        HWND hwndParent, int nID);

이 함수는 캡쳐 윈도우를 생성한다. name 에는 윈도우위 이름을 지정한다. style 윈도우위 스타일을 지정한다. (x,y)에는 캡쳐 윈도우의 좌측 상단의 좌표를 지정한다. width height에는 캡쳐 윈도우의 크기를 지정한다. hWnd에는 부모윈도우의 핸들값을 입력한다. id에는 윈도우의 식별 번호를 입력한다. 캡쳐 윈도우가 정상적으로 생성되면 캡쳐 윈도우의 핸들의 함수 결과값으로 반환되고 그렇지 않으면 NULL값이 반환된다.

 

 

BOOL VFWAPI capGetDriverDescriptionW (UINT wDriverIndex,
        LPWSTR lpszName, int cbName,
        LPWSTR lpszVer, int cbVer);

이 함수는 캡쳐 드라이버의 이름 및 버전 정보를 검색한다. 첫 번째 매개변수인 index는 검색하고자 하는 드라이버의 번호를 나타내는데, 0부터 9까지의 값을 가질 수 있다.

즉, 한 컴퓨터에서 9대의 캡쳐 장치가 사용될 수 있다고 가정하고 있다. 검색하고자 하는 번호의 드라이버가 존재하면 이 함수는 name에 드라이버의 이름을 저장하고 version에 드라이버 버전을 저장한 다음에 함수 결과값으로 TURE 값을 반환한다. 

 

capDriverConnect(hWnd, index);

이 함수는 아래 처럼 디파인 되어 있으며

#define capDriverConnect(hwnd, i)          ((BOOL)AVICapSM(hwnd, WM_CAP_DRIVER_CONNECT, (WPARAM)(i), 0L))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_DRIVER_CONNECT 를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 캡쳐 윈도우를 캡쳐 드라이버에 연결한다. hWnd는 캡쳐 윈도우의 핸들을 나타내고 index는 캡쳐 드라이버의 번호를 나타낸다. 이 함수는 캡쳐 장치가 정상적으로 작동하여 연결이 성공되면 TURE 값을 반환하고 그렇지 않으면 FALSE 값을 반환한다.

 

 

capPreviewRate(hWnd, rate);

이 함수는 아래 처럼 디파인 되어 있으며

#define capPreviewRate(hwnd, wMS)   ((BOOL)AVICapSM(hwnd, WM_CAP_SET_PREVIEWRATE, (WPARAM)(wMS), 0))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_SET_PREVIEWRATE를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 미리보기 (preview)모드에서의 프레임 재생 속도를 설정한다. 여기에서 미리보기란 카메라에서 입력된 비디오를 파일에 저장하는 것이 아니라 화면에 보여준다는 것을 의미한다. hWnd는 캡쳐 윈도우의 핸들 값으로 설정하고 rate는 밀리초(ms) 단 위의 시간으로 설정한다. 예를 들어, rate 값을 66으로 설정하면 0.066초마다 새로운 비디오 프레임을 캡쳐해서 디스플레이 하게 도니다. 이와 같은 속도로 재생을 하면 1초에 15개의 비디오 프레임이 디스플레이된다.

 

capSetVideoFormat(hWnd, videoFormat, videoFormat_size);

#define capSetVideoFormat(hwnd,s,wSize)          ((BOOL)AVICapSM(hwnd, WM_CAP_SET_VIDEOFORMAT, (WPARAM)(wSize), (LPARAM)(LPVOID)(s)))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_SET_VIDEOFORMAT를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 캡쳐된 비디오 데이터 형식을 설정한다. 사용자가 원하는 비디오 데이터형식이 캡쳐 장치에서 지원이 되면 이 함수는 TRUE 값을 반환하고 그렇지 않으면 FALSE 값을 반환하므로 반드시 이 함수의 결과값이 TRUE 인지 검사한 다음에 다음단계로 넘어가야 한다.

hWnd는 캡쳐 윈도우의 핸들 값으로 설정한다. videoFormat은 설정하고자 하는 비디오 데이터 형식을 나타내는데, 비디오 데이터의 각 프레임에 대한 비트맵 형식을 BITMAPINFO 구조로 기술한다.

 

BITMAPINFO 구조는 다음과 같다.

typedef struct tagBITMAPINFO{

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColrs[1];

} BITMAPINFO;  

BITMAPINFO는 BITMAPINFOHEADER와 RGBQUAD로 구성되는데, 여기에서는 다음과 같이 정의되는 BITMAPINFOHEADER 구조체의 값만 사용하면 된다.


pBmiInfo->bmiHeader.biSize = 40;  //BITMAPINFOHEADER 구조체의 크기

pBmiInfo->bmiHeader.biHeight = 480; //영상의 가로크기

pBmiInfo->bmiHeader.biWidth = 640;

//영상의 세로크기(양수:좌측 하단이 원점   음수:좌측 상단이 원점)

pBmiInfo->bmiHeader.biPlanes = 1; //목표 장치의 플레인 수(1로 설정해야함)

pBmiInfo->bmiHeader.biBitCount = (short) 24; //각 픽셀의 비트수

pBmiInfo->bmiHeader.biCompression = 0; //압축 방법(bi_rgb 또는 0:무압축 비트맵)

pBmiInfo->bmiHeader.biSizeImage =0; //비트맵 영상 크기(무압축인 경우 0으로 설정)

pBmiInfo->bmiHeader.biClrImportant = 0; // 비트맵 디스플레이에 사용되는 컬러수

pBmiInfo->bmiHeader.biClrUsed = 0; // 사용된 컬러의 수

pBmiInfo->bmiHeader.biXPelsPerMeter = 10000; //수평 해상도 (미터당 픽셀수)

pBmiInfo->bmiHeader.biYPelsPerMeter = 10000; //수직 해상도 (미터당 픽셀 수)

}BITMAPINFOHEADER;

비디오 캡쳐를 위해서는 다음과 같이 BITMAPINFOHEADER 구조체에서 biSize,biWidth, biHeight, biPlanes, biBitCount 값을 설정하고 나머지 값을은 0으로 설정하면된다.

BITMAPINFO bmi;

memset(&bmi.bmiHeader, 0, sizeof(bmiHeader));

pBmiInfo->bmiHeader.biSize = sizeof(bmi.bmiHeader);

pBmiInfo->bmiHeader.biWidth = 640;//영상의 세로크기(양수:좌측 하단이 원점

pBmiInfo->bmiHeader.biHeight = 480;//영상의 가로크기

pBmiInfo->bmiHeader.biPlanes = 1;//목표 장치의 플레인 수(1로 설정해야함)

pBmiInfo->bmiHeader.biBitCount =  24;//각 픽셀의 비트수

 

capDriverDisconnect(hWnd);

#define capDriverDisconnect(hwnd)       ((BOOL)AVICapSM(hwnd, WM_CAP_DRIVER_DISCONNECT, (WPARAM)0, 0L))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_DRIVER_DISCONNECT를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 carDriverConnect() 함수에 의하여 연결한 캡쳐 윈도우와 캡쳐 장치를 분리하는 함수이다. hWnd에는 분리하고자 하는 캡쳐 윈도우의 핸들 값을 설정한다.

 

BOOL capSetCallbackOnFrame(hWnd, func);

#define capSetCallbackOnFrame(hwnd, fpProc)        ((BOOL)AVICapSM(hwnd, WM_CAP_SET_CALLBACK_FRAME, 0, (LPARAM)(LPVOID)(fpProc)))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_SET_CALLBACK_FRAME를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

VFW 라이브러리에서는 캡쳐된 비디오 프레임을 화면에 보여주는 작업을 callback함수를 사용해서 처리하도록 하고 있다. capSetCallbackOnFrame()함수는 캡쳐 장치로부터 비디오 프레임이 캡쳐되었을때에 이를 화면에 보여주기 위해서 호출되는 callback 함수를 설정한다. hWnd는 캡쳐 윈도우의 핸들 값으로 설정하고 func는 호출될 함수 이름으로 설정한다.  

 

 

BOOL capOverlay(hWnd, f);
#define capOverlay(hwnd, f)          ((BOOL)AVICapSM(hwnd, WM_CAP_SET_OVERLAY, (WPARAM)(BOOL)(f), 0L))

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 비디오 오버레이를 사용 할것인지 아닌지를 설정한다. 설정 성공시 TRUE, 그렇지 않을경우 FALSE를 반환.
매개변수는 순서대로 윈도우 핸들, 설정시-true 미설정시 -false
비디오 오버레이를 사용하면 CPU자원이 요구 되지 않아 효과적이라고 한다. 
 
BOOL capPreview(hWnd, f);
#define capPreview(hwnd, f)       ((BOOL)AVICapSM(hwnd, WM_CAP_SET_PREVIEW, (WPARAM)(BOOL)(f), 0L))

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 미리보기 모드를 동작 시킬 것인지 아닌지를 설정한다. 설정을 하게 되면 영상 프레임이 시스템의 메모리로 전달되어 GDI함수를 사용하여 캠처 윈도우에 영상을 출력하게 된다.
매개 변수는 순서대로 윈도우 핸들, 설정시 -true, 미설정시 -false 

'공부합시다 > 영상처리' 카테고리의 다른 글

VFW 영상 처리 함수  (0) 2009.07.21
비트맵 헤더 분석  (0) 2009.07.15
비트맵 파일의 구조  (0) 2009.07.15
Trackback 0 Comment 0
2009. 7. 15. 13:34

비트맵 헤더 분석

이번엔 실제 BMP파일을 헥스뷰어로 열어보고 헤더의 정보를 살펴보도록 하겠다.


위의 BMP 파일을 WinHex 같은 헥스뷰어로 열어보면, 다음과 같은 화면을 볼 수 있다.


BMP 헤더 정보
1. 0x0 ~ 0x1: 가장 처음의 두 바이트는 매직넘버이다. B와 M에 해당하는 ASCII값인 42, 4D가 보인다.

2. 0x2 ~ 0x5: 그 다음 부터 4바이트는 파일의 크기이다. 다만, 리틀 인디안으로 저장되어 있으므로 0x64356가 된다. 실제로 저 그림 파일을 다운받아 저장해보면 410,454 바이트인것을 알 수 있는데 이를 16진수로 변환하면 0x64356이 된다.

3. 그 다음 2바이트와 그 다음 2바이트는 응용프로그램에 따라 달라지는데 이는 다음 기회에 알아보도록 하자.

4. 0xA ~ 0xD : 픽셀 데이터의 시작 위치 이다. 헤더의 크기 14바이트와 비트맵 정보 40바이트(윈도우v3 비트맵일 경우. 대부분 이경우다.), 총 54바이트 다음인 0x36번지 부터 픽셀데이터가 기록되어 있다.

비트맵 정보
1. 0xE ~ 0x11 : 헤더의 크기. 0x28 = 40 이므로 헤더의 크기는 40바이트이다. 즉, 윈도우 v3 비트맵이란 이야기.

2. 0x12 ~ 0x15 : 비트맵 가로크기. 비트맵은 그림의 가로 길이가 4의 배수가 아닌 경우 더미 데이터가 들어가게 된다. 0x1E0 = 480 이므로, 이 비트맵의 가로 크기는 480이다.

3. 0x16 ~ 0x19 : 비트맵 세로 크기.

4. 0x1A ~ 0x1B : 사용하는 색 판(color plane)의 수. 1로 설정해야 한다.

5. 0x1C ~ 0x1D :
한 화소에 들어가는 비트 수이며 그림의 색 깊이를 뜻한다. 보통 값은 1, 4, 8, 16, 24, 32이다. 이 파일의 경우는 0x18 = 24비트이다.

6. 0x1E ~ 0x21 :
압축 방식. 비트맵도 압축 방식이 있다. 하지만 잘 사용하지 않는다. 따라서 값은 0.

7. 0x22 ~ 0x25 :
그림 크기. 압축되지 않은 비트맵 데이터의 크기이며, 파일 크기와 혼동하지 말 것. 즉, 헤더를 제외한 크기. 이 파일의 경우에는 0x64320인데 파일크기는 0x64356이므로, 0x36바이트 즉, 54바이트의 헤더를 제외한 만큼 차이가 난다.

8. 0x26 ~ 0x29 : 그림의 가로 해상도. (미터 당 화소, signed integer)

9. 0x2A ~ 0x2D :
그림의 세로 해상도. (미터 당 화소, signed integer)

10. 0x2E ~ 0x31 :
색 팔레트의 색 수, 또는 0에서 기본값 2n.

11. 0x32 ~ 0x35 :
중요한 색의 수. 모든 색이 중요할 경우 0. 일반적으로 무시.

픽셀 데이터

0x36 ~ : 한 픽셀을 표현하기 위해 R, G, B 3바이트가 필요하므로, 3바이트씩 한픽셀을 표현한다. 다만 리틀 인디언이므로 BGR의 순으로 저장되어 있다. 또한, BMP파일은 그림의 상하를 반대로 저장하므로 첫 픽셀의 정보는 그림의 가장 아래줄 첫번째 픽셀이 된다.

'공부합시다 > 영상처리' 카테고리의 다른 글

VFW 영상 처리 함수  (0) 2009.07.21
비트맵 헤더 분석  (0) 2009.07.15
비트맵 파일의 구조  (0) 2009.07.15
Trackback 0 Comment 0
2009. 7. 15. 12:23

비트맵 파일의 구조

비트맵(BMP) 파일은 가장 간단한 구조의 이미지 파일 중 하나이다. 기본적으로 1, 4, 8, 16, 24, 32 비트를 지원하지만 16과 32비트는 잘 사용하지 않는다. bmp파일은 구조가 간단한 만큼 용량이 매우 큰 파일이다. 그렇기 때문에 기본적인 압축형식을 지원하지만 효율이 그다지 좋지 않아 압축을 사용하는 경우는 드물다.

BMP 파일 포맷은 단순하고 윈도와 같은 여러 곳에서 널리 쓰일뿐 아니라 상대적으로 특허에서 자유로우므로, 수많은 운영 체제가 제공하는 그림 처리 프로그램이 읽고 쓸 수 있는 매우 일반적인 포맷으로 자리잡았다.

bmp파일도 여러가지 방식이 있기는 하지만 대부분이 사장되고, 현재 남은 형식은 windows version3를 가장 많이 사용하고 있다. 물론 OS/2같은 운영체제에서는 다른 형식을 사용하기도 한다.

BMP 파일의 구조

일반적으로 쓰이는 BMP는 다음의 데이터 블록을 담고 있다.

 BMP 헤더
 BMP 파일에 대한 일반 정보를 담고 있다.
 비트맵 정보(DIB 헤더)
 비트맵 그림에 대한 자세한 정보를 담고 있다.
 색 팔레트
 인덱스 컬러 비트맵에 쓰이는 색의 정의를 담고 있다.
 비트맵 데이터
 화소 대 화소 단위의 실제 그림을 담고 있다.

1. BMP 파일 헤더

오프셋#

크기

목적

0

2

BMP 파일을 식별하는 데 쓰이는 매직넘버:0x42 0x4D (B와 M에 대한 ASCII 코드값)

2

4

BMP 파일 크기(바이트 단위)

6

2

그림을 만드는 데 쓰인 응용 프로그램에 따라 달라진다.

8

2

그림을 만드는 데 쓰인 응용 프로그램에 따라 달라진다.

10

4

오프셋. 픽셀 데이터의 시작 주소(바이트 단위)


2. 비트맵 정보(DIB 헤더)

기본적으로 쓰이는 비트맵 정보는 다음과 같다.

크기

헤더

식별자

GDI지원

40

윈도우 V3

BITMAPINFOHEADER

윈도우 3.0 이후의 모든 윈도우 버전

12

OS/2 V1

BITMAPCOREHEADER

OS/2 및 윈도 3.0 이후의 모든 윈도 버전

64

OS/2 V2

 

 

108

윈도우 V4

BITMAPV4HEADER

윈도우 95/NT4 이후의 모든 윈도우 버전

124

윈도우 V5

BITMAPV5HEADER

윈도우 98/2000 이상


 호환성을 이유로 대부분의 응용 프로그램은 파일을 저장할 때 오래된 DIB 헤더를 사용한다. OS/2가 물러나자, 이제 일반적으로 쓰이는 포맷은 V3 헤더가 되었다. 더 자세한 것은 아래의 표를 참조하자.

오프셋 #

크기

목적

14

4

이 헤더의 크기 (40 바이트)

18

4

비트맵 가로 (단위는 화소, signed integer).

22

4

비트맵 세로 (단위는 화소, signed integer).

26

2

사용하는 색 판(color plane)의 수. 1로 설정해야 한다.

28

2

한 화소에 들어가는 비트 수이며 그림의 색 깊이를 뜻한다. 보통 값은 1, 4, 8, 16, 24, 32이다.

30

4

압축 방식. 가능한 값에 대한 목록은 다음 표를 참조하라.

34

4

그림 크기. 압축되지 않은 비트맵 데이터의 크기(아래 참조)이며, 파일 크기와 혼동하지 말 것.

38

4

그림의 가로 해상도. (미터 당 화소, signed integer)

42

4

그림의 세로 해상도. (미터 당 화소, signed integer)

46

4

색 팔레트의 색 수, 또는 0에서 기본값 2n.

50

4

중요한 색의 수. 모든 색이 중요할 경우 0. 일반적으로 무시.




'공부합시다 > 영상처리' 카테고리의 다른 글

VFW 영상 처리 함수  (0) 2009.07.21
비트맵 헤더 분석  (0) 2009.07.15
비트맵 파일의 구조  (0) 2009.07.15
Trackback 0 Comment 0
2009. 7. 9. 11:27

[소켓프로그래밍] WSAAsyncSelect

WSAAsyncSelect

WSAAsyncSelect 함수는 지정된 소켓에 대해서 특정한 네트웍 이벤트에 대한 윈도즈 통지 메시지를 받을 것인지 않받을 것인지 설정하는 함수입니다.    - 좀 양이 많네요^^ 그래두 중요한 부분이니까 꼭 숙지 하도록 해요.

int WSAAsyncSelect (
            SOCKET   
s,
            HWND   
hWnd,
            unsigned int   
wMsg,
            long   
lEvent
);

 

Parameters

s
[입력] 이벤트 통지를 설정할 대상 소켓 기술자

hWnd
[입력] 네트웍 이벤트가 발생 했을 때 메시지를 수신할 윈도즈 핸들

wMsg
[입력] 네트웍 이벤트가 발생했을 때 수신될 메시지

lEvent
[입력] 어플리케이션이 수신할 네트웍 이벤트의 비트조합

 

Remarks

WSAAsyncSelect 함수는 WS2_32.DLL 이 IEvent 매개변수에 의해 지정된 네트웍 이벤트가 발생했을대 마다 hWnd 윈도우로 메시지를 보내야 할 때 사용됩니다. 보내야 하는 메시지는 wMsg 매개변수로 지정한 메시지가 되고, 통지받는 대상 소켓은 s 매개변수에 의해서 지정된 소켓입니다.

WSAAsyncSelect 함수는 IEvent 값에 개의치 않고, 소켓 s 를 비동기 모드로 자동적으로 설정합니다. 동기모드에서 비동기 모드로 설정하는 방법에 대해 알고 싶으면, ioctlsocket 함수를 참조하도록 하세요.

IEvent 매개변수는 아래 리스트에 보이는 값들을 OR 비트연산 해서 구성된 값으로 이루어 집니다.

Value Meaning
FD_READ 데이터 수신 통지메시지를 수신 하려할때.
FD_WRITE 데이터 전송이 준비가 되었다는 통지메시지를 수신하려 할 때
FD_OOB out-of-band 데이터가 수신되었다는 통지메시지를 수신하려 할때
FD_ACCEPT 접속 요구가 들어왔다는 통지메시지를 수신하려 할때
FD_CONNECT 접속이나, multi-point join 작업이 완료되었다는 통지메시지를 수신하려 할 때
FD_CLOSE 연결된 상대방 소켓이 종료되었다는 통지메시지를 수신하려 할 때
FD_QOS Socket Quality of Service(QOS)가 변경되었다는 메시지를 수신하려 할 때
FD_GROUP_QOS Socket Group Quality of Service(QOS)가 변경되었다는 메시지를 수신하려 할 때
FD_ROUTING
_INTERFACE_CHANGE
지정된 목적지(들)에 대해서 경로배정 인터페이스가 변경되었다는 메시지를
수신하려 할 때.
FD_ADDRESS_LIST
_CHANGE
소켓의 프로토콜 집합에 대한 로컬 어드레스 리스트가 변경되었다는 메시지를
수신하려 할 때.

간단한 사용법을 한번 알아 볼까요? 데이터의 수신과 송신 통지 메시지를 수신하기 위해서는 FD_READ 그리고, FD_WRITE를 비트연산자로 합쳐서 WSAAsyncSelect 함수를 호출하게 됩니다. 아래와 같이 말이죠.

rc = WSAAsyncSelect(s, hWnd, wMsg, FD_READ|FD_WRITE);

WSAAsyncSelect 함수를 지정된 IEvent 를 가지고 호출하게 되면, 이전에 동일한 소켓에 대해서 WSAAsyncSelect 함수나 WSAEventSelect 함수로 설정되어 있던 메시지 이벤트는 무시됩니다. 새로 설정된 메시지 이벤트가 영향력을 미치게 되는 것이죠. 아래의 코드에서는 s 소켓에 대해서 wMsg2 메시지로 FD_WRITE 이벤트만이 발생하게 됩니다.

rc = WSAAsyncSelect(s, hWnd, wMsg1, FD_READ);
rc = WSAAsyncSelect(s, hWnd, wMsg2, FD_WRITE);

소켓에 대해 설정되어있는 모든 네트웍 통지메세지를 앞으로 수신받지 않으려면, IEvent 를 0으로 셋팅하면, 됩니다.

rc = WSAAsyncSelect(s, hWnd, 0, 0);

WSAAsyncSelect 함수가 소켓에 대한 이벤트 메시지를 바로 비활성화 시킬지라도, 메시지가 어플리케이션의 메시지 큐에서 대기하도록 하는 것은 가능합니다. 따라서 어플리케이션은 취소작업후에 네트웍 이벤트 메시지를 수신받기위해 준비해야 합니다. closesocket 함수로 소켓을 닫는겨우 역시 WSAAsyncSelect 메시지 전송을 취소 하지만, 큐에 남아있던 메시지는 없어지지 않고 여전히 남아있게 됩니다.

accept 함수로 생성된 소켓은 이 소켓을 접속허용 하는데 사용되었던 리슨소켓(listen socket)과 같은 속성을 가지게 됩니다. 그 결과로 리슨소켓에 WSAAsyncSelect 로 셋팅되었던 이벤트는 접속허용된 소켓에 그대로 적용됩니다.  한 예로, 리슨소켓이 FD_ACCEPT, FD_READ, 그리고 FD_WRITE 이벤트를 가지고 있다면, 이 리슨소켓으로 접속허용된 소켓 또한 역시 FD_ACCEPT, FD_READ, 그리고 FD_WRITE 속성을 가지게 된다는 거져. 만약 새로 접속허용된 소켓에 대해서 다른 wMsg 와 이벤트를 적용시키고 싶다면, WSAAsyncSelect 함수를 사용해서 새로운 적절한 이벤트 정보를 다시 설정해야 합니다.

등록된 네트웍 이벤트가 지정된 소켓 s 에 대해서 발생했을 때, 어플리케이션 윈도우 hWnd wMsg 메시지를 수신받게 됩니다. wParam 매개변수는 네트웍 이벤트가 발생한 소켓을 의미합니다. lParam의 하위 16비트는 발생된 네트웍 이벤트를 의미하고, lParam의 상위 16비트는 특정한 에러코드를 의미합니다. 에러코드는 WINSOCK(2).H에 정의된 에러코드 입니다.

이벤트 통지 메시지를 사용하는 방법에서, WSAGetLastError 함수는 에러값을 체크하는데 사용할 수 없습니다. 왜냐믄, WSAGetLastError 함수의 리턴값이 lParam의 상위 16비트와 다를 수 있기 때문입니다.

에러코드와 이벤트코드는 WSAGETSELECTERROR와 WSAGETSELECTEVENT 매크로를 사용해서 lParam으로부터 뽑아낼 수 있습니다. 이 매크로는 WINSOCK(2).H에 정의되어 있습니다.

#define WSAGETSELECTERROR(lParam) HIWORD(lParam)
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)

이러한 매크로를 사용하면, 코드의 이식성이 좋아진다고 합니다.

사용가능한 네트웍 이벤트 코드를 아래에 리스팅 했습니다. 참고 하세요.

Value Meaning
FD_READ 데이터를 수신 할 수 있는 상태가 되었당.
FD_WRITE 데이터를 전송 할 수 있는 상태가 되었당.
FD_OOB out-of-band 데이터를 수신할 수 있는 상태가 되었당.
FD_ACCEPT 새로운 접속 요구를 허용할 준비가 되었당.
FD_CONNECT 접속 또는 multi-point join 연산이 완료되었당.
FD_CLOSE 연결된 상태방 소켓이 종료 했당.
FD_QOS Socket Quality Of Service 가 변경되었당.
FD_GROUP_QOS Socket Group Quality of Service(QOS)가 변경되었당
FD_ROUTING
_INTERFACE_CHANGE
지정된 목적지(들)에 대해서 경로배정 인터페이스가 변경되었당.
FD_ADDRESS_LIST
_CHANGE
소켓의 프로토콜 집합에 대한 로컬 어드레스 리스트가 변경되었당.

WSAAsyncSelect 함수는 데이터 전송/수신 연산(send 또는 recv) 을 성공적으로 할 수 있을 때를 결정하는데 주로 사용됩니다. 이렇게 사용되는 경우, 튼튼한 어플리케이션을 만들려면, WSAEWOULDBLOCK이 발생될 수 있는 상황에 대해서 철저한 준비를 해야합니다. 한 예로 아래와 같은 상황이 일어 날 수도 있습니다.

1. 데이터가 소켓 s 에 도착했습니다... 삐리비리빅~
    이때 윈속은 메시지를 포스팅 합니다.

2. 이때 어플리케이션은 몇몇 다른 메시지를 처리하고 있습니다.

3. 처리하고 있는동안에 어플리케이션이 ioctlsocket( s, FIONREAD...)를 호출하고, 소켓에 읽을 수 있는 데이터가 있다는 것을 알아냅니다.

4. 어플리케이션은 recv( s, ...)를 호출하여 데이터를 읽습니다.

5. 1.에서 이미 수신된 메시지를 post했었죠? 즉, 메시지 큐에 메시지는 남아있게 되므로 데이터를 읽을 수 있는상태라는 FD_READ 가 발생합니다.

6. 어플리케이션은 recv( s, ... )를 호출하겠죠? 하지만, WSAEWOULDBLOCK 에러가 발생하게 됩니다.

윈도즈 소켓은 특정한 네트웍 이벤트 메시지로 어플리케이션 윈도우로 항상 메시지를 주지 않습니다. 이러한 어플리케이션 윈도우로의 이벤트의 통보는, 어플리케이션이 네트웍 이벤트의 암시적 재활성화(re-enables) 통지를 위한 함수를 호출하기 전까지 (메시지큐에) 붙여지지 않습니다.
영문을 고대루 해석하려 하니까 좀 어색한 것 같죠? 쉽게 예를 들어서 설명하자면, 소켓으로부터 읽혀질 데이터가 있다고 가정 합시다. 어플리케이션이 FD_READ 이벤트의 통보를 활성화 했었다면, 윈도우즈 소켓은 지정된 윈도우로 하나의 FD_READ 이벤트를 전송할 것입니다. 이때 어플리케이션이
recv함수나 recvfrom 함수를 호출해야만 FD_READ 이벤트 생성이 다시 활성화 된다는 것입니다. 동시다발적으로 메시지가 도착하지는 않는다는 말입니다. recv 함수나 recvfrom 함수가 호출된 후에 소켓의 수신큐에 더 많은 데이터가 기다리고 있을 경우 다른 FD_READ 이벤트가 지정된 윈도우로 전송될 것입니다. 이렇게 메시지를 재 활성화 하는 함수를 "re-enabling function" 이라고 합니다. 아래의 표에 이러한 함수들을 나열해 보았습니다.

Event Re-enabling function
FD_READ recv, recvfrom, WSARecv, or WSARecvFrom
FD_WRITE send, sendto, WSASend, or WSASendTo
FD_OOB recv, recvfrom, WSARecv, or WSARecvFrom
FD_ACCEPT accept or WSAAccept ( condition 함수가 CF_DEFER인 WSATRY_AGAIN 일 경우는 제외)
FD_CONNECT 없음
FD_CLOSE 없음
FD_QOS SIO_GET_QOS 컴맨드로 호출된 WSAIoctl
FD_GROUP_QOS SIO_GET_GROUP_QOS 컴맨드로 호출된 WSAIoctl
FD_ROUTING
_INTERFACE_CHANGE
SIO_ROUTING_INTERFACE_CHANGE 컴맨드로 호출된 WSAIoctl
FD_ADDRESS_LIST
_CHANGE
SIO_ADDRESS_LIST_CHANGE 컴맨드로 호출된 WSAIoctl

FD_READ, FD_OOB, 그리고 FD_ACCEPT 이벤트에 대한 메시지 포스팅 방법은 "level-triggered"라고 합니다. 무슨 말이냐구요? 만약 재활성화 루틴이 호출되고, 이와 관련된 상태가 루틴 호출후에도 여전히 남아 있다면, 메시지는 어플리케이션에 다시 포스팅 됩니다. 이러한 포스팅 방법으로 어플리케이션은 메시지 구동 방식이 됩니다. 아직 이해가 잘 않되시죠? 아래의 순서를 한번 생각해 보세요... 지금 제가 한말을 쉽게 이해 할 수 있을 겁니다.

1. 네트웍 전송/수신 스택이 소켓에 100 바이트의 데이터를 수신받고, 윈속이 FD_READ 메시지를 포스팅 했습니다.

2. 어플리케이션이 recv( s, buffer, 50, 0 )을 호출해서 50바이트를 수신했습니다.

3. 읽을 데이터가 여전히 남아있는 동안 FD_READ 메시지가 포스팅 됩니다.

이러한 구조상에서, 어플리케이션은 하나의 FD_READ 메시지에 대해서 모든 가능한 데이터를 읽어낼 필요가 없습니다. 각각의 FD_READ 메시지에 대해서 하나의 recv를 사용하는 것이 적절한 방법입니다. 만약 어플리케이션이 하나의 FD_READ에 대해서 여러개의 recv를 호출할 경우, 여러개의 FD_READ 메시지를 수신할 수도 있습니다. 이러한 어플리케이션에서는 recv함수를 호출하기전에 WSAAsyncSelect 함수를 FD_READ 이벤트를 셋팅하지 않는 방법으로 FD_READ 메시지 수신을 막아 버려야할 필요가 있습니다.

FD_QOS 그리고 FD_GROUP_QOS 이벤트는 "edge triggered"로 간주됩니다. 메시지는 Quality Of Service가 바뀌었을 때 정확히 한번 포스팅 됩니다. 게다가 메시지는 프로바이더가 QOS에서의 다른 변화를 감지하든지, 아니면, 어플리케이션이 소켓에 대한 QOS를 재설정할 때 까지 메시지는 수신되지 않습니다.

FD_ROUTING_INTERFACE_CHANGE 메시지는 SIO_ROUTING_INTERFACE_CHANGE 컴맨드로 WSAIoctl 함수가 호출될 때 포스팅 됩니다.

FD_ADDRESS_LIST_CHANGE 메시지는 SIO_ADDRESS_LIST_CHANGE 컴맨드로 WSAIoctl 함수가 호출될 때 포스팅 됩니다.

만약 어플리케이션이 WSAAsyncSelect함수를 호출하거나 재활성(re-enabling)함수를 호출했을 때 이미 이벤트가 발생되었다면, 이때의 메시지는 적절히 포스팅 됩니다. 이해를 쉽게 하기위해 아래의 일어날 수 순서 상황을 생각해 보세요.

1. 어플리케이션이 생성한 소켓에 대해서 listen 함수를 호출 했습니다.

2. 접속 요구가 들어왔으나 아직 accept 하지 않았습니다.

3. 어플리케이션은 이 소켓에 대해서 FD_ACCEPT로 WSAAsyncSelect 함수를 호출하였습니다. 이때 이벤트의 영속성이 보존되기 때문에 윈속은 FD_ACCEPT 메시지를 바로 포스팅 하게 됩니다.

FD_WRITE 이벤트는 약간 다르게 운용됩니다. 즉, 소켓이 connect/WSAConnect 함수의 호출로 첨 접속되었을 때, 또는 accept/WSAAccept 함수로 접속허용되었을 때, 또는, send 연산이 WSAEWOULDBLOCK 으로 실패(비동기소켓은 항상 WSAEWOULDBLOCK입니다)한 상태에서 전송용 버퍼공간이 사용 가능해졌을 때 FD_WRITE 메시지는 포스팅 됩니다. 따라서 어플리케이션은 FD_WRITE 메시지를 받았을 때 데이터의 전송이 가능하다고 판단 할 수 있습니다.

FD_OOB 이벤트는 소켓이 out-of-band 데이터를 부분적으로 수신하여 나열할 때만 사용됩니다. 만약 소켓이 out-of-band 데이터를 인라인 방식으로 수신하도록 나열 한다면, out-of-band데이터는 일반 데이터와 같이 다루어 지게 됩니다. 이때, 어플리케이션은 interest(?)를 등록해야 합니다. 일반데이터와 같이 다루어 진다는 의미는 뭔지 아시겠죠? FD_OOB 이벤트가 아닌 FD_READ이벤트가 발생했을 때 데이터를 수신한다는 의미입니다. 어플리케이션은 out-of-band 데이터가 SO_OOBINLINE 옵션으로 setsockopt함수나 getsockopt함수를 사용하여 핸들링 하는 방법을 설정할 수 있습니다.

FD_CLOSE 메시지의 에러코드는 소켓이 우아한닫힘(graceful close)인지 그렇지 않은 닫힘인지를 나타냅니다. 만약 에러코드가 0이라면, 소켓은 우아한닫힘 이라는 의미이고, 에러코드가 WSAECONNRESET 이라면, 소켓의 가상 회선망이 리셋되었다는 것을 의미합니다. 이러한 것들은 SOCK_STREAM과 같은 접속 지향형 소켓일 경우에만 사용될 수 있는 것들입니다.

FD_CLOSE 메시지는 소켓과 연관된 가상 회선망이 끊겼다는 것을 수신받았을 때 포스팅 됩니다. TCP 의 경우는 접속이 TIME WAIT 나 CLOSE WAIT 상태로 들어갔을 때 FD_CLOSE 메시지가 포스팅 되게 됩니다. 즉, 원격 종단점에서의 전송영역의 셧다운이나 closesocket 함수의 호출의 결과로 일어날 수 있는 메시지입니다. FD_CLOSE 메시지는 모든 데이터가 소켓으로부터 읽혀지고 난 후에 포스팅 됩니다 그렇긴 해도, 최대한 손실된 데이터들을 피하고자 한다면, FD_CLOSE 메시지를 받은 후에 남아있는 데이터들을 체크하는 것이 좋겠죠?

아래에 지금까지 설명했던 비동기 통지 메시지에 대한 이벤트와 상태들을 간략히 요약해 놓았습니다.

FD_READ:  

    WSAAsyncSelect 함수가 호출 되고나서, 수신할수 있는 데이터가 있을 때

    데이터가 로컬 호스트로 도착하고, FD_READ 가 아직 포스팅되지 않았을 때

    recv 함수나 recvfrom 함수가 호출된 후에, 수신가능한 데이터가 여전히 남아 있을 때

    SO_OOBINLINE 옵션으로 setsockopt 함수를 호출하여 out-of-band 데이터가 일반데이터 처럼 취급되었을 때 수신할 out-of-band 데이터가 있을 때

FD_WRITE:

    WSAAsyncSelect 함수가 호출 되고, send sendto 함수가 사용가능 할 때

    connectaccept함수가 호출되고 나서, 접속이 완료 되었을 때

    sendsendto 함수가 WSAEWOULDBLOCK으로 실패한후, 전송용 버퍼가 사용 가능 할 때

    비접속 지향 소켓의 바인드 후에, FD_WRITE가 현재 발생되었거나 그렇지 않았을 때. (이러한 비접속 지향형 소켓은 바인드 후에 바로 데이터를 전송할 수 있는 상태의 소켓입니다.)

FD_OOB: setsockopt 함수를 이용해서 SO_OOBINLINE 옵션을 무시한 경우에만. (기본 설정값 이죠)

    WSAAsyncSelect 함수가 호출 되고나서, OOB 데이터를 MSG_OOB 플래그로 수신할수 있을 때.

    OOB데이터가 로컬 호스트로 도착하고, FD_OOB 가 아직 포스팅 되지 않았을 때.

    recv 또는 recvfrom 함수가 호출되고나서, 수신가능한 OOB 데이터가 남아 있을때.

FD_ACCEPT:

    WSAAsyncSelect 함수가 호출되고 나서, 허용가능한 접속 요청이 있을 때.

    접속 요청이 로컬호스트로 도착하고, FD_ACCEPT 가 아직 포스팅 되지 않았을 때.

    accept 함수의 호출 후에, 허용가능한 다른 접속 요청이 있을때.

FD_CONNECT:

    WSAAsyncSelect 함수가 호출되고 나서, 접속요청 작업의 완료가 있었던 경우.

    connect 함수가 호출되고 난 후에 접속 작업이 완료되었을 때.

    WSAJoinLeaf 함수의 호출 후에 join 연산이 완료 되었을 때.

    connect, WSAConnect 또는 WSAJoinLeaf 함수가 접속지향형 비동기 모드에서 호출된 후에, 연산이 시작되고 나면, WSAEWOULDBLOCK 이라는 에러코드가 발생될 것입니다. 왜냐구? 비동기기 때문이쥐... 암튼 이러한 비동기 함수의 호출후에 접속이 성공했든 실패했든간에 FD_CONNECT 메시지가 발생합니다. 클라이언트는 FD_CONNECT 메시지가 발생되었을 때 정말로 접속이 성공적으로 일어났는지 아닌지를 판단하기 위해서 에러코드를 체크 해 보아야 합니다.

FD_CLOSE: SOCK_STREAM과 같은 접속 지향형 소켓에서만 발생합니다.

    WSAAsyncSelect 함수가 호출되고, 접속이 종료 되었을때.

    원격지 소켓시스템이 우아한 종료(graceful close)를 한후에, 수신큐에 아무런 데이터가 없는 경우.

    로컬 시스템이 shutdown함수로 우아한 종료(graceful close)를 하고, 원격지 시스템이 이에 End of Data 로 응답하고(TCP 프로토콜의 FIN 이 1로셋팅된 경우), 수신가능한 데이터가 남아있지 않은 경우 발생.

    원격지 시스템이 접속을 리셋 시켰을 때(TCP 프로토콜의 RST가 1로 셋팅된 경우) 발생되는데, 이때 lParam은 WSAECONNRESET 에러 코드를 가지고 있게 됩니다.

    Note : closesocket 함수를 호출하고 난 후에 FD_CLOSE 메시지는 포스팅 되지 않습니다.

FD_QOS:

    WSAAsyncSelect 함수가 호출되고 나서, 소켓과 연관된 Quality Of Service가 변경된 경우

    SIO_GET_QOS 컴맨드로 WSAIoctl 함수가 호출된후, QOS가 변경된 경우

FD_GROUP_QOS (소켓 그룹으로 미래에 사용하기 위해 예약 되었다고 하네요?):

    WSAAsyncSelect 함수가 호출되고 나서, 소켓과 연관된 Group Quality Of Service가 변경된 경우

    SIO_GET_GROUP_QOS 컴맨드로 WSAIoctl 함수가 호출된후, GROUP QOS가 변경된 경우

FD_ROUTING_INTERFACE_CHANGE:

    SIO_ROUTING_INTERFACE_CHANGE 컴맨드로 WSAIoctl 함수가 호출되고 나서 로컬 인터페이스가 변경되었을 때

FD_ADDRESS_LIST_CHANGE

    SIO_ADDRESS_LIST_CHANGE 컴맨드로 WSAIoctl 함수가 호출되고 나서 로컬어드레스의 리스트가 변경되었을 때.

     

Return Values

에러가 없을 경우 WSAAsyncSelect 함수는 0을 반환 합니다. 에러가 있을 경우 SOCKET_ERROR를 반환 하며, WSAGetLastError 함수를 호출하여 지정된 에러코드를 검색할 수 있습니다.

 

Error Codes

WSANOTINITIALISED

이 함수를 사용하기 이전에 WSAStartup 함수를 성공적으로 호출해야 합니다.

WSAENETDOWN

네트웍 서브 시스템에서 에러가 났습니다.

WSAEINVAL 함수의 매개변수가 잘못 사용되었습니다.
WSAEINPROGRESS

블럭킹 윈속 v1.1이 진행상태에 있거나, 서비스 프로바이더가 아직 콜백함수를 진행하고 있습니다.

WSAENOTSOCK 지정된 소켓기술자가 소켓이 아닙니다.

추가적인 에러코드는 어플리케이션 윈도우가 메시지를 수신할 때 얻어낼 수 있습니다. 지금부터 나열하게될 에러코드는 lParam 매개변수를 WSAGETSELECTERROR 매크로를 이용하여 뽑아낸 에러코드 값입니다.

Event: FD_CONNECT

Error Code Meaning
WSAEAFNOSUPPORT 지정된 주소 패밀리가 본 소켓에서 사용될 수 없는 형태 입니다.
WSAECONNREFUSED 접속시도가 강제로 종료되었습니다.
WSAENETUNREACH 네트웍이 현재 지정된 호스트로 연결될 수 없습니다.
WSAEFAULT name 또는 namelen 매개변수가 올바른 형태가 아닙니다.
WSAEINVAL 소켓이 이미 특정한 어드레스와 바이드 되어 있습니다.
WSAEISCONN 소켓이 이미 접속상태에 있습니다.
WSAEMFILE 더 이상 소켓 기술자를 할당 할 수 없습니다.
WSAENOBUFS 사용할 수 있는 버퍼 공간이 없어서 접속할 수 없습니다.
WSAENOTCONN 소켓이 접속되어지지 못했습니다.
WSAETIMEDOUT 접속을 완료하지 못하고 타임아웃 되었습니다.

Event: FD_CLOSE

Error Code Meaning
WSAENETDOWN

네트웍 서브 시스템에서 에러가 났습니다.

WSAECONNRESET 접속이 원격지 로부터 리셋 되었습니다.
WSAECONNABORTED 접속이 타임아웃이나 다른 에러로 인하여 터미네이트 되었습니다.

Event: FD_READ
Event: FD_WRITE
Event: FD_OOB
Event: FD_ACCEPT
Event: FD_QOS
Event: FD_GROUP_QOS
Event: FD_ADDRESS_LIST_CHANGE

Error Code Meaning
WSAENETDOWN 네트웍 서브시스템에서 에러가 났습니다.

Event: FD_ROUTING_INTERFACE_CHANGE

Error Code Meaning
WSAENETUNREACH 지정한 목적지에 더 이상 도달 할 수 없습니다.
WSAENETDOWN 네트웍 서브시스템에서 에러가 났습니다.
 

QuickInfo

Windows NT : 사용가능
Windows : 사용가능
Windows CE : 지원되지 않음
Header :
          Win16/32 : winsock.h
          Win32-II : winsock2.h
Import Library :
          Win16 : winsock.lib
          Win32 : wsock32.lib
          Win32-II : ws2_32.lib

See Also

Trackback 0 Comment 0
2009. 7. 8. 20:44

[소켓프로그래밍] setsockopt

setsockopt

 

setsockopt 함수는 소켓 옵션을 설정하는 함수입니다.

 

int setsockopt (
        SOCKET
 s,
        int  
level,
        int
 optname,
        const char FAR *
  optval,
        int  
optlen
);

 

Parameters

s
[입력] 대상 소켓의 기술자(descriptor)를 명시합니다.

level
[입력] 소켓 옵션 레벨이 정의 되며, SOL_SOCKET 과 IPPROTO_TCP 중 하나가 될 수 있습니다.

optname
[입력] 셋팅 하기위한 소켓옵션 값입니다.

optval
[입력] 검색된 옵션을 저장 하기위한 데이터 버퍼를 포인트 합니다.

optlen
[입력]
optval 매개변수 버퍼의 크기

 

Remarks

setsockopt 함수는 지정된 소켓의 옵션을 특정한 형태(type), 상태(state)로 결합하기 위해서 지정한 소켓옵션 값을 셋팅하는 함수입니다. 비록 여러 프로토콜 레벨로의 소켓 옵션이 존재 할 순 있다 하더라도, 가장 우선하는 레벨이 존재하게 됩니다. 옵션은 소켓 연산에 영향을 미칩니다. 예를들어 발송된 데이터(OOB 데이터를 예로 들 수 있습니다) 를 일반 데이터 스트림으로 수신했을때나, 소켓으로 브로드캐스트(broadcast) 메세지를 전송할때 영향을 미치게 됩니다.

이 함수에서 사용 할 수 있는 두가지 형태의 소켓옵션이 있습니다. 활성화 또는 비활성화 시키기 위한 Boolean 옵션과, 정수형(integer) 또는 구조체를 요구하는 옵션이 있습니다. Boolean 옵션을 활성화 하기 위해서 optval 매개변수는 0이 아닌 정수를 포인트 하고, 비활성화 하기 위해서  0을 포인트 합니다. optlen 매개변수는 Boolean 옵션에 대해서는 sizeof(int)로 설정 해야만 합니다. 다른 옵션들에 대해서, optval 매개변수는 설정하고자 하는 정수(integer)나 구조체를 넘겨야 하고, optlen 매개변수는 정수(integer) 또는 구조체의 길이를 넘겨야 합니다. 아래에 setsockopt 함수에서 사용 할 수 있는 옵션들을 간단한 설명과 함께 나열 해 보았습니다.

 

level = SOL_SOCKET

옵션값(Value)

데이터형태(Type)

의미(Meaning)

SO_BROADCAST
 

BOOL
 

소켓으로 브로드캐스트(broadcast) 메세지를 전송 할 수 있도록 합니다.

SO_DEBUG

BOOL

디버깅 정보를 레코딩 합니다.

SO_DONTLINGER



BOOL


 

소켓을 종료(close)할때 보내지지 않은 데이터를 보내기위해서 블럭킹 상태가 되지 않도록 합니다. 이 옵션을 셋팅하는 것은 LINGER 구조체의 멤버중 l_onoff를 0으로 셋팅하고 SO_LINGER 옵션을 셋팅 했을때와 같은 효력을 발휘합니다.

SO_DONTROUTE

BOOL

라우팅 하지 않고, 직접 인터페이스로 보냅니다.

SO_GROUP_PRIORITY




 

int




 

차후에 소켓그룹을 사용하기위해 예약 되어있습니다. 소켓그룹의 일부분인 소켓을 만들기위해 관련된 우선사항을 명시합니다.
(우아아아악~ 정말 해석 못해 먹겠네여... 원문을 첨가 하겠습니다)
Reserved for future use with socket groups. Specify the relative priority to be established for sockets that are part of a socket group.

SO_KEEPALIVE

BOOL

Keepalives를 전송합니다.

SO_LINGER
 

struct LINGER
 

아직 전송되지 않은 데이터가 있을 경우 소켓을 종료(close)할때 대기합니다.

SO_OOBINLINE
 

BOOL
 

일반 데이터 스트림(normal data stream)으로 out-of-band 데이터를 수신합니다.

SO_RCVBUF

 

int

 

데이터를 수신하기 위한 총 버퍼공간을 명시합니다. 요놈은 SO_MAX_MSG_SIZE 나, TCP Window의 크기와 상관이 없습니다.

SO_REUSEADDR
 

BOOL
 

이미 사용되고 있는 어드레스를 바인드 할 수 있도록 합니다.

SO_SNDBUF

 

int

 

데이터를 전송하기 위한 총 버퍼공간을 명시합니다. 요놈은 SO_MAX_MSG_SIZE 나, TCP Window의 크기와는 상관이 없습니다.

level = IPPROTO_TCP

TCP_NODELAY
 
BOOL
 

전송합병(send coalescing)을 위해 Nagle 알고리즘을 비 활성화 합니다.

Return Values

에러가 발생하지 않으면, setsockopt 함수는 0을 반환합니다. 에러가 발생하면, SOCKET_ERROR을 반환하면 WSAGetLastError 함수를 이용해서 특정 에러 코드를 얻어낼 수 있습니다.

Error Codes

WSANOTINITIALISED

이 함수를 사용하기 전에 성공적인 WSAStartup 함수의 호출이 없었습니다.

WSAENETDOWN 네트웍 서브 시스템에 에러가 발생했습니다.
WSAEFAULT

optval 또는 optlen 매개변수 중의 하나가 올바르지 않거나 optlen 매개변수의 값이 너무 작습니다.

WSAEINPROGRESS

블럭킹 윈속 v1.1 이 현재 진행 중이거나, 서비스 프로바이더가 콜백 함수를 여전히 처리하고 있습니다.

WSAEINVAL

level 매개변수가 잘못됐거나, 알 수 없는 값 입니다.

WSAENETRESET

SO_KEEPALIVE가 설정 되었을때, 접속이 타입아웃 되었습니다.

WSAENOPROTOOPT

알 수 없는 옵션이거나, 지원지지 않는 옵션을 사용 했습니다.

WSAENOTCONN

SO_KEEPALIVE가 설정 되었을때, 접속이 리셋 되었습니다.

WSAENOTSOCK

기술자(descriptor)가 소켓 기술자가 아닙니다.

 

QuickInfo

Windows NT : 사용가능
Windows : 사용가능
Windows CE : 버젼 1.0 그리고 그이후의 버젼에서 사용가능
Header :
          Win16/32 : winsock.h
          Win32-II : winsock2.h
Import Library :
          Win16 : winsock.lib
          Win32 : wsock32.lib
          Win32-II : ws2_32.lib

See Also

overview, bind, getsockopt, ioctlsocket, socket, WSAAsyncSelect, WSAEventSelect

 

 

출처 : DEVPIA 곽용진님 style  APIs

[출처] [Socket] setsockopt|작성자 임종배

Trackback 0 Comment 0