본문 바로가기

공부/tcp/ip 프로그래밍

23장 IOCP(Input Output Completion Port)

반응형

23장.pptx

 

Overlapped IO를 기반으로 IOCP 이해하기

넌 블로킹 모드의 소켓 구성하기

SOCKET hLisnSock;

int mode = 1;

...

hLisnSock = WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

ioctlsocket(hLisnSock, FIONBIO, &mode); // for non-blocking socket

 

핸들 hLisnSock이 참조하는 소켓의 입출력 모드(FIONBIO)를 변수 mode에 저장된 값의 형태로 변경함

FIONBIO는 소켓의 입출력 모드를 변경하는 옵션, 함수의 세 번째 인자로 전달된 주소 값의 변수에 0이 저장되어 있으면 블로킹 모드로, 0이 아닌 값이 저장되어 있으면 넌-블로킹 모드로 소켓의 입출력 속성을 변경

넌-블로킹 모드가 되면 추가되는 특징

1. 클라이언트의 연결요청이 존재하지 않는 상태에서 accept함수가 호출되면 INVALID_SOCKET이 곧바로 반환

그리고 이어서 WSAGetLastError함수를 호출하면 WSAEWOULDBLOCK가 반환됨

2. accept 함수호출을 통해서 새로 생성되는 소켓 역시 넌-블로킹 속성을 지님

 

 서버 구현방식

동작원리
1. 클라이언트가 연결되면 WSARecv함수를 호출하면서 넌-블로킹 모드로 데이터가 수신되게 함
2. 수신이 완료되면 ReadCompRoutine함수가 호출
3. ReadCompRoutine함수가 호출되면 WSASend 함수를 호출하면서 넌-블로킹 모드로 데이터가
송신
4. 송신이 완료되면 WriteCompRoutine함수가 호출
5. 호출된 WriteCompRoutine함수는 다시 WSARecv함수를 호출하면서 넌-블로킹 모드로 데이터의
수신을 기다림

입출력 완료 시 자동으로 호출되는 Completion Routine 내부로 클라이언트 정보(소켓과 버퍼)를
전달하기 위해서 WSAOVERLAPPED 구조체의 멤버 hEvent를 사용

 

클라이언트 구현방식

클라이언트 구현
tcp의 전송특성(경계가 존재 안 함)을 고려해서 수신해야 할 데이터가 완전히 수신될 때까지
recv함수 반복 호출

 

IOCP 구조 

1. main함수

쓰레드 생성(io완료후 처리를 위해서)

입출력을 시작

2. 쓰레드함수

입출력이 완료된 소켓과 그에 대한 내용을 알아냄

입출력이 완료된 후의 그에 대한 처리

 

CP오브젝트와 소켓, 소켓정보, io정보가 연결된 모습

 

코딩 순서에 따른 함수의 사용과 그때에 따른 정보의 모습 및 흐름

CP(Completion Port) 오브젝트의 생성함수

#include <windows.h>

 

HANDLE CreateIoCompletionPort(

HANDLE FileHandle, HANDLE ExistingCompletionPort, ULONG_PTR CompletionKey,

DWORD NumberOfConcurrentThreads);

성공 시 CP 오브젝트의 핸들, 실패 시 NULL 반환

 

FileHandle : CP 오브젝트 생성시에는 INVALID_HANDLE_VALUE를 전달

ExistingCompletionPort : CP 오브젝트 생성시에는 NULL 전달

CompletionKey : CP 오브젝트 생성시에는 0 전달

NumberOfConcurrentThreads : CP 오브젝트에 할당되어 완료된 IO를 처리할 쓰레드의 수를 전달

                                           예를 들어 2가 전달되면 CP 오브젝트에 할당되어 동시 실행 가능한

                                           쓰레드의 수는 최대 2개로 제한된다.

                                           그리고 이 인자에 0이 전달되면 시스템의 CPU 개수가 동시 실행 가능한

                                           쓰레드의 최대수로 지정

 

