gnugk5.5源码分析(4)之ras相关消息处理实现

一、ras消息类型

在h225.h中,H225_RasMessage有定义了H323协议上的各类ras消息。

    enum Choices {
      e_gatekeeperRequest,
      e_gatekeeperConfirm,
      e_gatekeeperReject,
      e_registrationRequest,
      e_registrationConfirm,
      e_registrationReject,
      e_unregistrationRequest,
      e_unregistrationConfirm,
      e_unregistrationReject,
      e_admissionRequest,
      e_admissionConfirm,
      e_admissionReject,
      e_bandwidthRequest,
      e_bandwidthConfirm,
      e_bandwidthReject,
      e_disengageRequest,
      e_disengageConfirm,
      e_disengageReject,
      e_locationRequest,
      e_locationConfirm,
      e_locationReject,
      e_infoRequest,
      e_infoRequestResponse,
      e_nonStandardMessage,
      e_unknownMessageResponse,
      e_requestInProgress,
      e_resourcesAvailableIndicate,
      e_resourcesAvailableConfirm,
      e_infoRequestAck,
      e_infoRequestNak,
      e_serviceControlIndication,
      e_serviceControlResponse,
      e_admissionConfirmSequence
    };

而与之相对应的,就是表述各ras的类定义,各类分别代表着当前这种消息类型所数据字段。

class H225_GatekeeperRequest;
class H225_GatekeeperConfirm;
class H225_GatekeeperReject;
class H225_RegistrationRequest;
class H225_RegistrationConfirm;
class H225_RegistrationReject;
class H225_UnregistrationRequest;
class H225_UnregistrationConfirm;
class H225_UnregistrationReject;
class H225_AdmissionRequest;
class H225_AdmissionConfirm;
class H225_AdmissionReject;
class H225_BandwidthRequest;
class H225_BandwidthConfirm;
class H225_BandwidthReject;
class H225_DisengageRequest;
class H225_DisengageConfirm;
class H225_DisengageReject;
class H225_LocationRequest;
class H225_LocationConfirm;
class H225_LocationReject;
class H225_InfoRequest;
class H225_InfoRequestResponse;
class H225_NonStandardMessage;
class H225_UnknownMessageResponse;
class H225_RequestInProgress;
class H225_ResourcesAvailableIndicate;
class H225_ResourcesAvailableConfirm;
class H225_InfoRequestAck;
class H225_InfoRequestNak;
class H225_ServiceControlIndication;
class H225_ServiceControlResponse;
class H225_ArrayOf_AdmissionConfirm;

二、gnugk对接各类ras消息

gnugk对各ras消息的对接,主要是运用到了模板工厂方法和GatekeeperMessage类。其关系类图可以如下。
在这里插入图片描述

从关系图中可以看出,RasMsg作为所有ras消息的基类,继承自Task类,以便于各个消息能多线程异步处理,互不影响,因此RasMsg需要实现Task协定的Exec接口;

void RasMsg::Exec()
{
	PTRACE(1, "RAS\t" << m_msg->GetTagName() << " Received from " << AsString(m_msg->m_peerAddr, m_msg->m_peerPort));
	if (Process()) {
		Reply(m_authenticators);
	}
}

从RasMsg::Exec的实现来看,逻辑很简单,调用Process方法作具体的处理,然后调用Reply发送响应,因此RasMsg重新约定了纯虚接口virtual bool Process() = 0,这个接口需要针对各个不同的RAS消息单独实现;除此之外,RasMsg还约定了另外两个纯虚接口virtual int GetSeqNum() const = 0和virtual H225_NonStandardParameter *GetNonStandardParam() = 0,但是gnugk对于各Ras消息需要实现Process接口这一行为,并不是采用各个消息类型都继承RasMsg,然后定义相应类型的类,而是结合了模板工厂Factory的Creator1,定义RasPDU模板类来实现;因此对于每个具体的ras只需要重载实现Process即可,不需要每个都重新定义类,如template<> bool RasPDU<H225_GatekeeperRequest>::Process()表示对GRQ的Process处理实现;其中针对RRQ、ARQ比较特殊些,采用了继承RasPDU的方式来实现;

另外,也还需要结合GatekeeperMessage类和RasListener类来实现数据的读取和发送。

2.1 GatekeeperMessage类

