17. Session
Session 복습
- Session은 클라이언트와 통신하기 위해 클라이언트에 관한 정보를 담고 있는 클래스이다.
- Session의 특징은 기본 기능들은 서버코어단에서 제공하고, 서버단에서 기능을 확장할 수 있는 구조를 지니고 있다.
Session
- Send, Recv, Connect, Disconnect에 대한 내용을 처리하고, 이를 담당하는 Event(Overlapped)들을 소유하고 있다.
- 위 4개의 이벤트들은 이전에 Listener 예제에서 봤던 것처럼 Register, Process의 쌍을 처리하며, 내부 작동과정은 AcceptEx를 사용하는 것과 유사한 패턴을 띈다.
- IOCP큐와 주시할 소켓을 전달하고 각 이벤트가 발생하면 IOCP큐에 해당 이벤트를 던져넣는 식(이 로직들 또한 내가 IocpEvent를 전달한 내용을 토대로 함)
void Session::Dispatch(IocpEvent* iocpEvent, int32 numOfBytes)
{
switch (iocpEvent->type)
{
case EventType::Connect:
ProcessConnect();
break;
case EventType::Disconnect:
ProcessDisconnect();
break;
case EventType::Recv:
ProcessRecv(numOfBytes);
break;
case EventType::Send:
ProcessSend(iocpEvent, numOfBytes);
break;
default:
break;
}
}
- Session의 Dispatch가 호출되면 같이 전달 받은 IocpEvent의 타입을 통해 어떤 이벤트를 처리할지 분기한다.
- Connect 함수는 내부적으로 클라이언트 서비스가 아니라면 return된다.
- Process 함수들의 경우 Process가 완료되면 OnXXXXProcess와 같은 함수들을 호출하게 되는데,
이 함수들은 서버단에서 구현된 Session을 상속받는 자식 Session들에서 동작을 정의한다.
(서버 코어는 엔진이고, 서버는 클라의 개념)
- 그리고 참조계수를 1 줄임으로써 해당 이벤트가 IocpObject를 마지막으로 참조하고 있던 오브젝트였다면 해당 오브젝트가 삭제되게 된다.
void Session::ProcessSend(IocpEvent* sendEvent, int32 numOfBytes)
{
sendEvent->owner = nullptr; // 참조계수 -1
delete sendEvent;
if (numOfBytes == 0)
{
Disconnect(L"Send 0");
return;
}
OnSend(numOfBytes); // 클라이언트단에서 상속받아서 구현
}
- Session에서 Send나 Recv 등의 이벤트를 요청할 수도 있는데, RegisterSend는 다른 함수 Send로부터 호출된다.
- Send는 내용과 사이즈를 인자로 받는다.
void Session::Send(BYTE* buffer, int32 len)
{
// Send 속성을 가진 Overlapped(IocpEvent)를 만든 후 버퍼에 데이터를 담는다
IocpEvent* sendEvent = new IocpEvent(EventType::Send);
sendEvent->owner = shared_from_this();
sendEvent->buffer.resize(len);
memcpy(sendEvent->buffer.data(), buffer, len);
// 락을 걸고 Send Job 등록
// 이는 IOCP 큐에 들어가면 워커스레드에 의해 처리됨
WRITE_LOCK;
RegisterSend(sendEvent);
}
- 특징적인 부분은 Register 함수들의 경우 sendEvent->owner = shared_from_this와 같이 자신의 참조 계수를 하나 올려주는 코드가 있는데, 그 이유는 나중에 IOCP 큐에 처리할 이벤트들이 남아있을 때 해당 소켓이 연결이 끊어지면 메모리에서 바로 정리되지 않고 모든 이벤트들이 끝난 후에야 정리되도록 하기 위함이다. (크래쉬 방지)
- Session을 상속받는 예제는 아래와 같다
class GameSession
: public Session
{
public:
GameSession() = default;
~GameSession()
{
cout << "~GameSession()" << endl;
}
virtual int32 OnRecv(BYTE* buffer, int32 len) override
{
cout << "OnRecv Len = " << len << endl;
Send(buffer, len);
return len;
}
// 성공적으로 전달이 되었다면 호출
virtual void OnSend(int32 len) override
{
cout << "OnSend Len = " << len << endl;
}
};