게임서버

게임서버) 8. Select모델

PJNull 2023. 1. 13.
728x90
반응형

select 모델

select모델은 소켓함수 호출을 미리 알 수 있다면 블로킹/논블로킹에서 했던 불필요한 것을 하지 않아도 된다는 것이다.

 

 

select모델 순서

 

0.소켓 셋 초기화

1.ListenSocket(관찰 대상)  등록(읽기,쓰기,예외 설정)

2.세션 소켓 등록

3.Select 옵션 설정(관찰 시작 하나 이상의 소켓이 준비되면 리턴->나머지는 알아서 제거됨(readSet,writeSet,exceptSet))

4.남은 소켓 체크해서 진행

 

 

Select모델 생성

 

 

0.소켓 셋 초기화 및 세션생성

 

먼저 세션과 fd_set을 생성해준다.

struct Session  //클라이언트의 정보를 서버로 가져오는 역할
{
	SOCKET socket=INVALID_SOCKET;
    char recvBuffer[1000]={};
    int32 recvBytes=0;
};

vector<Session>sessions;

fd_set reads;
fd_set writes;

다음으로 스켓셋을 초기화해준다.

while(true)//select모델
{
	FD_ZERO(&reads);
}

 

 

1.ListenSocket등록

 

FD_SET(listenSocket,&reads);

 

2.세션 소켓 등록

 

for(Session& s:sessions)
{
	FD_SET(s.socket,&reads);
}

 

3.Select옵션 설정

 

int32 retval=::select(0,&reads,nullptr,nullptr,nullptr);

if(FD_ISSET(listensocket,&reads))
{
	SOCKADDR_IN clientAddr;
   	int32 AddrLen=sizeof(clientAddr);
   	SOCKET clientSocket=::accept(listenSock,(SOCKADDR*)&clientAddr,&AddrLen);
    
    if(clientSocket!=INVALID_SOCKET)
    {
    	if(::WSAGetLastError()==WSAEWOULDBLOCK)
        {
        	//TODO
            sessions.push_back(Session{clientSocket});
        }
        
    }
    
}

 

4.남은 소켓 체크해서 진행

 

if (FD_ISSET(listenSocket, &read))
{
	SOCKADDR_IN clientAddr;
	int32 clientaddrLen = sizeof(clientAddr);
	SOCKET clientSocket=::accept(listenSocket,(SOCKADDR*)&clientAddr,&clientaddrLen);
			
	if (clientSocket != INVALID_SOCKET)
	{
		cout << "Client Connect!" << endl;
		sessions.push_back(Session{clientSocket});
	}
}

 

Select모델의 한계

 

소켓이 set상태인지 항상 확인을 해야되며, fd_set의 최대숫자가 64이기 때문에 많은 클라이언트를 받아 들이기에는 다소 부적절하다.

 

 

WSAEventSelect

 

소켓과 관련된 네트워크 이벤트들을 이벤트객체를 통해 감지한다. 관찰대상을 설정하고 Select를 통해 제거하던 윗 방식과는 달리 이벤트와 소켓을 연동하여 이벤트를 감지하는 형태의 모델이다. 

 

 

WSAEventSelect의 순서 및 이벤트 종류

 

생성:WSACreateEvent(수동리셋+NonSignaled상태)

신호 상태 감지:WSAWaitForMultipleEvents

구체적인 네트워크 이벤트:WSAEnumNetworkEvents

클라이언트 세션 소켓 체크

삭제:WSACloseEvent

 

 WSAEventSelect생성 

 

 

1.세션과 이벤트객체 생성

 

	SOCKADDR_IN ClientAddr;
	int32 addrLen = sizeof(ClientAddr);
	
	vector<WSAEVENT> wsaEvent;
	vector<Session>sessions;
    
	WSAEVENT listenEvent = ::WSACreateEvent();
   	wsaEvent.push_back(listenEvent);
	sessions.push_back(Session{ listenSocket });

 

 

2.신호 상태 감지

 

 

if (::WSAEventSelect(listenSocket, listenEvent, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)return 0;
//이벤트와 소켓을 1:1 맵핑시킨후 소켓을 대상으로 이벤옵션에 대해서 감지하겠다를 설정	
while(true)
{

	int32 index = ::WSAWaitForMultipleEvents(wsaEvent.size(), &wsaEvent[0], FALSE, WSA_INFINITE, FALSE);
	//다수의 이벤트를 동시에 관찰하고 하나라도 감지되었을 경우 index에 반환한다.
	if (index == WSA_WAIT_FAILED)continue;
	index -= WSA_WAIT_EVENT_0;
}

 

 

3.구체적인 네트워크 이벤트

 

 

WSANETWORKEVENTS networkEvent;
if (::WSAEnumNetworkEvents(sessions[index].socket, wsaEvent[index], &networkEvent) == SOCKET_ERROR)continue;

if (networkEvent.lNetworkEvents & FD_ACCEPT)
		{
			if (networkEvent.iErrorCode[FD_ACCEPT_BIT] != 0)continue;
			

			SOCKET clientSocket = ::accept(listenSocket,(SOCKADDR*)&ClientAddr,&addrLen);
			if (clientSocket != INVALID_SOCKET)
			{
				//TODO
                
                WSAEVENT clientEvent = ::WSACreateEvent();
				wsaEvent.push_back(clientEvent);
				sessions.push_back(Session{clientSocket});

				if (::WSAEventSelect(clientSocket, clientEvent, FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR)return 0;
				//이벤트 맵핑
            }

		}

 

 

4.클라이언트 세션 소켓 체크

 

if (networkEvent.lNetworkEvents & FD_READ)
{
	if (networkEvent.iErrorCode[FD_READ_BIT] != 0)continue;
	Session& s = sessions[index];
	int32 recvLen = recv(s.socket,s.recvbuffer,BUF_SIZE,0);

	if (recvLen == SOCKET_ERROR && ::WSAGetLastError() != WSAEWOULDBLOCK)
	{
		if (recvLen <= 0)continue;
	}
	//TODO
			
}
728x90
반응형

'게임서버' 카테고리의 다른 글

게임서버) 11. IOCP Core  (0) 2023.01.17
게임서버) 7. 논블로킹 소켓  (0) 2023.01.12
게임서버) 6. 소켓프로그래밍  (0) 2023.01.11
게임서버) 3.멀티쓰레드 공유자원  (0) 2023.01.06
게임서버) 2. 멀티쓰레드  (0) 2023.01.06

댓글