★Overlapped 입출력의 의미

이전의 전송이 끝나기 전에 새로운 전송을 시작할 수 있게끔 입출력을 하는 방식

Overlapped 입출력이라고 부른다. (중첩됬으므로...)

이런 것이 가능해지려면 일단 전송을 하는 함수(ex: send)가 비동기 방식을 취해야한다.

다시 말해서, 데이터 전송이 안끝나도 실행되자마자 리턴해야된다.

참고로 하나의 쓰레드 내에서 말하고 있는 것이다.

 

 

★Overlapped 입출력을 위한 기본 단계

1] Overlapped 소켓의 생성

지금까지와는 다르게 소켓 자체가 overlapped가 되게끔 만들어야한다.

socket()보다 더 기능이 많은 WSASocket()을 사용한다. (WSA_FLAG_OVERLAPPED 플래그)

 

2] 데이터의 전송

WSASend()와 WSARecv()를 사용하면 중첩된 방식의 데이터 입출력이 가능해진다.

다시 한 번 말하지만 이 함수가 Overlapped 입출력을 가능하게끔 하는 원리는

비동기 방식으로 전송을 하는 것에서부터 비롯된다. (asynchronized)

실행이 되면 바로 리턴된다.

리턴하고 바로 또 WSASend/WSARecv를 호출하면 중첩되는 것이 당연.

    int WSASend(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags,

                      lpOverlapped, lpCompletionRoutine);

     int WSARecv(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags,

                      lpOverlapped, lpCompletionRoutine);

     ※ lpBuffers는 WSABUF 구조체 배열의 포인터다. (pg491)

      ※ lpNumberOfBytes~ : Pointer to the number of bytes received by this call if the

                                       send/receive operation completes immediately.

여기서 문제가 생긴다. WSASend로 이루어진 전송에 대한 결과 정보를 어떻게 받느냐다.

첫 번째 방법은 "Event 커널 오브젝트를 이용하는 방법"이고

두 번째 방법은 "CALLBACK 함수 기반의 Completion Routines를 사용하는 방법"이다.

 

WSASend나 WSARecv의 여섯번째의 파라미터(lpOverlapped)가 첫 번째 방법에 쓰이고

일곱번째의 파라미터(lpCompletionRoutine)가 두 번째 방법에 쓰인다.

 

※※ 리눅스에서 writev, readv를 이용하여 Gather/Scatter 입출력을 쉽게 했었는데

       윈도우에서도 WSASend/WSARecv의 인자로

       WSABUF을 여러 개 이용하여 Gather/Scatter 입출력이 가능하다.

 

 

★첫번째 방법: Event 커널 오브젝트 기반의 Overlapped I/O

이 전에 말했듯이, WSASend나 WSARecv함수의 여섯 번째 인자를 사용하게 된다.

WSAOVERLAPPED라는 타입의 구조체를 만들어가지고 초기화시켜서 집어넣으면 된다.

실제로 WSAOVERLAPPED 구조체의 멤버를 보면 5개나 되는데

4개는 내부적으로 쓰여서 우리가 신경쓸 바 없고

나머지 1개(WSAEVENT hEvent)만 우리가 초기화시켜줘서 넣어야한다.

 

이렇게 해서 이제 "전송 또는 수신"과 "Event 커널 오브젝트"가 연결되었다.

이렇게 연결된 전송 또는 수신이 끝나는 시점이 그 Event 커널 오브젝트가 signaled가 되는 시점이다.

(또한 OVERLAPPED 구조체도 "전송 또는 수신"과 연결됬다고 봐도 된다. 끝나고 결과를 저장...)

※ 참고로 복습하자면, WSAEventSelect는 "소켓"과 "Event 커널 오브젝트"를 연결시키는 함수였다.

 

Overlapped I/O니까 n개의 전송 또는 수신이 n개의 Event 커널 오브젝트와 연결될 것이다.

n개의 전송 또는 수신은 동시다발적으로 이루어지고

Event 커널 오브젝트도 그에 맞춰서 모두 signaled로 변할 것이다.

 

그렇게 signaled가 된 Event 커널 오브젝트들을 뭘 어째야할까?

바로 지금까지 해왔던 듯이, WaitForMultipleEvents() 함수를 쓰는 것이다.

책에서는 그냥 똑같은 기능을 하는 WSAWaitForMultipleEvents()를 사용했다.

이제 WSAWaitForMultipleEvent()가 리턴하면

모든 overlapped 전송과 수신이 완료됬다는 것을 의미한다.

 

완료된 입출력의 성공/실패 여부, 입출력된 바이트 수 등에 대한 정보를 어떻게 뽑아낼까?

