2016년 6월 11일 토요일

뇌자극 TCP_IP 프로그래밍 22강 요약

22강 윈도우 IOCP 프로그래밍

1. IOCP 란?

IOCP의 원리

1) 소켓의 입력을 IOCP에 걸어 놓으면,
2) 소켓에 데이터 입력이 완료된 시점에 이벤트로 통지한다.
3) 데이터를 처리할 워커 스레드가 깨어나서
4) 입력 함수를 호출하고 데이터를 처리한다.

IOCP 지원 소켓 생성

입력 완료 통지는 중첩 소켓의 고유 기능이다. IOCP를 사용하려면 소켓을 중첩 모델로 열어야 한다.
IOCP 흐름은 다음과 같다

1) CP를 만든다. (소켓, 파일의 완료 결과를 전담해서 받아들이는 객체다.)

2) CP는 해당 소켓에서 데이터 입출력이 완료되면 스레드에게 완료 보고를 한다.
    그리고 이 완료 보고를 검사하며 기다리는 워커 스레드를 만든다.

3) 소켓에 입출력 작업이 완료되면 os는 워커 스레드에게 알려준다.

4) 보고 받은 워커 스레드는 입출력 함수를 이용해서 데이터를 처리한다.
    중첩 입출력을 사용하기 때문에 WSARecv, WSASend 함수를 써야 한다.

IOCP 프로그램의 구성

IOCP 프로그램은 기본적으로 2개의 스레드로 구성된다.
메인 스레드는 클라이언트의 연결을 기다렸다가 연결 소켓을 CP에 적용하는 일을,
워커 스레드는 완료 통지를 기다려면서 클라이언트의 데이터를 읽고 처리하는 일을 한다.

2. IOCP 프로그램 개발

CP를 만드려면 CreateIoCompletionPort 함수로 만들 수 있다.

HANDLE WINAPI CreateIoCompletionPort (
  __in HANDLE FileHandle,
  __in_opt HANDLE ExistingCompletionPort,
  __in ULONG_PTR CompletionKey,
  __in DWORD NumberOfconcurrentThreads
);
매개 변수 설명

1) FileHandle : CP에 등록학 파일 핸들이다. 여기에서는 소켓을 사용한다.

2) ExistingCompletionPort : 새로운 CP를 생성하려면 NULL을, 만들어진 CP를 이용하려면 CP의 키 값을 입력한다.

3) CompletionKey : CP를 다루기 위해서 사용하는 CP에 대한 키 값이다.

4) NumberOfconcurrentThreads : CP가 입출력 작업에 얼마나 많은 스레드를 사용할 것인지 설정한다. 명확하지 않으면 0을 설정한다. 0은 os가 자동으로 생성한다.

워커 스레드의 생성

IOCP는 미리 워커 스레드를 만든 다음 입출력이 완료되면 여러 스레드를 중 하나에 작업을 할당한다.
멀티 스레드와 달리 미리 적당한 수의 워커 스레드를 만들어 둔다.
몇 개를 생성할지는 서비스의 종료와 처리 데이터 양에 따라 전적으로 달라진다.
일반적으로 CPU 개수의 2배로 설정한다.

입출력 완료 기다리기와 처리

워커 스레드는 GetQueuedCompletionStatus 함수로 입출력 완료를 기다린다.

BOOL WINAPI GetQueuedCompletionStatus (
  __in HANDLE CompletionPort,
  __out LPDWORD lpNumberOfBytes,
  __out PULONG_PTR lpCompletionKey,
  __out LPOVERLAPPED *lpOverlapped,
  __in DWORD dwMilliseconds
);
매개 변수 설명

1) CompletionPort : CP의 핸들

2) lpNumberOfBytes : 완료된 입출력이 읽고 쓴 데이터의 바이트 크기

3) lpCompletionKey : 유저가 등록한 키값으로 소켓과 소켓을 등록한 CP를 가리킨다.

4) lpOverlapped : OVERLAPPED 구조체를 가리키는 포인터로 완료된 입출력에 대한 정보

5) dwMilliseconds : CP 로 부터 입출력 완료를 기다리는 시간이다. 입출력 완료가 이루어지지 않으면 함수는 실패하고 FALSE를 반환한다.

3. IOCP 기반의 에코 서버 프로그램

소스는 깃허브에 올렸고, 프로그램의 흐름만 적겠다.

1) 중첩 연산 관련 정보를 전달하기 위한 구조체

2) GetQueuedCompletionStatus 함수로 입출력 완료 보고를 기다린다.

3) 만일 읽은 값이 0이라면 소켓을 닫는다.

4) 값을 제대로 읽었다면 WSASend 함수로 데이터를 쓴다. 여기에서는 중첩 속성을 NULL로 했다. 데이터 전송에는 중첩 속성을 사용하지 않아도 된다.

5) 새로 CP를 만든다. 아직 소켓 핸들은 지정하지 않았다.

6) 워커 스레드를 만든다.

7) accept 함수로 새로운 클라이언트 연결을 성공으로 가져왔다면 연결 소켓을 CP에 등록하고, 중첩 입력 연산을 수행한다.

8) 이제 이 소켓에 데이터 입력이 완료되면 os는 CP에 대해서 입출력 완료 보고를 하게 되고, 워커 스레드들 중 하나가 깨어나서 데이터를 처리한다.

소스 코드 받기

댓글 없음:

댓글 쓰기