동기화와 비동기화 --------------------------------------------------------------------------------------------------



일단 동기화에 대한 설명

1. 동기화란 일단 실행순서의 동기화 개념으로 이해하면 편하다.

2. 간단히 설명하자면 함수는 함수내의 연산이 끝나기 전에는 함수는 반환되지 않는다.

3. 단순하게 본다면 당신의 프로그램은 정지한다. (물론 파일을 읽어오는 연산을 열심히 하고 있기는 하겠지만.)

4. 10기가짜리 파일을 읽어오는 연산은 10분이 걸린다고 치자. 10기가짜리 파일을 처음부터 끝까지 다 읽어야 함수가 반환된다.

5. 당신의 컴퓨터는 10분동안 정지한다.


그렇다면 비동기화란?

1. 비동기화란 하나의 실행흐름을 또 만들어내는 작업이라고 생각해보자. 그리고 그것을 OS에서 지원해준다고 생각해보자.

2. 10기가짜리 파일을 읽어오는 연산은 10분이 걸린다고 치자. 10기가짜리 파일을 처음부터 끝까지 다 읽어야 함수가 반환된다.

3. 당신의 컴퓨터는 CPU에 파일을 읽어오는 연산에 대한 명령을 내린다.

4. 당신의 컴퓨너는 한편으로는 10기가짜리 파일에 대한 입출력 연산을 하면서 동시에 당신의 프로그램또한 실행시킬 것이다.

5. 10기가짜리 파일 입출력과 이후의 프로그램은 함께 실행될 것이다.


이해가 가는가?


Asynchronous(비동기) Notification(알림) IO 방식 ---------------------------------------------------------------------


비동기로 알림을 처리한다?

무슨 말인지 선뜻 이해가 안간다면 RECV와 함수를 좀더 생각해 보자.

RECV함수는 한번 실행되면 신호가 있건 없건 연락이 올때까지 무한정 기다린다.

하지만 이것을 자신의 입력버퍼에 데이터가 있을때만 체크해준다면 좀더 중간중간의 입출력에 관련된 작업에 효율을 기할 수 있지 않을까?

즉 상대가 데이터를 분명히 보내서  꼭 필요할때만 RECV함수를 호출할 수 없는가? 라는 것이다.

이러려면 소켓의 상태를 파악해줄 방법이 필요하다. 이에 대한 방법으로는 2가지 방법이 가능한다.


WSAEventSelect

WSAAsyncSelect

두가지 함수를 이용하는 것이며 책에서는 WSAEventSelect의 방식이 나와있으므로 그에 대해서 설명한다.


WSAEventSelect를 이용한 방식이란 윈도우에서는 이벤트라는 쓰레드 동기화용 커널오브젝트를 생성하는 기능이 이를 소켓에 적용하여 소켓의 신호호를 이벤트로 보고 처리하는 방식을 말한다.


이 비동기 방식에는 다음의 단계를 거치게 된다.


1. 이벤트와 소켓의 연결 -> 변화가 생긴 소켓을 체크하기 위해서 이벤트와 소켓을 하나로 엮는 방법을 알아야 한다.

2. 이벤트를 통한 소켓의 상태 체크 -> 엮여진 소켓들을 체크해가면서 실제 변화가 있는 녀석이 있는지 감시해야 한다.

3. 이벤트의 종류 판별 -> 소켓에 발생한 이벤트가 무엇인지 판단해야하고 그에 따라 분류해서 처리해줘야 한다.


1. 이벤트와 소켓의 연결.


int WSAEventSelect(

  _In_  SOCKET s,                        -> 이벤트와 연결된 소켓을 인자로 준다.

  _In_  WSAEVENT hEventObject,   -> 소켓과 연결한 이벤트핸들을 설정한다.

  _In_  long lNetworkEvents           -> 감시하고자하는 이벤트의 유형 정보를 전달한다.

);


세번째 인자의 이벤트 정보값

FD_READ -> 수신할 데이터가 존재하는가?

FD_WIRTE -> 블로킹 없이 데이터 전송이 가능한가?

FD_OOB -> Out-of-band 데이터가 수신되었는가?

FD_ACCEPT -> 연결요청이 있었는가?

FD_CLOSE -> 연결의 종료가 요청되었는가?


2. 이벤트를 통한 소켓의 상태 체크


DWORD WSAWaitForMultipleEvents(

  _In_  DWORD cEvents, -> signaled 상태로의 전이여부를 확인한 Event 오브젝트 개수 정보 전달.

  _In_  const WSAEVENT *lphEvents, -> Event 오브젝트의 핸들을 저장하고 있는 배열의 주소 값 전달.

  _In_  BOOL fWaitAll, -> TRUE시 모든 Event가 signaled 상태일 때 반환, FALSE 전달시 하나만 signaled 상태가 되어도 반환.

  _In_  DWORD dwTimeout, -> TRUE 전달시, alertable wait 상태로 진입

  _In_  BOOL fAlertable -> 반환된 정수 값에서 상수값 WSA_WAIT_EVENT_0을 빼면, 두 번째 매개변수로 전달된 배열을 기준으로, signaled 상태가 된 event오브젝트의 핸들이 저장된 인덱스가 계산된다. 만약 둘 이상의 Event 오브젝트가 signaled 상태로 전이 된다면, 그중 작은 인덱스 값이 계산되고 타임아웃이 발생하면 WAIT_TIMEOUT가 반환된다.

);


3. 이벤트의 종류 판별


이벤트 종류 판별용 구조체.
int WSAEnumNetworkEvents(
  _In_   SOCKET s,                            -> 이벤트를 체크할 소켓을 확인한다.
  _In_   WSAEVENT hEventObject,       -> 소켓과 연결된 이벤트를 넣는다.
  _Out_  LPWSANETWORKEVENTS lpNetworkEvents -> 이벤트의 분류된 정보를 넣어줄 구조체를 넣는다.
);

세번째 인자값에 들어갈 구조체의 내용을 확인해보자.
typedef struct _WSANETWORKEVENTS {

       long lNetworkEvents; -> 이벤트의 종류

       int iErrorCode[FD_MAX_EVENTS]; -> 에러코드를 담을 배열

} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;


두번째 값인 iErrorCode[FD_MAX_EVENTS];의 내용를 확인해보자.

FD_READ -> 수신할 데이터가 존재하는가?

FD_WIRTE -> 블로킹 없이 데이터 전송이 가능한가?

FD_OOB -> Out-of-band 데이터가 수신되었는가?

FD_ACCEPT -> 연결요청이 있었는가?

FD_CLOSE -> 연결의 종료가 요청되었는가?

위와 같은 옵션중 한가지에 오류가 발생한다면 그 배열의 순서의 비트를 0이 아닌 값으로 채워 넣는다.


FD_READ_BIT      0

FD_WRITE_BIT     1

FD_OOB_BIT       2

FD_ACCEPT_BIT    3

FD_CONNECT_BIT   4

FD_CLOSE_BIT     5

이런식으로 처리가 되는 것이다.

Posted by JJOREG