众所周知,通信交互就存在数据的读取和发送,而对ras的消息读取和发送,以及数据的保存就主要由GatekeeperMessage类来完成。从代码定义就可以看到,最主要的成员方法是bool Read(RasListener *)读取和bool Reply(GkH235Authenticators * authenticators)发送;而数据成员就很自然的是H225_RasMessage m_recvRAS和H225_RasMessage m_replyRAS;也就是说,数据从各RasListener读取上来后,就保存在m_recvRAS中,而在完成了gnugk内部对不同数据的逻辑处理后,最生成的响应结果,就保存在m_replyRAS,然后通话发送方法发回到请求方。
换句说,一个GatekeeperMessage类实例,就代表着一个ras的完整的生命周期。

class GatekeeperMessage {
public:
	GatekeeperMessage() : m_peerPort(0), m_socket(NULL)
#ifdef HAS_H46017
		, m_h46017Socket(NULL)
#endif
		{ }
	unsigned GetTag() const { return m_recvRAS.GetTag(); }
	const char *GetTagName() const;
	bool Read(RasListener *);
#ifdef HAS_H46017
	bool Read(const PBYTEArray & buffer);
#endif
	bool Reply(GkH235Authenticators * authenticators);

	PPER_Stream m_rasPDU;
	H225_RasMessage m_recvRAS;
	H225_RasMessage m_replyRAS;
	PIPSocket::Address m_peerAddr;
	WORD m_peerPort;
	PIPSocket::Address m_localAddr;
	RasListener * m_socket;
#ifdef HAS_H46017
	CallSignalSocket * m_h46017Socket;
#endif
};

2.2 各ras消息对接到模板工厂模型

template<class RAS>
class RasPDU : public RasMsg {
public:
	typedef RAS RasClass;

	RasPDU(GatekeeperMessage *m) : RasMsg(m), request(m->m_recvRAS) { }
	virtual ~RasPDU() { }

	// override from class RasMsg
	virtual bool Process() { return false; }
	
	typedef Factory<RasMsg, unsigned>::Creator1<GatekeeperMessage *> RasCreator;
	struct Creator : public RasCreator {
		Creator() : RasCreator(RasInfo<RAS>::tag) { }
		virtual RasMsg *operator()(GatekeeperMessage *m) const { return new RasPDU<RAS>(m); }
	};
};

上面这个只是截取了RasPDU继承自RasMsg,并对接模板工厂的实现部分,并不是RasPDU全部定义。
如前面所述,各个类型的RAS消息,通过RasPDU这个模板类来实现自己的Process处理流程。RasPDU,接受的模板参数类型RAS,实际上指的就是H225_GatekeeperRequest这类H225的消息类;我们知道RasPDU只是一个模板类,只有在具体实例化时,才能具体确定;而为了在其它地方都能成功各类型的RAS消息,在RasPDU内部定义了Creator,该Creator继承自Factory的Creator1;并且指定了返回的产品实例类型是RasMsg,产品的标识类型是unsigned,而且接受一个参数,参数类型是GatekeeperMessage *;从对Factory的Creator1的定义,可知其子类需要重载实现约定的实例运算符operator(),从代码定义中,也看到了Creator确实重载实现了这个操作符。

2.3 理解RasInfo的实现

在Creator的构造函数中,其向上构造父类时,传递的产品标识是unsigned的,而且其值为RasInfo::tag,那么这个值具体等于多少,为什么对于具体的RAS类,RasInfo::tag能唯一表示标识它呢?这个也是需要梳理清楚的。
对于RasInfo::tag,我们先不管其成员变量tag的值,先来看下RasInfo的定义,从调用方式也可以看出,这也是一个模板类。要理解RasInfo的定义实现,就需要查看rasinfo.h。

首先,定义一个RasTag类,表示RAS tag值;其逻辑也简单,就是在特化后,进行操作转为unsigned时,返回特化时的值 I;也就是实现操作符unsigned()。

template<int I> struct RasTag {
	operator unsigned() const { return I; }
};

其次,定义一个RasType类,映射每个具体的Ras tag与相应对应的Ras类的类型。

template<int> struct RasType;

如GAQ/GCF/GRJ三种消息各自的tag值和类类型的关联如下,比如tag值为H225_RasMessage::e_gatekeeperRequest时,其对应的类类型为H225_GatekeeperRequest ,并且重命名为Type;

template<> struct RasType<H225_RasMessage::e_gatekeeperRequest> {
	typedef H225_GatekeeperRequest Type;
};
template<> struct RasType<H225_RasMessage::e_gatekeeperConfirm> {
	typedef H225_GatekeeperConfirm Type;
};
template<> struct RasType<H225_RasMessage::e_gatekeeperReject> {
	typedef H225_GatekeeperReject Type;
};

