KoreanFoodie's Study
[C++ 게임 서버] 4-5. Session #2 본문
[C++ 게임 서버] 4-5. Session #2
핵심 :
1. Session 구현에 있어, Send 는 나름 복잡한 구석이 있다.
2. 송신 버퍼를 어떻게 만들 것인지, 전송 시 여러 쓰레드가 Pending 상태로 놓였을 때, 순서가 반드시 보장되어야 하는지 등등을 생각하면서 구현해 보자.
이번 글에서는 이전에 만들었던 Session 관련 클래스에 내용을 덧붙여 보겠다.
조금 더 구체적으로 이야기하자면, Send 하는 부분을 간단하게 구현해 볼 것이다. 😉
그래서 기존의 Session 코드에서 바뀐 부분만 살짝 보면...
Session.h
/*--------------
Session
---------------*/
class Session : public IocpObject
{
public:
void ProcessSend(SendEvent* sendEvent, int32 numOfBytes);
public:
// Circular Buffer [ ]
//char _sendBuffer[1000];
//int32 _sendLen = 0;
};
일단 ProcessSend 에서 SendEvent* 를 받을 수 있도록 인자를 추가했고, 기존에 수신 버퍼가 BYTE 타입의 배열을 타입으로 갖고 있었던 것처럼 송신 버퍼도 비슷하게 만들어 줄 수 있다.
다만 위 방식처럼 Circular Buffer 를 만들게 되면, 송신을 할 때마다 버퍼를 돌면서 채우고 빼고를 반복해 주어야 하며, 복사 비용이 많이 들어 실제로 저런 식으로 사용하지는 않는다! 😅 따라서 위는 그냥 예시로 알아두면 좋겠다.
Session.cpp
void Session::ProcessSend(SendEvent* sendEvent, int32 numOfBytes)
{
sendEvent->owner = nullptr; // RELEASE_REF
xdelete(sendEvent);
if (numOfBytes == 0)
{
Disconnect(L"Send 0");
return;
}
// 컨텐츠 코드에서 오버로딩
OnSend(numOfBytes);
}
그럼 ProcessSend 의 구현을 보자. 일단 sendEvent 가 들어왔을 때 메모리를 잘 해제해 주고, 관련한 처리를 진행해 준다.
이 프로젝트에서는 게임 서버의 GameSession 클래스가 Session 클래스를 상속받아 관련 동작을 구현해 줄 것이다.
GameSession
class GameSession : public Session
{
public:
virtual int32 OnRecv(BYTE* buffer, int32 len) override
{
// Echo
cout << "OnRecv Len = " << len << endl;
Send(buffer, len);
return len;
}
virtual void OnSend(int32 len) override
{
cout << "OnSend Len = " << len << endl;
}
};
일단은 간단하게 OnRecv 의 동작은 Echo 서버처럼 동작하도록 만들었다. 송신 시에는 OnSend 를 호출하는데, 로그만 찍어주도록 했다.
OnRecv 에서는 다시 데이터를 전송하는데, 이 부분은 사실 Session::Send 에 구현되어 있다.
void Session::Send(BYTE* buffer, int32 len)
{
// 생각할 문제
// 1) 버퍼 관리?
// 2) sendEvent 관리? 단일? 여러개? WSASend 중첩?
// TEMP
SendEvent* sendEvent = xnew<SendEvent>();
sendEvent->owner = shared_from_this(); // ADD_REF
sendEvent->buffer.resize(len);
::memcpy(sendEvent->buffer.data(), buffer, len);
WRITE_LOCK;
RegisterSend(sendEvent);
}
여기서는 사실 여러 가지 생각할 거리가 있다.
예를 들어, 버퍼를 어떻게 관리할 것인가? 라는 문제가 생길 수 있다. Send 를 여러 번 호출할 때, 결국 버퍼에 해당 정보가 쌓이는데 버퍼가 꽉 차면 어떻게 할 것인가... 부터, 버퍼를 어떤 식으로 만들 것인지(아까 간단하게 Circular Buffer 를 언급했다) 등의 문제가 있다.
또한 버퍼가 꽉 차면 여러 송신 요청들이 Pending 상태로 놓이게 되는데, 과연 Pending 에서 풀려날 때 송신이 원하던 순서대로 이루어질지에 대한 문제도 있다.
왜냐면, GetQueuedCompletionStatus 함수를 통해 잠자던 쓰레드를 깨워 Send 를 동작시킬 때, 먼저 잠든 녀석이 선순위로 깨어난다는 보장이 없기 때문이다! 😮
여기에서는 다루기 않겠지만, 그런 문제는 Page Lock 등의 기법으로 해결한다고 한다. 여백이 적어 여기에 적지는 않겠다 🤣
'Game Dev > Game Server' 카테고리의 다른 글
[C++ 게임 서버] 4-7. RecvBuffer (0) | 2023.12.12 |
---|---|
[C++ 게임 서버] 4-6. Session #3 (0) | 2023.12.12 |
[C++ 게임 서버] 4-4. Session #1 (0) | 2023.12.06 |
[C++ 게임 서버] 4-3. Server Service (0) | 2023.12.05 |
[C++ 게임 서버] 4-2. IocpCore (0) | 2023.10.26 |