코딩/게임서버공부

[CS] Select 모델과 IOCP 모델

이즈99 2024. 6. 4. 10:58
728x90

Select 모델

- select모델의 개념

select 모델이란 select 함수를 주로 사용하는 I/O 기반 소켓 프로그래밍이다.

select 모델을 사용하면 소켓 모드(블로킹, 넌블로킹)에 관계없이 여러 소켓을 한 스레드로 처리할 수 있다.

 

-select모델의 특징

select 모델은 소켓 함수 호출이 성공할 시점을 미리 알 수 있다는 특징이 있다.

함수를 호출할 수 있는지에 대한 여부를 미리 확인하기 때문에 가능하다.

수신/송신 버퍼에 데이터가 비었는데/꽉 찼는데 read/write 하는 상황이 기존 소켓 프로그래밍에 있었다.

select모델은 이 상황을 미리 확인 하여 예방할 수 있다.

또한 Select 모델은 여러 소켓에 대해 함수 호출 시점(또는 호출 결과)을 알려주는 역할을 할 뿐 소켓 정보를 관리해주지는 않는다. 따라서 각 소켓에 필요한 정보(응용 프로그램 버퍼, 송/수신 바이트 정보 등)을 관리하는 기능은 응용 프로그램이 구현해야 한다.

 

-select 모델의 장점

  • 블로킹의 경우
    조건이 만족되지 않아서 블로킹 되는 상황을 예방
  • 논 블로킹의 경우
    조건이 만족되지 않아서 불필요한 반복 체크를 예방

-select 모델의 단점

  • while()을 통해 반복해 주어야 한다.
  • set에 한번에 등록할 수 있는 소켓의 개수가 매우 적다.(64개까지만 소켓의 등록가능하고 초과한다면 추가적인 set을 적용하고 관리 해줘야 한다.)

IOCP 모델

- IOCP모델의 개념

Input/Ouptput Completion Port의 약자다. 입력과 출력의 완료를 담당할 포트를 지정해서 처리하겠다는 의미다. 입력과 출력의 완료시점에서의 통지는 overlapped(중첩 입출력)에서 처리가 되므로, 이 기술은 윈도의 중첩 입출력 기술을 확장시킨 것으로 볼 수 있다.
(간단히 말하면 윈도우에서 제공하는 비동기 IO 라이브러리로 생각하면 될것이다.)

 

-IOCP모델 동작 순서

1. 쓰레드 폴(미리 쓰레드를 할당) 을 이용하여 생성한다.

2. 비동기 I/O 시작

동기 함수(Connect Close Accept Send)과 같은 역활을 하는 비동기 함수들을 실행해서 윈도우 I/O에 남긴다. 이 동작을 거친 뒤에는 프로그램의 흐름이 다시 호출한 쓰레드로 바로 돌아온다. (비동기)

3. 비동기 입출력 완료

비동기 입출력이 윈도우 I/O에서 종료되면 IOCP라는 Port(항구)에 Queue자료구조로 쌓이게 된다.

 

 

4. GetQueuedCompletionStatus

쓰레드 중에서 작업이 끝난, 그러니까 할 일이 없는 쓰레드에서 GetQueuedCompletionStatus를 호출하면 IOCP에서 완료된 내용을 꺼내 받을 수 있다.

-IOCP 작동코드 예시

while (true)
{
    DWORD dwTransferred = 0;
    OverlappedIOContext* context = nullptr;
    ClientSession* asCompletionKey = nullptr;  //키로 세션을 활용

    int ret = GetQueuedCompletionStatus( 
        hComletionPort , &dwTransferred, 
        (PULONG_PTR)&asCompletionKey, (LPOVERLAPPED*)&context, GQCS_TIMEOUT );
    DWORD errorCode = GetLastError();

    //time out처리
    if (ret == 0 &&  errorCode == WAIT_TIMEOUT)
        continue;
    //기타 에러 처리
        ...

        //overlapped 구조체에 I/O 타입을 전달하는 방법
    switch (context->mIoType)
    {
        //각 I/O에 대응하는 완료함수를 불러서 처리한다.
    case IO_SEND:
        completionOk = SendCompletion(asCompletionKey, context, dwTransferred);
        break;

    case IO_RECV:
        completionOk = ReceiveCompletion(asCompletionKey, context, dwTransferred);
        break;
    }
}bool ReceiveCompletion(const ClientSession* client, 
                       OverlappedIOContext* context, 
                       DWORD dwTransferred)
{
    /// echo back 처리 client->PostSend()사용.
    bool result = true;
    if( !client->PostSend( context->mBuffer , dwTransferred ) )
    {
        printf_s( "PostSend error: %d\n" , GetLastError() );
        delete context;
        return false;
    }

    delete context;
    return client->PostRecv();
}
출처: https://ozt88.tistory.com/23 [공부 모음:티스토리]

 

-select모델을 사용하지 않고 IOCP를 사용하는 이유

가장 큰 이유는 select 모델의 경우 소켓의 양이 한정되어있는 것이 가장 크다. 64개가 한계치고 한계치를 넘기기가 매우 힘들기 때문에 굳이 대용량 정보가 왔다가야하는 서버의 경우 IOCP 모델을 사용한다.