然后,定义TagInfo,真正关联起一个tag值和它对应的类型。在其内部定义重定义出了类型Tag和Type,以及枚举值tag和flag。

template<int I> struct TagInfo {
	typedef RasTag<I> Tag;
	typedef typename RasType<I>::Type Type;
	enum {
		tag = I,
		// there are just 32 types of RAS, lucky!
		flag = (1 << I)
	};
};

此外,对于一个请示,其响应通常是成功或拒绝,因此还多定义了一些比较方便使用的模板;但是这些模板的定义是有要求的,对于一个Request来说,其Comfirm响应和Reject响应的tag值刚好比Request请求大1和大2,那么为什么刚好是1和2这两个数值呢。其实,这里只是因为H225_RasMessage::Choices在枚举时,就是按这么顺序写的,如e_gatekeeperRequest=0,e_gatekeeperConfirm=1,e_gatekeeperReject=2;所以对于GRQ/GCF/GRJ的TagInfo,刚好就可以完成下面的RequestInfo和ConfirmInfo、RejectInfo的定义;因此,这里的三个模板定义,是gnugk在理解了H225_RasMessage的类型定义后,特殊化取巧的定义实现方式。

template<int I> struct RequestInfo : public TagInfo<I> {
	typedef RasTag<I+1> ConfirmTag;
	typedef RasTag<I+2> RejectTag;
	typedef typename RasType<I+1>::Type ConfirmType;
	typedef typename RasType<I+2>::Type RejectType;
};

template<int I> struct ConfirmInfo : public TagInfo<I> {
	typedef RasTag<I-1> RequestTag;
	typedef typename RasType<I-1>::Type RequestType;
};

template<int I> struct RejectInfo : public TagInfo<I> {
	typedef RasTag<I-2> RequestTag;
	typedef typename RasType<I-2>::Type RequestType;
};

最后,定义RasInfo,表示一个Ras请求和它所关联的响应。

template<class> struct RasInfo;

再接下来就是特化定义各具体的Ras消息的RasInfo消息了。主要是分为4种类型:RAS request、RAS confirm、RAS reject和others。

理解了这里的定义后,再回过头来看,前面Creator的构造函数,Creator() : RasCreator(RasInfo::tag) { },就可以理解RasInfo::tag实际上表示的就是某一个Ras消息具体的H225_RasMessage::Choices所枚举的值。

三、RAS消息完整处理流程

这里,我们从头开始,梳理一遍,RAS消息的处理流程。
首先,在RasServer::Run执行时,注册各类RAS消息。

	RasPDU<H225_GatekeeperRequest>::Creator GRQCreator;
	RasPDU<H225_GatekeeperConfirm>::Creator GCFCreator;
	RasPDU<H225_GatekeeperReject>::Creator GRJCreator;
	RegistrationRequestPDU::Creator RRQCreator;
	RasPDU<H225_RegistrationConfirm>::Creator RCFCreator;
	RasPDU<H225_RegistrationReject>::Creator RRJCreator;
	RasPDU<H225_UnregistrationRequest>::Creator URQCreator;
	RasPDU<H225_UnregistrationConfirm>::Creator UCFCreator;
	RasPDU<H225_UnregistrationReject>::Creator URJCreator;
	AdmissionRequestPDU::Creator ARQCreator;
	RasPDU<H225_AdmissionConfirm>::Creator ACFCreator;
	RasPDU<H225_AdmissionReject>::Creator ARJCreator;
	RasPDU<H225_BandwidthRequest>::Creator BRQCreator;
	RasPDU<H225_BandwidthConfirm>::Creator BCFCreator;
	RasPDU<H225_BandwidthReject>::Creator BRJCreator;
	RasPDU<H225_DisengageRequest>::Creator DRQCreator;
	RasPDU<H225_DisengageConfirm>::Creator DCFCreator;
	RasPDU<H225_DisengageReject>::Creator DRJCreator;
	RasPDU<H225_LocationRequest>::Creator LRQCreator;
	RasPDU<H225_LocationConfirm>::Creator LCFCreator;
	RasPDU<H225_LocationReject>::Creator LRJCreator;
	RasPDU<H225_InfoRequest>::Creator IRQCreator;
	RasPDU<H225_InfoRequestResponse>::Creator IRRCreator;
	RasPDU<H225_UnknownMessageResponse>::Creator UMRCreator;
	RasPDU<H225_RequestInProgress>::Creator RIPCreator;
	RasPDU<H225_ResourcesAvailableIndicate>::Creator RAICreator;
	RasPDU<H225_ServiceControlIndication>::Creator SCICreator;
	RasPDU<H225_ServiceControlResponse>::Creator SCRCreator;
	RasPDU<H225_NonStandardMessage>::Creator NonStandardCreator;

