728x90

IOCP에서의 IO의 완료 확인

- IOCP에서는 완료된 IO를 관리하기 위해 Completion Port 오브젝트 사용

- CP를 사용하기 위해서는 아래의 단계 수행

1. Completion Port 생성

2. Completion Port에 소켓을 등록

3. IO가 완료되면 Completion Port에 등록

4. 등록된 정보를 확인해서 처리

 

IOCP 관련 함수

- CreateIoCompletionPort : CP 오브젝트 생성 및 소켓을 CP에 등록하는 함수

    -> FileHandle : 소켓 핸들 전달

    -> ExistingCompletionPort : CP 전달

    -> CompletionKey : 전달되는 인자 값

    -> NumberOfConcurrentThread : CP 오브젝트에 할당되어 완료된 IO를 동시에 처리할 수 있는 쓰레드의 수 전달. 0 전달 시 동시에 실행 가능한 최대 수

HANDLE CreateIoCompletionPort (HANDLE FileHandle, HANDLE ExistingCompletionPort, 
				ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads);

     -> CP 오브젝트 생성 시

 

hComPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

    -> 클라이언트 소켓 등록

typedef struct    // socket info
{
	SOCKET hClntSock;
	SOCKADDR_IN clntAdr;
} PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

LPPER_HANDLE_DATA handleInfo;
hClntSock=accept(hServSock, (SOCKADDR*)&clntAdr, &addrLen);		  

handleInfo=(LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA));		
handleInfo->hClntSock=hClntSock;
memcpy(&(handleInfo->clntAdr), &clntAdr, addrLen);

CreateIoCompletionPort((HANDLE)hClntSock, hComPort, (DWORD)handleInfo, 0);

- GetQueuedCompletionStatus : 완료된 IO를 확인

    -> CompletionPort : 완료된 IO가 등록된 CP 핸들 전달

    -> lpNumberOfBytes : 송수신된 데이터의 크기를 저장할 변수 주소

    -> lpCompletionKey : CreateIoCompletionPort에서 등록한 정보

    -> lpOverlapped : WSASend, WSARecv 시 전달한 OVERLAPPED 구조체

    -> dwMilliseconds : 타임아웃 정보. INFINITE 전달 시 완료된 IO가 CP에 등록될 때까지 블로킹 상태로 대기

BOOL GetQueuedCompletionStatus (HANDLE CompletionPort, LPDWORD lpNumberOfBytesTransferred, 
		PULONG_PTR lpCompletionKey, LPOVERLAPPED *lpOverlapped, DWORD dwMilliseconds);

 

IOCP 기반의 에코 서버

int main(int argc, char* argv[])
{
	HANDLE hComPort;	
	SYSTEM_INFO sysInfo;
	LPPER_IO_DATA ioInfo;
	LPPER_HANDLE_DATA handleInfo;
    ...;

	hComPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    // CP 생성
	GetSystemInfo(&sysInfo);
    // 시스템 정보

	for(i=0; i<sysInfo.dwNumberOfProcessors; i++)
		_beginthreadex(NULL, 0, EchoThreadMain, (LPVOID)hComPort, 0, NULL);
    // 프로세서 개수만큼 쓰레드 생성 시 CP전달

	hServSock=WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	...;
	
	while(1)
	{	
		SOCKET hClntSock;
		SOCKADDR_IN clntAdr;		
		int addrLen=sizeof(clntAdr);
		
		hClntSock=accept(hServSock, (SOCKADDR*)&clntAdr, &addrLen);	
        
		handleInfo=(LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA));		
		handleInfo->hClntSock=hClntSock;
		memcpy(&(handleInfo->clntAdr), &clntAdr, addrLen);
		// 연결된 클라이언트 정보
        
		CreateIoCompletionPort((HANDLE)hClntSock, hComPort, (DWORD)handleInfo, 0);
        // 연결된 클라이언트 소켓 등록 시 정보도 전달
		
		ioInfo=(LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));
		memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));		
		ioInfo->wsaBuf.len=BUF_SIZE;
		ioInfo->wsaBuf.buf=ioInfo->buffer;
		ioInfo->rwMode=READ;
		// 처리할 때 사용할 정보들 등록

		WSARecv(handleInfo->hClntSock,	&(ioInfo->wsaBuf),	
			1, &recvBytes, &flags, &(ioInfo->overlapped), NULL);			
	}
	return 0;
}
DWORD WINAPI EchoThreadMain(LPVOID pComPort)
{
	HANDLE hComPort=(HANDLE)pComPort;		// 인자로 전달된 CP
	SOCKET sock;
	DWORD bytesTrans;
	LPPER_HANDLE_DATA handleInfo;
	LPPER_IO_DATA ioInfo;
	DWORD flags=0;
	
	while(1)
	{ 
		GetQueuedCompletionStatus(hComPort, &bytesTrans, 
			(LPDWORD)&handleInfo, (LPOVERLAPPED*)&ioInfo, INFINITE);
        // 완료된 IO에 대한 정보를 얻어옴
		sock=handleInfo->hClntSock;

		if(ioInfo->rwMode==READ)
		{
			if(bytesTrans==0)    // EOF 전송 시
			{
				closesocket(sock);
				free(handleInfo); free(ioInfo);
				continue;		
			}

			memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));			
			ioInfo->wsaBuf.len=bytesTrans;
			ioInfo->rwMode=WRITE;
			WSASend(sock, &(ioInfo->wsaBuf), 
				1, NULL, 0, &(ioInfo->overlapped), NULL);
            // Send

			ioInfo=(LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));
			memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));
			ioInfo->wsaBuf.len=BUF_SIZE;
			ioInfo->wsaBuf.buf=ioInfo->buffer;
			ioInfo->rwMode=READ;
			WSARecv(sock, &(ioInfo->wsaBuf), 
				1, NULL, &flags, &(ioInfo->overlapped), NULL);
            // Send 후 다시 Recv 호출하여 수신 대기
		} // 수행한 IO가 READ 였다면
		else
		{
			puts("message sent!");
			free(ioInfo);
		} // 수행한 IO가 SEND 였다면
	}
	return 0;
}
728x90

+ Recent posts