gnugk5.5源码分析(2)之网络监听

前言

gnugk本身是作为服务器的角色,那么就必然少不了网络监听,而网络监听整体上就大概分为UDP消息监听和TCP连接的监听。对于H323协议,其UDP相关的消息,主要是RAS消息,即GRQ/RRQ/LRQ等,而TCP相关的消息,主要是H.225.0 消息和H.245消息;因此对于gnugk的实现来说,主要也是有监听UDP和TCP两种连接的消息。

一、角色划分

由于是充当服务器,因此对于网络连接请求是需要监听并建立连接的。对于UDP协议,本质就是创建一个网络套接字,等待接收UDP数据包即可;而对于TCP协议,则需要先等待网络连接后,才能正常接收TCP数据包。在gnugk中,最关键的一个类是RasServer类,这个类本身即监听接收UDP包,即RAS消息,又通过TCPServer类,监听TCP连接。
我们先看下,整体的类图关系,再来解释。
在这里插入图片描述
在上图中,可以按颜色所标识的那样,从右到左,主要分为
第一:UDP监听类(RasListener类等);
第二:TCP socket连接类,即ServerSocket类和其子类(StatusClient、CallSignalSocket类等);这里的Socket连接类,指的是TCP accept后,指代同客户所建立的那个socket连接;
第三:TCP socket监听类,即TCPListenSocket类和其子类(CallSignalListener、StatusListener类等),负责启动后,监听网络,等待客户端过来进行TCP连接;
第四:TCP监听管理、监测功能相关的TCPServer类;
第五:UDP监听管理功能相关的RasServer类;

二、监听对象与线程

2.1 监听对象

从业务上来说,对于每个IP地址,都需要设置监听其相应的业务。如对于GRQ消息,需要支持UDP广播;因此在实现上,具体有下面这几个监听相关的类定义。

  • MulticastListener:属于UDP协议,负责监听接收GRQ消息;
  • RasListener: 属于UDP协议,负责监听接收RRQ、ARQ等RAS消息;
  • CallSignalListener:属于TCP协议,负责监听H225.0呼叫信令的TCP连接的建立;
  • TLSCallSignalListener:属于TCP协议,若支持TLS,则通过TLS来完成H225.0的TCP连接的建立;
  • StatusClient:属于TCP协议,支持后端Telnet连接的建立;
  • MultiplexH245Listener:属于TCP协议,H460协议下,可能存在的H245多路复用技术

2.2 监听线程

由于网络监听肯定是一个周期性执行的任务,因此TCPServer类和RasServer类都继承实现于RegularJob类,从上一节的gnugk的多线程分析中,可以知道,这个RegularJob类就是周期性的执行某一个具体任务,而具体怎么执行,由RegularJob::Exec方法约定,从类关系图中可以看到直接继承自RegularJob类的是SocketsReader类,因此由SocketsReader类来实现这个约定的方法。
从实现上可以看到,主要是有三个动作:
第一:调用BuildSelectList(slist),创建可监听的socket列表对象(SocketSelectList对象)
第二:调用SelectSockets(slist),轮询监听该列表对象;
第三:当有socket状态可读时,通过ReadSocket(dynamic_cast<IPSocket *>(slist[i]))方法,进一步实现状态读取和连接建立。但SocketsReader::ReadSocket方法是一个纯虚函数,所以具体如何处理这个连接请求或者如何读取数据,由SocketsReader的子类决定。

void SocketsReader::Exec()
{
	ReadLock cfglock(ConfigReloadMutex);
	SocketSelectList slist(GetName());

	if (BuildSelectList(slist)) {
		if (SelectSockets(slist)) {	// SelectSockets() will unlock ConfigReloadMutex while waiting
			int ss = slist.GetSize();
			for (int i = 0; i < ss; ++i)
#ifdef LARGE_FDSET
				ReadSocket(slist[i]);
#else
				ReadSocket(dynamic_cast<IPSocket *>(slist[i]));
#endif
		}
		CleanUp();
	} else {
		CleanUp();
		ConfigReloadMutex.EndRead();
		Wait(SOCKETSREADER_IDLE_TIMEOUT);
		ConfigReloadMutex.StartRead();
	}
}

而从类图上,可以明显知道,对于如何建立TCP连接,由TCPServer类来实现,而如何读取Ras消息,由RasServer类来实现。因此,TCP监听任务通过TCPServer类单独一个线程在执行,而UDP消息(即RAS消息)读取任务通过RasServer类单独一个线程在执行。

因此,需要分析下RasServer和TCPServer是如何重载实现这个ReadSocket方法的。

