一、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从读取到处理再到发送响应的过程完成。