游戏服务器之主动连接线程池

本文内容是游戏服务器之主动连接线程池,功能是实现处理服务器之间主动发起的连接的网络的数据收发。

一般来说,在场景服务器里连接其他服务器进程的连接是启动了新的线程的(如 场景服务器的主动连接:db服务器、社会服务器、日志服务器)。

但是也有不启动线程的客户端连接的做法。对于这类方式,他们的网络收发是统一由主动连接线程池来处理。

目前使用案例:

例如游戏服务器的网关服务器的主动连接:社会服务器、db服务器、所有场景服务器。


本文内容:

1、线程类型

(1)连接测试线程

(2)验证线程

(3)网络处理线程

2、连接池初始化

3、创建连接到连接池

4、主动连接会话结构


1、线程类型

客户端连接池有3类:连接测试线程、验证线程、网络收发处理线程。

(1)连接测试线程

connection_thread_manager<check_connect_thread> checkconnectThread

连接测试线程是来处理主动连接是否能连接成功,如果成功就转到验证线程来处理,如果失败就一直连接(几秒的间隔)。

(2)验证线程

connection_thread_manager<check_wait_thread> checkwaitThread

在构造函数里创建epoll描述符,

在例程里处理转过来的连接任务列表,处理该TCP连接的验证,如果验证不通过,需要回收这个连接。

验证方式是处理该连接的epoll事件看有没有错误,处理连接自定义的checkRebound ,这个根据需求,是否需要检查该连接的合法性。  

如:中心服务器主动连接到登录服务器(使用主动连接线程池),需要在登录服务器创建会话对象后,在同步线程中返回消息给中心服务器后,中心服务器验证线程的对该主动连接的验证工作才算成功通过。

void check_wait_thread::run()
{
	...
	if (!tasks.empty())
	{
		int retcode = epoll_wait(kdpfd, &epfds[0], task_count, 0);
		if (retcode > 0)
		{
			for(int i = 0; i < retcode; i++)
			{
				tcp_client *task = (tcp_client *)epfds[i].data.ptr;
				if (epfds[i].events & (EPOLLERR | EPOLLPRI))
				{
					//套接口出现错误
					remove(task);
					task->resetState();
				}
				else if (epfds[i].events & EPOLLIN)
				{
					switch(task->checkRebound())//验证连接
					{
						case 1:
						//验证成功,获取下一个状态
						remove(task);
						if (!pool->addMain(task))
						task->resetState();
						break;
						case 0:
						//超时,下面会处理
						break;
						case -1:
						//验证失败,回收任务
						remove(task);
						task->resetState();
						break;
					}
				}
			}
		}
	}
	...
}


(3)网络处理线程

connection_thread_manager<main_client_thread> clienttaskThread

处理网络的实际数据的收发。

详细参考:http://blog.csdn.net/chenjiayi_yun/article/details/31765803 内容大致是相同的。

只是那里是被动连接的数据收发处理为例,这里是主动连接的数据收发处理。


2、连接池初始化

初始化线程池中的各类线程的数量

bool tcp_client_pool::init(const uint32 perThreadSize)
{
	//测试线程数量1个
	if (!checkconnectThread.init(1, 1, "checkconnectThread",this))
	{
		return false;
	}
	//验证线程数量1个
	if (!checkwaitThread.init(1, 1, "checkwaitThread",this))
	{
		return false;
	}
	main_client_thread::settMaxSize(perThreadSize);
	int maxThreadCount = (maxConns + main_client_thread::getMaxSize() -1)/main_client_thread::getMaxSize();
	//网络处理线程数量跟连接最大限制有关
	if (!clienttaskThread.init(1, maxThreadCount, "clienttaskThread",this))
	{
		return false;
	}
	return true;
}


3、创建连接到连接池

例如创建主动到数据库的连接,并添加到线程池。

网关跟档案服务器的连接 。 

dbClient = new db_client("Record", serverEntry->pstrExtIP, serverEntry->wdExtPort,serverEntry->ServerID);
if(!dbClient || !clientPool->put(dbClient))
{
	g_log->error("没有足够内存,不能建立db 服务器客户端实例");
	return false;
}
线程池会把连接加入到连接线程,等到线程池启动后,连接测试线程会处理这些主动连接的连接测试,
连接失败会一直连接,连接成功后会验证连接,验证成功后就转到网络处理线程(网络处理线程可能有多个,选择其中一个来处理)。
bool tcp_client_pool::put(tcp_client *task)
{
	check_connect_thread *pThread = checkconnectThread.getOne();
	if (pThread)
	{
		pThread->_add(task);
	}
	else
	{
		g_log->fatal("%s: 不能得到一个空闲线程", __PRETTY_FUNCTION__);
	}
	return true;
}


其中:

class db_client : public client_no_msg_queue ,没有使用消息队列,那么消息的处理就会在线程clienttaskThread(网络数据处理线程)里处理了



4、主动连接会话结构

客户端连接tcp_client包含的内容含:

连接任务类型 uint32 taskType;

底层套接口  tcp_socket *pSocket;

连接状态volatile ConnState state;

是否含有读事件 bool fdsradd;

是否支持压缩 const bool compress;

服务器地址 const std::string ip;

服务器端口 const unsigned short port;

使用套接口收发。

...


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值