2.2.1 RasServer的ReadSocket方法
void RasServer::ReadSocket(IPSocket *socket)
{
	RasListener *listener = static_cast<RasListener *>(socket);
	if (GatekeeperMessage *msg = listener->ReadRas()) {
		CreateRasJob(msg);
	}
}

从上面可以看到,对于UDP消息,通过各RasListener的ReadRas方法,读取成一个GatekeeperMessage对象msg,再通过CreateRasJob(msg),另启一个线程去具体处理这个GatekeeperMessage消息;具体如何处理,后面另起章节来说明。

2.2.2 TCPServer的ReadSocket方法
void TCPServer::ReadSocket(IPSocket * socket)
{
	// don't accept new calls when shutdown is already in progress
	if (IsGatekeeperShutdown()) {
	   	// 当前正在退出时,不接受新的连接
	}

	PTRACE(4, GetName() << "\tAccept request on " << socket->GetName());
	// rate limiting
	if (cps_limit > 0) {
        //超过限制后,拒绝连接
	}

	TCPListenSocket *listener = dynamic_cast<TCPListenSocket *>(socket);
	if (!listener)
		return;

	ServerSocket *acceptor = listener->CreateAcceptor();
	if (acceptor && acceptor->Accept(*listener)) {
		PTRACE(6, GetName() << "\tAccepted new connection on " << socket->GetName() << " from " << acceptor->GetName());
		CreateJob(acceptor, &ServerSocket::Dispatch, "Acceptor");
	} else {
		PTRACE(4, GetName() << "\tAccept failed on " << socket->GetName());
		delete acceptor;
	}
}

从上面的主要代码片断可以看到,对于TCP连接请求,主要是通过TCPListenSocket::CreateAcceptor创建一个接收对象ServerSocket,由该接收对象的Accept方法(acceptor->Accept(*listener))实现TCP连接的建立,而后创建一个线程,去执行后续具体的数据;处理线程执行的具体方法由ServerSocket::Dispatch实现;
这里TCPListenSocket::CreateAcceptor是一个纯虚函数,其具体如何创建相应的ServerSocket对象,由其子类实现;而后ServerSocket::Dispatch也是一个纯虚函数,即如何具体的处理相应的TCP数据,由相应的ServerSocket子类来实现。

从上面分析,可以看到不管是UDP数据,还是TCP数据都是另一个线程去具体执行的。

2.3 创建监听对象

从上面的2.1和2.2章节,我们知道了监听对象和监听线程是哪些。那么监听对象是如何被创建的?又是如何被添加到监听线程的?

实际上RasServer是通过GkInterface类来在特定的IP地址上建立起各个监听,可以说GkInterface类是对各Listener类的一个工厂类;监听对象的创建和添加,主要步骤有3点:

第一:GkInterface *gkif = CreateInterface(addr), RasServer通过CreateInterface(addr)方法,在特定IP上创建一个GkInterface对象;

第二:gkif->CreateListeners(this),GkInterface对象内部创建各类监听对象。

其内部对应关系如下:
GkInterface::CreateRasListener方法创建RasListener类对象;
GkInterface::CreateMulticastListener方法创建MulticastListener类对象;
GkInterface::CreateCallSignalListener方法创建CallSignalListener类对象;
GkInterface::CreateTLSCallSignalListener方法创建TLSCallSignalListener类对象;
GkInterface::CreateMultiplexH245Listener方法创建MultiplexH245Listener类对象;
GkInterface::CreateStatusListener方法创建StatusClient类对象;

第三:在创建了各监听对象后,GkInterface实例内部也有相应的成员变量,保存各监听对象,同时重要的是通过RasServer的AddListener方法,添加回相应的UDP和TCP监听线程;这里AddListener是重载的方法,void AddListener(RasListener *)方法负责添加UDP的监听对象,即保存到RasServer自身上;void AddListener(TCPListenSocket *)方法负责添加TCP的监听对象,保存在RasServer内部的一个TCPServer *listeners对象上;这样就把各监听对象添加回了两个主要的监听线程上。

三、总结

从前面分析,可以基本总结如下:
1 RasServer通话GkInterface类对象在各个具体的ip地址上创建各类的监听对象;
2 通过RasServer的AddListener方法,将各类监听对象按UDP和TCP协议,分别添加到RasServer对象的工作线程内和TCPServer对象的工作线程内;
3 对于监听线程内部,主要是周期性的创建监听列表(SocketSelectList对象),实施监听;
4 对于特定可读socket,通过RasServer的ReadSocket和TCPServer的ReadSocket方法,来封闭数据的读取或连接的建立;
5 对于具体的UDP数据(即RAS消息)或者TCP数据(如Setup),都是通过另启一个线程去处理。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值