헛둘이 2023. 12. 11. 09:42

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;
	}
};