CP(Completion Port) 오브젝트와 소켓의 연결함수

#include <windows.h>

 

HANDLE CreateIoCompletionPort(

HANDLE FileHandle, HANDLE ExistingCompletionPort, ULONG_PTR CompletionKey,

DWORD NumberOfConcurrentThreads);

성공 시 CP 오브젝트의 핸들, 실패 시 NULL 반환

 

FileHandle : CP 오브젝트에 연결할 소켓의 핸들 전달

ExistingCompletionPort : 소켓과 연결할 CP 오브젝트의 핸들 전달

CompletionKey : 완료된 IO 관련 정보의 전달을 위한 매개변수(소켓의 정보(socket info)를 전달)

NumberOfConcurrentThreads : 어떠한 값을 전달하건, 이 함수의 두 번째 매개변수가 NULL이 아니면

                                            그냥 무시

 

CP에 등록되는 완려된 IO의 확인함수

#include <windows.h>

 

BOOL GetQueuedCompletionStatus(

HANDLE CompletionPort, LPDWORD lpNumberOfBytes, PULONG_PTR lpCompletionKey,

LPOVERLAPPED* lpOverlapped, DWORD dwMilliseconds);

성공 시 TRUE, 실패 시 FALSE 반환

 

CompletionPort : 완료된 IO 정보가 등록되어 있는 CP 오브젝트의 핸들 전달

lpNumberOfBytes : 입출력 과정에서 송수신 된 데이터의 크기정보를 저장할 변수의 주소 값 전달

lpCompletionKey : CreateIoCompletionPort 함수의 세 번째 인자로 전달된 값의 저장을 위한 변수의

                          주소 값 전달 (소켓의 정보(socket info) 저장을 위해서 사용됨)

lpOverlapped : WSASend, WSARecv 함수호출 시 전달하는 OVERLAPPED 구조체 변수의 주소 값이

                     저장될, 변수의 주소 값 전달(완료된 IO에 대한 정보(buffer info) 저장을 위해 사용됨)

dwMilliseconds : 타임아웃 정보전달, 여기서 지정한 시간이 완료되면 FALSE를 반환하면서 함수를

                         빠져나가며, INFINITE를 전달하면 완료된 IO가 CP 오브젝트에 등록될 때까지 블로킹

                         상태에 있게 된다

 

socket info

typedef struct

{

SOCKET hClntSock;

SOCKADDR_IN clntAdr;

} PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

 

buffer info

typedef struct

{

OVERLAPPED overlapped;

WSABUF wsaBuf;

char buffer[BUF_SIZE];

int rwMode; //READ or WRITE

} PER_IO_DATA, *LPPER_IO_DATA;

 

*코딩시 주의점

WSARecv(handleInfo->hClntSock, &(ioInfo->wsaBf), 1 &recvBytes, &flags,
&(ioInfo->overlapped), NULL);
WSARecv 함수를 호출하면서 일곱 번째 인자로 OVERLAPPED 구조체 변수의 주소 값을 전달하였다
이 값은 이후에 GetQueued… 함수가 반환을 하면서 얻을 수 있다 그런데 구조체 변수의 주소 값은
첫 번째 멤버의 주소 값과 동일하므로 PER_IO_DATA 구조체 변수의 주소 값을 전달한 것과 같음

 

GetQueuedCompletionStatus(hComPort, &bytesTrans, (LPDWORD)&handleInfo,
(LPOVERLAPPED*) &ioInfo, INFINITE);
포인터 iofo에 저장된 값은 OVERLAPPED 구조체 변수의 주소 값이지만, PER_IO_DATA 구조체 변수의
주소 값이기도 하다. 따라서 멤버 rwMode에 저장된 값의 확인을 통해서 입력의 완료인지 출력의
완료인지를 확인하고 있다

반응형