WSAGetOverlappedResult() 함수가 그 기능을 하는 함수다.

 

 BOOL WSAGetOverlappedResult(s, lpOverlapped, lpcbTransfer, fWait, lpdwFlags)

     - s: 입출력이 완료된 소켓의 핸들

     - lpOverlapped: WSASend 혹은 WSARecv 함수 호출시 전달했던

                            OVERLAPPED 구조체 변수의 포인터를 전달한다.

     - fWait: TRUE  >> 전송 또는 수신이 끝날 때까지 블로킹 

                FALSE >> 바로 리턴

     - lpcbTransfer, lpdwFlags는 파라미터를 이용한 일종의 리턴이다. 

     = 리턴: 입출력의 성공적인 완료 시 TRUE 리턴. 입출력이 현재 진행중이거나,

               에러가 발생해서 입출력을 완료하지 못한 경우를 실패로 보아 FALSE 리턴.

     ※ WSAWaitForMultipleEvent()를 이 전에 실행했을 경우,

         입출력이 현재 진행중이어서 FALSE를 리턴하는 케이스는 존재하지 않을 것이다.

         또한 fWait를 TRUE로 설정하더라도 블로킹 되는 케이스는 존재지 않음. 

 

 

★두번째 방법: Completion Routines 기반의 Overlapped I/O

WSASend와 WSARecv의 일곱 번째 전달 인자를 사용해야한다.

전달되는 것은 Completion Routine의 함수 포인터이다. 원형은 다음과 같이 선언한다.

 

void CALLBACK 이름(DWORD dwError, DWORD cbTransferred,

                            LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)

- dwError: 완료된 중첩된 입출력 상태 정보. 정상종료 시에 0. 그 이외는 오류 발생을 의미.

- cbTransferred: 전송된 바이트 수

- lpOverlapped: WSASend, WSARecv 함수 호출 시 전달하는 그 구조체 변수의 포인터

- dwFlags: 중첩된 입출력 함수 호출 시 설정했던 옵션 정보

     ※ dwError와 cdTransferred만 요긴하게 쓰이, 나머지 것들은 별로...

 

일단 CALLBACK의 이름에서 의미하는바와 같이 운영체제가 자신의 필요성에 따라 부르는 함수다.

그렇다면 이제 우리가 알아야할 것은... 이 것을 언제 부르는가?

또 이 함수를 통해서 우리는 얼마만큼의 자유를 가지고 있는가? 등이다.

 

일단 WSARecv에 일곱 번째 전달 인자를 사용하게 될 경우

Event 커널 오브젝트가 signaled가 되는 "조건"이

Event 커널 오브젝트 기반의 Overlapped I/O일 때(첫번째 방법)와는 완전히 다르게 된다.

signaled가 되는 조건은 CompletionRoutine이 가리키는 CALLBACK함수가

한 번 실행되어 종료된 이후이다.

 

그렇다면 CompletionRoutine이 가리키는 CALLBACK함수는 언제 실행될까?

바로 WSAWaitForMultipleEvents()를 실행시켰을 때부터 실행된다.

 

 

< 시나리오 >

WSARecv나 WSASend를 통하여, Event 커널 오브젝트와 CALLBACK 함수 연결

 ↓

WSAWaitForMultipleEvents() 호출 (Event 커널 오브젝트에 관한 정보만 파라미터로 넘김) 

 ↓

만약 전송/수신이 아직 안끝났으면 끝날 때까지 기다린 후

(CALLBACK함수는 전송/수신이 끝나기 전에는 실행대기 상태에 빠진다 - 커널에서 알아서)

 ↓

CompletionRoutine이 가리키는 CALLBACK 함수 실행 시작

 ↓

CompletionRoutine이 가리키는 CALLBACK 함수 종료

 ↓

WSARecv로 CALLBACK함수와 연결시켰던 Event 커널 오브젝트가 signaled가 되는 시점

 ↓

WSAWaitForMultipleEvents() 종료

 

참고로 WSAWaitForMultipleEvents 함수를 대신해서 SleepEx 함수를 사용할 경우

I/O completion callback function(CALLBACK 함수)가 불려질 때까지

블로킹 상태에 빠진다. 이럴 경우 Event 커널 오브젝트를 생성할 필요가 없어진다.

더 깔끔해진다고나할까?

 

DWORD SleepEx(

  DWORD dwMilliseconds// time-out interval
  BOOL bAlertable        // early completion option
);

 


 

/* EXAMPLE */

void CALLBACK CompletionRoutine(DWORD error, DWORD szRecvBytes,

                                                      LPWSAOVERLAPPED lpOverlapped, DWORD flags){
     if(error != 0) //에러 발생 시
          ErrorHandling("CompletionRoutine error");
     recvBytes = szRecvBytes;                //전역 recvBytes    
     buf[szRecvBytes] = 0;                     //전역 buf[]
     printf("수신한 메시지: %s \n", buf);
}

'개발지식창고 > Serial통신' 카테고리의 다른 글

OVERLAPPED  (0) 2011.12.19
비동기 I/O 요청에 대한 완료 통지의 수신  (0) 2011.12.19
Overlapped I/O (2)  (0) 2011.12.19
Serial 통신 Cycle  (0) 2011.11.30
Posted by 모과이IT
,