반응형

앞서 봤던 select 모델들은 비동기/봉쇄(blocking) 모델이었지만 이번에 배울

Overlapped 모델(이벤트 기반)은 비동기/비봉쇄(non-blocking)의 응용 모델이다.

 


Overlpapped IO

 

overlapped io (비동기 + 논블로킹)

  • overlapped 함수를 건다 (wsarecv, wsasend)
  • overlapped 함수가 성공했는지 확인 후
  • 성공했으면 결과 얻어서 처리
  • 실패했으면 사유를 확인

char sendbuffer[100];
wsabuf wsabuf[2];
wsabuf[0].buf = sendbuffer;
wsabuf[0].len = 100;

char sendbuffer2[100];
wsabuf[1].buf = sendbuffer2;
wsabuf[1].len = 100;


scatter-gather

 

  • 패킷을 보낼 때 분산되어 있는 패킷을 연결시켜서 하나의 덩어리로 만들어 보내준다.

 

 

 

WSASend, WSARecv

 

  1. 비동기 입출력 소켓
  2. WSABUF배열의 시작주소 + 개수
  3. 보내고/받은 바이트 수
  4. 상세 옵션인데 일단 0
  5. WSAOVERLAPPED구조체 주소값
  6. 입출력이 완료되면 os가 호출할 콜백 함수

WSASend 와 WSARecv는 비동기이다 보니 우리가 호출한 시점과 실제로 호출된 시점이 다를 수도 있다.
그렇기 때문에 WSASend나 WSARecv 요청을 하였을 때 가만히 놔두어야 한다.

 

 

 

 

Overlapped모델 (이벤트 기반) 동작 순서

 

  1. 비동기 입출력 지원하는 소켓 생성 + 통지 받기 위한 이벤트 객체 생성
  2. 비동기 입출력 함수 호출 (1에서 만든 이벤트 객체를 같이 넘겨줌)
  3. 비동기 작업이 바로 완료되지 않으면, WSA_IO_PENDING오류 코드
  4. 운영체제는 이벤트 객체를 singaled 상태로 만들어서 완료 상태 알려줌
  5. WSAWaitForMultipleEvents함수 호출해서 이벤트 객체의 signal 판별
  6. WSAGetOverlappedResult호출해서 비동기 입출력 결과 확인 및 데이터 처리

 

 

 

WSAGetOverlappedResult

 

  1. 비동기 소켓
  2. 넘겨준 Overlapped구조체
  3. 전송된 바이트 수
  4. 비동기 입출력 작업이 끝날때까지 대기할지 선택
  5. 비동기 입출력 잡업 관련 부가 정보. 거의 사용 안함.

 

//Server

struct Session
{
	SOCKET socket = INVALID_SOCKET;
	char recvBuffer[BUFSIZE] = { };
	int32 recvBytes = 0;
	int32 sendBytes = 0;
	WSAOVERLAPPED overlapped = {};
};

.
.
.
    
    while (true)
	{
		SOCKADDR_IN clientAddr;
		int32 addrLen = sizeof(clientAddr);

		SOCKET clientSocket;

		while (true)
		{
			clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);

			if (clientSocket != INVALID_SOCKET)
				break;

			if (::WSAGetLastError() == WSAEWOULDBLOCK)
				continue;

			//문제 있는 상황
			return 0;
		}

		Session session = Session{ clientSocket };
		WSAEVENT wsaEvent = ::WSACreateEvent();
		session.overlapped.hEvent = wsaEvent;

		cout << "Client Connected!" << endl;

		while (true)
		{
			WSABUF wsaBuf;
			wsaBuf.buf = session.recvBuffer;
			wsaBuf.len = BUFSIZE;

			DWORD recvLen = 0;
			DWORD flags = 0;

			if (::WSARecv(clientSocket, &wsaBuf, 1, &recvLen, &flags, &session.overlapped, nullptr) == SOCKET_ERROR)
			{
				if (::WSAGetLastError() == WSA_IO_PENDING)
				{
					//Pending
					::WSAWaitForMultipleEvents(1, &wsaEvent, TRUE, WSA_INFINITE, FALSE);
					::WSAGetOverlappedResult(session.socket, &session.overlapped, &recvLen, FALSE, &flags);
				}
				else
				{
					//TODO : 문제 있는 상황
					break;
				}
			}

			cout << "Data Recv Len = " << recvLen << endl;
		}

		::closesocket(session.socket);
		::WSACloseEvent(wsaEvent);
	}

 

 

	//Client
    
    WSAEVENT wsaEvent = ::WSACreateEvent();
	WSAOVERLAPPED overlapped = {};
	overlapped.hEvent = wsaEvent;

	while (true)
	{
		WSABUF wsaBuf;
		wsaBuf.buf = sendBuffer;
		wsaBuf.len = 100;

		DWORD sendLen = 0;
		DWORD flags = 0;
		if (::WSASend(clientSocket, &wsaBuf, 1, &sendLen, flags, &overlapped, nullptr) == INVALID_SOCKET)
		{
			if (::WSAGetLastError() == WSA_IO_PENDING)
			{
				//Pending
				::WSAWaitForMultipleEvents(1, &wsaEvent, TRUE, WSA_INFINITE, FALSE);
				::WSAGetOverlappedResult(clientSocket, &overlapped, &sendLen, FALSE, &flags);
			}
			else
			{
				//진짜 문제 있는 상황
				break;
			}
		}

		cout << "Send Data! Len = " << sendBuffer << endl;

		this_thread::sleep_for(1s);
	}
반응형

'네트워크 프로그래밍' 카테고리의 다른 글

IOCP 모델  (0) 2023.02.24
Overlapped 모델 (콜백 기반)  (0) 2023.02.24
비동기 vs 동기, Blocking vs Non-Blocking  (0) 2023.02.23
WSAEventSelect Model  (0) 2023.02.23
Select Model  (0) 2023.02.20