반응형

//////////////// IOCP(Input Output Completion Port) /////////////
기본적으로 Overlapped I/O 방식에 Thread Pool이라는 것이 합쳐저 있는 것을 말한다.
Thread Pool이라는 것은, 일정 개수의 쓰레드가 미리 만들어져 대기하고 있다가 필요할 때 대기중인 쓰레드가 작업을 처리하는 방식

// Completion Port 기능 //
Completion Port 오브젝트를 생성하고, 이 오브젝트를 이용하여 지정된 수만큼의 스레드들이 Overlapped I/O를 처리하게 된다.

1. 기본적으로 비동기 입출력 모델이다.(Overlapped I/O)
2. 제한된 쓰레드의 수를 통해서 여러 소켓의 입출력을 담당하게된다.
3. 컨텍스트 스위칭에 소비되는 시간을 줄이는 모델이다.
4. Overlapped 입출력 모델의 특징과 비동이 Notification입출력 모델의 특징을 동시에 지닌다.

다중 접속 서버의 경우 여러 클라이언트가 동시 접속을 해 오는 경우 일반적으로 클라이어트 수만큼 쓰레드를 생성한다.
Overlapped I/O 모델을 사용하면 입,출력을 중첩시켜서 쓰레드의 수를 줄여 볼 수도 있지만 한계가 있다.

쓰레드의 수를 왜 줄여야 할까?
1. 생성 및 소멸 자체가 부담이 되는 작업
2. 쓰레드의 수가 많으면 CPU의 할당 시간을 더 작게 쪼개서 나눠 가져야 하기 때문에 '컨텍스트 스위칭'이 빈번하게 발생한다.

// Completion Port 입출력 기반의 서버 구현 순서 //
1. 항구를 구한다.
- Completion Port 오브젝트를 생성한다.

2. 일꾼을 구한다.
- Completion Port 오브젝트와의 연결을 한다
- Completion Port 오브젝트에 연결되어 있는 Overlapped 소켓이 중첩된 입,출력을 완료 했을 경우 적절한 처리를 할 쓰레드를
생성해 놓는다.

3. 배를 만들어서 정착해야 할 항구를 지정한다.
- 클라이언트가 연결 요청을 할 때마다 소켓을 생성하게 되는데, 이 소켓을 Completion Port 오브젝트와 연결해 줘야 한다.
그래야 중첩된 입,출력을 완료 했을 때 항구에서 대기중인 쓰레드가 이를 감지하고 적절한 처리를 할 수 있게 된다.

// Completion Port 오브젝트 생성 //
1. Completion Port 오브젝트의 생성
2. 이미 생성된 Completion Port 오브젝트에 소켓을 연결시켜주는 기능

HANDLE CreateIoCompletionPort(
HANDLE FileHandle,   // 연결 시킬 소켓 핸들
HANDLE ExistingCompletionPort,  // 연결 시킬 Completion Port핸들
ULONG_PTR CompletionKey,  // Completion Key -> Completion packet에 저장되는 Data
     // Packet에 추가적인 정보를 넣을 수 있다.
     // Completion Port에 연결되는 소켓에 관련된 정보를 쓰레드에 전달하기
     // 위한 용도로 사용된다.
DWORD NumberOfConcurrentThreads  // 동시 실행 가능한 쓰레드의 수
)
--> 성공시 Completion Port 오브젝트 핸들, 실패시 NULL 리턴

ex)
hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
리턴된 핸들은 나중에 소켓에 Completion Port를 적용할 때 이용된다.
// Completion 소켓과 Completion Port의 연결 //
ex)
CreateIoCompletionPort((HANDLE)hClntSock, hCompletionPort, (DWORD)PerHandleData, 0);

// Worker Thread의 생성 및 할당 //
단 하나의 소켓만 Completion Port에 할당하여, 여러 개의 Overlapped 소켓 입,출력을 담당하게 하는 경우,
쓰레드 내에서 어떻게 입,출력을 완료한 소켓을 알아내가 할 수 있을까?
--> 입,출력의 완료를 확인할 수 있는 방법이 제공되어야 한다.

// Completion Queue에 들어있는 패킷 정보 확인하는 함수
BOOL GetQueuedCompletionStatus(  // 쓰레드가 호출하는 함수
HANDLE CompletionPort,   // Completion Port의 핸들
LPDWORD lpNumberOfBytes,  // 실제로 전송된 byte수
PULONG_PTR lpCompletionKey,  // file Completion Key
LPOVERLAPPED *lpOverlapped,  // WSASend, WSARecv 함수 호출 시 전달하는 OVERLAPPED
     // 구조체 변수의 포인터를 얻기 위해서 사용
DWORD dwMilliseconds   // INFINITE: 입출력이 완료될 때 까지 블로킹

// Completion Port model을 이용하여 에코서버를 만드는 순서 //
1. Completion Port 오브젝트를 생성한다. NumberOfConcurrentThreads 파라미터는 0으로 설정하여 프로세스 하나당 하나의 스레드가 Completion Port를 처리하도록 지정한다.
2. 시스템의 정보를 얻어온다(시스템에 몇 개의 프로세서가 있는지 확인한다.
SYSTEM_INFO SystemInfo

GetSystemInfo(&SystemInfo)
3. 2에서 얻은 시스템 정보를 이용하여 I/O를 요청할 쓰레드를 생성한다.
4. 9000 포트로 연결을 기다리는 소켓을 생성한다.
5. accept 함수를 이용하여 들어오는 연결 요청을 받아들인다.
6. 소켓 핸들별 데이터 구조체를 생성하고 소켓 핸들을 구조체에 저장한다.
7. CreateCompletionPort 함수를 호출하여 accept에 의하여 리턴된 소켓을 Completion Port에 할당한다.
이 때 6에서 생성한 구조체를 키값(Completion Key)으로 입력한다.
8. 소켓 연결에 대하여 I/O 작업을 수행한다. 먼저 WSARecf나 WSASend를 이용하여 한 번 I/O작업을 요청하면 그 다음부터는 3과정에서 생성한 스레드들이 I/O작업을 계속해서 요청할 것이다.
9. 종료될 때까지 5 ~ 8의 과정을 반복한다.

 
반응형

+ Recent posts