분산 게임 서버 프레임워크 sframe(4) - 메시지 맵

서버 업무 개발에 있어서 주요한 임무는 각종 소식, 즉 서비스 간의 소식, 클라이언트의 소식 등을 처리하는 것이다.일반적인 개발 모델은 모든 종류의 정보를 그에 대응하는 처리 함수에 비추는 것이다.서버는 이 메시지를 받은 후 그에 대응하는 처리 함수를 찾아 메시지를 디코딩하고 마지막으로 처리 함수를 호출합니다.이러한 절차는 모두 통용되기 때문에 대부분의 서버 프레임워크에는 메시지 매핑 메커니즘이 있다.실제 개발에서 하나의 체계적인 업무는 왕왕 매우 복잡하다. 이것은 우리가 처리해야 할 소식이 매우 많고 몇 백, 수천 가지 소식이 왕왕 정상이라는 것을 의미한다.그래서 좋은 소식 매핑 메커니즘이 매우 중요하다.이 글에서, 나는 주로 sframe의 메시지 매핑 메커니즘을 소개할 것이다.
앞의 글을 본 학생들은 sframe를 사용하여 서비스 정보를 처리하는 방법을 이미 알고 있을 것이다.논리적 서비스를 개발할 때 우리는 메시지 처리 함수를 등록해야 한다. 일반적인 방법은 서비스가 초기화될 때 다음과 같은 세 가지 함수를 호출하는 것이다.
       RegistInsideServiceMessageHandler()
       RegistNetServiceMessageHandler()
       RegistServiceMessageHandler()
앞에서 우리는 이미 그들 셋이 각각 무엇을 하는지 알았다.그럼 이 세 함수는 실제로 무엇을 했습니까?
실제로 sframe의 메시지 매핑 기능은 템플릿 클래스인 Delegate Manager를 통해 이루어진다.클래스는 Service 클래스에 있으며 다음과 같은 두 가지 객체가 있습니다.
       DelegateManager_inside_delegate_mgr;프로세스 내부 서비스 메시지의 매핑을 책임집니다.
       DelegateManager_net_delegate_mgr;네트워크 서비스 메시지의 매핑을 책임집니다.
RegistInsideServiceMessageHandler() 함수는 호출_inside_delegate_mgr.Regist().
RegistNetServiceMessageHandler() 함수는 호출_net_delegate_mgr.Regist().
RegistServiceMessageHandler() 동시 호출 _inside_delegate_mgr.Regist () 및_net_delegate_mgr.Regist().
따라서 진정한 메시지 매핑 기능은 DelegateManager 클래스에서 이루어집니다.이것은 클라이언트 메시지를 처리할 때 메시지 매핑 기능을 완성할 수 있는 일반적인 보조 클래스입니다.관련 구현도 헤더 파일 sframe/sfram/util/Serialization 하나뿐입니다.h.
1. 메시지 처리 함수 등록
DelegateManager는 임의의 매개 변수를 등록하는 정적 함수, 임의의 클래스를 등록하는 임의의 매개 변수의 비정적 구성원 함수를 지원합니다.물론, 메시지 처리 함수의 반환 형식은void여야 합니다.비정적 구성원 함수를 등록할 때 귀속 대상을 등록할 수도 있고 호출할 때 대상을 지정할 수도 있다.본고는 가장 간단한 비정태 함수의 등록을 예로 들어 설명하고자 한다. 비정태 구성원 함수에 대해 흥미를 가진 학생들은 원본 코드를 직접 보십시오(사실 원리는 모두 같고 기본 원리를 이해하면 어떤 변화도 곧 알게 될 것입니다).이곳의 설명은 제가 예시 코드와 협조할 것입니다. 그러나 이곳의 예시 코드는 가장 기초적인 부분만 포함하고 원본 코드와는 다르지만 원리는 모두 같습니다.
우선, 우리는 많은 함수를 함께 등록하고 하나의 디지털 ID(메시지 ID)를 키로 하려면 반드시 용기가 있어야 한다.의심할 여지가 없다. 맵이 가장 좋은 선택이다. 우리는 맵의 대상을 정의해서 이 함수를 저장한다.
그럼, 문제가 왔습니다.map, Key와 Val은 모두 고정된 유형입니다. Key는 당연히 고정된 유형일 수 있습니다. 모든 메시지의 메시지 ID 유형은 틀림없이 동일하게 실현될 것입니다. 그러나 Val이 고정된 유형을 사용한다면 우리가 등록하고자 하는 함수는 반드시 같은 매개 변수, 같은 반환 값을 요구해야 합니다.이렇게 되면 우리는 어떻게 임의의 매개 변수 유형의 함수 등록을 실현합니까?
이 문제는 보기에는 매우 까다롭지만, 실제로는 매우 간단하다.이 문제를 해결해야 할 관건은 두 가지가 있다. 임의의 유형, 임의의 유형을 하나의 용기에 넣는 것이다.
임의의 유형의 문제를 해결하는 데는 c++에서 템플릿이 가장 좋은 선택이라는 것을 의심할 여지가 없다.
해결은 임의로 같은 용기에 넣는다. 사실 우리는 처음에 c++를 배웠을 때 이미 배웠고 유형의 계승을 이용하여 해결할 수 있다.
그래서 상기 문제를 해결하는 방법은 이미 있다. 그것이 바로 템플릿+계승이다.
우리는 모든 함수 형식을 하나의 템플릿 클래스에 봉인합니다. 이 템플릿 클래스는 같은 클래스를 계승합니다. 맵 용기에 이 클래스 바늘을 저장하면 됩니다.
다음은 코드입니다.
// Delegate 
class IDelegate
{
public:
virtual ~IDelegate() {}
};

