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 |
댓글