其次,启动RAS监听后,在收到RAS消息时,会进入RasServer::ReadSocket(如果不清楚gnugk的网络结构的,可以查看网络监听章节的分析);通过RasListener::ReadRas读取相应的RAS消息,生成GatekeeperMessage实例;再通过 RasServer::CreateRasJob,提交给一个线程去异步执行,或者直接同步执行。

void RasServer::CreateRasJob(GatekeeperMessage * msg, bool syncronous)
{
	# 定义Ras的工厂类型,从RasPDU定义,可以知道该工厂的产品是RasMsg,产品标识是unsigned,也就是具体ras消息的tag值
	typedef Factory<RasMsg, unsigned> RasFactory;
	unsigned tag = msg->GetTag();
	PWaitAndSignal rlock(requests_mutex);
	PWaitAndSignal hlock(handlers_mutex);
	# 通过工厂方法,创建RasMsg的实例ras,之所以能知道每个具体的tag值对应的RasMsg是什么,是因为前面在RasServer::Run时,已经注册了;
	# 并且创建时需要传递GatekeeperMessage *参数,这个是由RasPDU约定的
	if (RasMsg *ras = RasFactory::Create(tag, msg)) {
		# 这里的RasServer::handlers成员,主要是用于gnugk的neighbor/parent关系的,这里先不讲解这部分功能。因此,iter == handlers.end()成立。
		std::list<RasHandler *>::iterator iter = find_if(handlers.begin(), handlers.end(), bind2nd(mem_fun(&RasHandler::IsExpected), ras));
		if (iter == handlers.end()) {
			std::list<RasMsg *>::iterator i = find_if(requests.begin(), requests.end(), bind2nd(mem_fun(&RasMsg::EqualTo), ras));
			# 这里作个判断,如何是重复的包,则忽略不处理。
			if (i != requests.end() && !(*i)->IsDone()) {
				PTRACE(2, "RAS\tDuplicate " << msg->GetTagName() << ", deleted");
				delete ras;
				ras = NULL;
			} else {
				# 若不是重复的包,则依赖是否要同步处理,走不同流程。
				if (syncronous) {
					# 若需要同步处理,则直接调用Exec方法,前面分析了RasMsg继承自Task,所以调用Exec就是直接进行Task处理。
					ras->Exec();
					delete ras;
					ras = NULL;
				} else {
					requests.push_back(ras);
					# 若需要异步处理,则把这个Task提交给Jobs实例,调用Jobs的Execute方法,启动多线程处理。
					# 提交给Jobs,是gnugk的多线程框架的定义,不清楚的,可以查看gnugk多线程分析的章节
					Job *job = new Jobs(ras);
					job->SetName(msg->GetTagName());
					job->Execute();
				}
			}
		} else {
			PTRACE(2, "RAS\tTrapped " << msg->GetTagName());
			// re-create RasMsg object by the handler
			ras = (*iter)->CreatePDU(ras);
			(*iter)->Process(ras);
		}
	} else {
		PTRACE(1, "RAS\tUnknown RAS message " << msg->GetTagName());
		delete msg;
	}
}

然后,执行到RasMsg::Exec的方法中,调用Process方法进行具体的处理。
这里的Process方法就是各RAS消息,通过RasPDU类来实现的。

template<> bool RasPDU<H225_GatekeeperRequest>::Process()
bool RegistrationRequestPDU::Process()
bool AdmissionRequestPDU::Process()
template<> bool RasPDU<H225_UnregistrationRequest>::Process()
template<> bool RasPDU<H225_BandwidthRequest>::Process()
template<> bool RasPDU<H225_DisengageRequest>::Process()
template<> bool RasPDU<H225_LocationRequest>::Process()
template<> bool RasPDU<H225_InfoRequestResponse>::Process()
template<> bool RasPDU<H225_ResourcesAvailableIndicate>::Process()
template<> bool RasPDU<H225_ServiceControlIndication>::Process()
template<> bool RasPDU<H225_ServiceControlResponse>::Process()
template<> bool RasPDU<H225_RegistrationReject>::Process()

最后,在处理完成后,经RasMsg::Reply方法通过GatekeeperMessage::Reply调用RasListener::SendRas将响应消息发送出去。至些,一个RAS从读取到处理再到发送响应的过程完成。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值