KoreanFoodie's Study
[C++ 게임 서버] 4-3. Server Service 본문
[C++ 게임 서버] 4-3. Server Service
핵심 :
1. IocpObject 는 안전성을 위해 shared_ptr 를 차용하는 것이 좋다.
2. 서버는 클라이언트와만 통신하지 않고, 다른 서버나 DB 서버와도 통신할 수 있다. 따라서 용도에 따라 세션의 생성 및 관리를 할 수 있도록, 서비스를 만들어 주면 편리하다.
우리는 이전에 IOCP Core 클래스를 만들어 핸들, 세션, 소켓과 관련한 기능들을 간단히 다룰 수 있도록 만들었다.
그런데 이전에 우리가 만든 IOCP 클래스는 리스너에서 Accept 를 받으면 세션을 임의로 생성하는 식으로 구현이 되어 있다.
그런데 사실 세션은 특정 서버에 종속되면 자유도가 떨어지게 된다. 우리는 일반적으로 게임 서버라고 하면 '클라이언트 <-> 서버' 사이의 통신만 떠올리는 경향이 있는데, 사실 DB 서버도 따로 있을 수 있고, 서버와 서버 끼리도 통신이 이루어질 수 있다.
따라서 '개념적으로' 세션을 어떻게 다룰지를 용도에 맞게 분리해서 사용할 수 있다면, 서로 다른 특성의 서버 및 네트워크에서 이를 가져다가 속성값만 바꿔서 활용할 수 있을 것이다. 풀링을 한다던지 할 수도 있고.
그래서 이번에는 서비스라는 개념을 추가할 것인데... 그 전에 먼저 간단하게 기존에 만든 IocpCore 오브젝트나 Session 을 SharedPtr 처럼 사용할 수 있도록 바꿀 것이다.
// shared_ptr
using IocpCoreRef = std::shared_ptr<class IocpCore>;
using IocpObjectRef = std::shared_ptr<class IocpObject>;
using SessionRef = std::shared_ptr<class Session>;
using ListenerRef = std::shared_ptr<class Listener>;
using ServerServiceRef = std::shared_ptr<class ServerService>;
그럼 기존에 사용했던 IocpCore 오브젝트들의 타입을 IocpCoreRef 로 바꾸고 참조 방식을 조금 바꿔주어야 하는데, 굳이 여기에 다 적지는 않겠다 😅
다시 핵심으로 넘어와서... 서비스의 구현을 보자.
Service.h
#pragma once
#include "NetAddress.h"
#include "IocpCore.h"
#include "Listener.h"
#include <functional>
enum class ServiceType : uint8
{
Server,
Client
};
/*-------------
Service
--------------*/
using SessionFactory = function<SessionRef(void)>;
class Service : public enable_shared_from_this<Service>
{
public:
Service(ServiceType type, NetAddress address, IocpCoreRef core, SessionFactory factory, int32 maxSessionCount = 1);
virtual ~Service();
virtual bool Start() abstract;
bool CanStart() { return _sessionFactory != nullptr; }
virtual void CloseService();
void SetSessionFactory(SessionFactory func) { _sessionFactory = func; }
SessionRef CreateSession();
void AddSession(SessionRef session);
void ReleaseSession(SessionRef session);
int32 GetCurrentSessionCount() { return _sessionCount; }
int32 GetMaxSessionCount() { return _maxSessionCount; }
public:
ServiceType GetServiceType() { return _type; }
NetAddress GetNetAddress() { return _netAddress; }
IocpCoreRef& GetIocpCore() { return _iocpCore; }
protected:
USE_LOCK;
ServiceType _type;
NetAddress _netAddress = {};
IocpCoreRef _iocpCore;
Set<SessionRef> _sessions;
int32 _sessionCount = 0;
int32 _maxSessionCount = 0;
SessionFactory _sessionFactory;
};
/*-----------------
ClientService
------------------*/
class ClientService : public Service
{
public:
ClientService(NetAddress targetAddress, IocpCoreRef core, SessionFactory factory, int32 maxSessionCount = 1);
virtual ~ClientService() {}
virtual bool Start() override;
};
/*-----------------
ServerService
------------------*/
class ServerService : public Service
{
public:
ServerService(NetAddress targetAddress, IocpCoreRef core, SessionFactory factory, int32 maxSessionCount = 1);
virtual ~ServerService() {}
virtual bool Start() override;
virtual void CloseService() override;
private:
ListenerRef _listener = nullptr;
};
서비스는 서비스 타입(해당 서비스의 용도에 따라 다를 것이다. 여기서는 서버에서 쓰는 것인지, 클라에서 쓰는 것인지로 일단 나눈다), NetAddress, IocpCoreRef(아까 스마트 포인터를 차용한 IocpCore 오브젝트), SessionFactory, maxSessionCount 값을 넣어 준다.
용도에 따라 ClientService 와 ServerService 를 만들고, 이를 커스터마이징 해 줄 것이다.
Service.cpp
#include "pch.h"
#include "Service.h"
#include "Session.h"
#include "Listener.h"
/*-------------
Service
--------------*/
Service::Service(ServiceType type, NetAddress address, IocpCoreRef core, SessionFactory factory, int32 maxSessionCount)
: _type(type), _netAddress(address), _iocpCore(core), _sessionFactory(factory), _maxSessionCount(maxSessionCount)
{
}
Service::~Service()
{
}
void Service::CloseService()
{
// TODO
}
SessionRef Service::CreateSession()
{
SessionRef session = _sessionFactory();
if (_iocpCore->Register(session) == false)
return nullptr;
return session;
}
void Service::AddSession(SessionRef session)
{
WRITE_LOCK;
_sessionCount++;
_sessions.insert(session);
}
void Service::ReleaseSession(SessionRef session)
{
WRITE_LOCK;
ASSERT_CRASH(_sessions.erase(session) != 0);
_sessionCount--;
}
/*-----------------
ClientService
------------------*/
ClientService::ClientService(NetAddress targetAddress, IocpCoreRef core, SessionFactory factory, int32 maxSessionCount)
: Service(ServiceType::Client, targetAddress, core, factory, maxSessionCount)
{
}
bool ClientService::Start()
{
// TODO
return true;
}
ServerService::ServerService(NetAddress address, IocpCoreRef core, SessionFactory factory, int32 maxSessionCount)
: Service(ServiceType::Server, address, core, factory, maxSessionCount)
{
}
bool ServerService::Start()
{
if (CanStart() == false)
return false;
_listener = MakeShared<Listener>();
if (_listener == nullptr)
return false;
ServerServiceRef service = static_pointer_cast<ServerService>(shared_from_this());
if (_listener->StartAccept(service) == false)
return false;
return true;
}
void ServerService::CloseService()
{
// TODO
Service::CloseService();
}
잘 보면, 세션을 만드는 역할을 과거에는 GameServer 클래스에서 임의로 생성했었는데, 이제는 각 Service 단에서 SessionFactory 를 통해 만들고 있는 것을 알 수 있다.
각 서비스에 따라, 서비스가 시작하고 종료할 때 어떤 동작을 해야 할지를 구현하면 될 것이다(TODO 부분). 😊
'Game Dev > Game Server' 카테고리의 다른 글
[C++ 게임 서버] 4-5. Session #2 (0) | 2023.12.07 |
---|---|
[C++ 게임 서버] 4-4. Session #1 (0) | 2023.12.06 |
[C++ 게임 서버] 4-2. IocpCore (0) | 2023.10.26 |
[C++ 게임 서버] 4-1. Socket Utils (0) | 2023.10.25 |
[C++ 게임 서버] 3-12. Completion Port 모델 (0) | 2023.10.18 |