//  
template
class StaticFuncDelegate : public IDelegate
{
public:
typedef void(*FuncT)(Args_Type...);

public:
StaticFuncDelegate(FuncT func) : _func(func) {}
~StaticFuncDelegate() {}

private:
FuncT _func;
};

class DelegateManager
{
public:

static const int kMaxArrLen = 65536;

//  、 
// ...

//  
template
void Regist(int id, void(*func)(Args...))
{
	IDelegate * caller = new StaticFuncDelegate(func);
	_map_callers[id] = caller;
}

private:
std::unordered_map _map_callers;
};

2. 메시지 처리 함수의 호출과 메시지 디코딩
메시지 처리 함수의 등록은 이미 완성되었지만, 등록의 목적은 호출하고, 등록된 함수의 매개 변수 유형에 따라 메시지를 디코딩하는 것이다.이것은 또 어떻게 해결합니까?
우선 우리가 해결해야 할 문제는 호출할 때 함수 등록을 할 때 들어오는 유형 정보를 어떻게 얻는가이다.보기에는 매우 번거롭지만, 실제로는 매우 간단하다.다중 모드를 이용하면 해결할 수 있습니다. 우리는 IDelegate 인터페이스에 콜 순허 함수를 설명합니다. StaticFuncDelegate는 이 방법을 실현합니다. 그러면 콜을 호출할 때 진정한 StaticFuncDelegate 클래스의 콜 함수에 들어갔습니다. StaticFuncDelegate의 콜 함수에 들어갔으니 처리 함수의 매개 변수 유형을 찾기가 쉽습니다.
유형을 찾으면 이 유형에 따라 메시지를 디코딩합니다.디코딩에 성공하면 구체적인 메시지 처리 함수를 호출할 수 있습니다.그러면 어떻게 디코딩을 합니까?
우선, 디코딩은 서비스 메시지 대상에서 메시지 처리 함수에 전달해야 할 데이터를 추출하는 것이다.내부 서비스 메시지에 대해 말하자면 모든 대상을 꺼내면 된다.인터넷 서비스 메시지에 대해 말하자면 이러한 유형에 따라 상기 글에서 말한 서열화와 반서열화 방법에 따라 데이터를 대상으로 반서열해야 한다. 물론 다른 서열화 방안을 사용해야 하는 경우도 있다.따라서 유니버설화의 원칙에 따라 우리는 디코딩 부분을 독립시켜야 한다.디코딩을 완료하기 위해 StaticFuncDelegate에서 호출할 수 있는 디코딩 방법을 외부에서 제공합니다.따라서 전체 Delegate Manager 클래스에는 디코더, 즉 Delegate Manager를 가리키는 템플릿 매개 변수가 있어야 합니다.디코더는 템플릿 함수를 제공하여 디코딩을 완성하면 됩니다.그러면 StaticFuncDelegate는 메시지 처리 함수의 매개 변수 유형에 따라 디코딩을 어떻게 호출합니까?수정된 최종 코드는 다음과 같습니다.
// Delegate 
template
class IDelegate
{
public:
	virtual ~IDelegate() {}
	virtual bool Call(Decoder_Type& decoder) = 0;
};

//  
template
class StaticFuncDelegate : public IDelegate
{
public:
	typedef void(*FuncT)(Args_Type...);

public:
	StaticFuncDelegate(FuncT func) : _func(func) {}
	~StaticFuncDelegate() {}

	bool Call(Decoder_Type& decoder) override
	{
		std::tuple::type ...> args_tuple;
		std::tuple::type ...> * p_args_tuple = nullptr;
		if (!decoder.Decode(&p_args_tuple, args_tuple))
		{
			return false;
		}

		if (p_args_tuple == nullptr)
		{
			p_args_tuple = &args_tuple;
		}

		UnfoldTuple(this, *p_args_tuple);
		return true;
	}

	template
	void DoUnfoldTuple(Args&&... args)
	{
		this->_func(std::forward(args)...);
	}

private:
	FuncT _func;
};

// Delegate 
template 
class DelegateManager
{
public:

	//  、 
	// ...

	bool Call(int id, Decoder_Type & decoder)
	{
		auto it = _map_callers.find(id);
		if (it == _map_callers.end())
		{
			return false;
		}

		return it->seconds->Call(decoder);
	}

	//  
	template
	void Regist(int id, void(*func)(Args...))
	{
		auto caller = new StaticFuncDelegate(func);
		_map_callers[id] = caller;
	}

private:
	std::unordered_map *> _map_callers;
};
그 중에서 UnfoldTuple 함수는 주로tuple 대상을 푸는 기능을 완성하였으며, 구체적인 코드는 원본 코드 sframe/sfram/util/TupleHelper를 보십시오.h.디코더에 대해 sframe는 두 개의 디코더 Inside Service Message Decoder와 Net Service Message Decoder를 실현하여 참고할 수 있습니다.양자는 각각 내부 서비스 메시지와 네트워크 서비스 메시지의 디코딩을 실현하고 코드를 실현하려면 원본 코드 sframe/sfram/serv/MessageDecoder를 참고하십시오.h.

좋은 웹페이지 즐겨찾기