Mangos服务器框架设计分析(二)

在上一篇博文中,我们分析了Mangos服务器的登陆服务器,Mangos登陆服务器主要就是验证用户的合法性,并且针对通过验证的用户发送游戏列表服务器列表,用户选择了相关游戏服务器时所涉及到的流程就是今天本博文需要分析的,Mangos游戏服务器的主要架构就是一对多的关系,下层的I/O是多线程的,而上层的游戏主逻辑是单线程的,两者的胶合部分就是已经封装好的session模块,好了,下面就来看看具体的流程细节吧,首先来看看游戏服务器(世界服务器)的主流程吧,代码如下:

extern int main(int argc,char** argv)
{
	const char* cfg_file = _MANGOSD_CONFIG;
	const char* options = ":a:c:s";

	ACE_Get_Opt cmd_opts(argc,argv,options);
	cmd_opts.long_option("version",'v',ACE_Get_Opt::NO_ARG);
	cmd_opts.long_option("ahbot",'a',ACE_Get_Opt::ARG_REQUIRED);

	char serviceDaemonMode = '\0';

	int option;
	while((option = cmd_opts())!= EOF)
	{
		switch (option)
		{
		case 'a':
			break;
		case 'c':
			cfg_file = cmd_opts.opt_arg();
			break;
		case 'v':
			return 0;
		case 's':
			break;
		default:
			break;
		}
	}

	if(!sConfig.SetSource(cfg_file))
	{
		printf("Could not find configuration file %s\n",cfg_file);
		return 1;
	}
	return sMaster.Run();
}

上述代码可能跟Mangos代码有部分出入,主要是为了看清整个流程而进行了精简,在上述代码中主要就是那个sMaster.run了,其他的就是一些配置方面的加载功能,好了,我们就顺着sMaster往下看看,代码如下:

int Master::Run()
{
	std::string pidfile = sConfig.GetStringDefault("PidFile","");
	if(!pidfile.empty())
	{
		uint32 pid = CreatePIDFile(pidfile);
		if(!pid)
		{
			printf("Cannot create pid file %s\n",pidfile.c_str());
			return 1;
		}
	}
	if(!_StartDB())
	{
		return 1;
	}

	//sWorld.SetInitialWorldSettings();

	CharacterDatabase.AllowAsyncTransactions();
	WorldDatabase.AllowAsyncTransactions();
	LoginDatabase.AllowAsyncTransactions();

	_HookSignals();

	ACE_Based::Thread world_thread(new WorldRunnable());
	world_thread.setPriority(ACE_Based::Highest);

	ACE_Based::Thread* cliThread = NULL;

#ifdef WIN32
	if(sConfig.GetBoolDefault("Console.Enable",true) && (m_ServiceStatus == -1))
#else
	if(sConfig.GetBoolDefault("Console.Enable",true))
#endif
	{
		cliThread = new ACE_Based::Thread(new CliRunnable());
	}

	ACE_Based::Thread* rar_thread = NULL;
	if(sConfig.GetBoolDefault("Ra.Enable",false))
	{
		rar_thread = new ACE_Based::Thread(new RARunnable());
	}

	//uint16 port = sWorld.getConfig(CONFIG_UINT32_PORT_WORLD);
	uint16 port = 17777;
	std::string ip = sConfig.GetStringDefault("BindIP","0.0.0.0");
	if(sWorldSocketMgr->StartNetwork(port,ip) == -1)
	{
		printf("Failed to start network\n");
		World::StopNow(ERROR_EXIT_CODE);
	}
	sWorldSocketMgr->Wait();

	_UnHookSignals();

	world_thread.wait();

	if(rar_thread)
	{
		rar_thread->wait();
		rar_thread->destroy();
		delete rar_thread;
	}

	//clearOnlineAccounts();

	//sMassMailMgr.Update(true);

	CharacterDatabase.HaltDelayThread();
	WorldDatabase.HaltDelayThread();
	LoginDatabase.HaltDelayThread();

#ifdef WIN32
	cliThread->wait();
#else
	cliThread->destroy();
#endif
	delete cliThread;

	return World::GetExitCode();
}

在上面的代码中,我们主要关注三点东西:1.数据库的初始化操作,2.世界主线程(游戏主线程)的初始化以及启动,3.底层的I/O线程的初始化以及启动,好了,我们就按照这三点来稍微看看,首先看第一点(1.数据库的初始化操作),代码如下:

bool Master::_StartDB()
{
	std::string dbstring = sConfig.GetStringDefault("WorldDatabaseInfo","");
	int nConnections = sConfig.GetIntDefault("WorldDatabaseConnections",1);
	if(dbstring.empty())
	{
		printf("Database not specified in configuration file\n");
		return false;
	}
	printf("World Database total connection:%d\n",nConnections+1);

	if(!WorldDatabase.Initialize(dbstring.c_str(),nConnections))
	{
		printf("Cannot connect to world database %s\n",dbstring.c_str());
		return false;
	}

	dbstring = sConfig.GetStringDefault("CharacterDatabaseInfo","");
	nConnections = sConfig.GetIntDefault("CharacterDatabaseConnections",1);
	if(dbstring.empty())
	{
		printf("Character Database not specified in Configuration file\n");
		return false;
	}

	printf("Character Database total connections:%d\n",nConnections+1);
	if(!CharacterDatabase.Initialize(dbstring.c_str(),nConnections))
	{
		printf("Cannot connect to Character database %s\n",dbstring.c_str());
		WorldDatabase.HaltDelayThread();
		return false;
	}

	dbstring = sConfig.GetStringDefault("LoginDatabaseInfo","");
	nConnections = sConfig.GetIntDefault("LoginDatabaseConnections",1);
	if(dbstring.empty())
	{
		printf("Login Database not specified in Configuration file\n");
		WorldDatabase.HaltDelayThread();
		CharacterDatabase.HaltDelayThread();
		return true;
	}

	printf("Login Database total connections:%d\n",nConnections+1);
	if(!LoginDatabase.Initialize(dbstring.c_str(),nConnections))
	{
		printf("Cannot connect to login database\n");
		WorldDatabase.HaltDelayThread();
		CharacterDatabase.HaltDelayThread();
		return false;
	}

	//clearOnlineAccounts();
	return true;
}

在这段代码中,我们主要看下数据库的初始化操作,在mangos中主要涉及到三个数据库,通过上面的代码也可以看出来,初始化数据库的接口时Initialize,传入的参数是创建数据的sql语句以及数据库连接数,针对数据库部分我们会在后面的博文中进行详细的分析,在此只需要记住数据库初始化的大致流程以及传入的参数即可,好了,接着上面,我们来看看第二点(游戏主线程的初始化以及启动),这段代码如下:

	ACE_Based::Thread world_thread(new WorldRunnable());
	world_thread.setPriority(ACE_Based::Highest);

这段代码中我们需要关注一下几点:1.ACE_Based::Thread,2.WorldRunnable,3.设置游戏主线程的优先级,我们先来看看ACE_Based::Thread这个吧,代码如下:

Thread::Thread(Runnable* instance) : m_iThreadId(0),m_hThreadHandle(0),m_task(instance)
{
	if(m_task)
		m_task->incReference();
	bool _start = start();
	assert(_start);
}
bool Thread::start()
{
	if(m_task ==0 || m_iThreadId !=0)
		return false;
	bool res = (ACE_Thread::spawn(&Thread::ThreadTask,(void*)m_task,THREADFLAG,&m_iThreadId,&m_hThreadHandle)==0);
	if(res)
		m_task->incReference();
	return res;
}

上面代码只是从Threading.cpp文件中提取出来的,在这里面可以看出通过ACE_Thread::spawn接口创建了相应的线程,并且设置线程执行函数,我们再来看看这个线程的执行函数吧(m_task),代码如下:

	class Runnable
	{
	public:
		virtual ~Runnable(){}
		virtual void run() = 0;

		void incReference()
		{
			++m_refs;
		}
		void decReference()
		{
			if(!--m_refs)
				delete this;
		}
	private:
		ACE_Atomic_Op<ACE_Thread_Mutex,long> m_refs;
	};

在Thread中引用了Runnable对象,也就是我们的m_task,从上面的代码中可以看出我们的m_task具有引用技术效果,并且该类只是一个充当接口的抽象函数,我们来看看具体的实例吧,代码如下:

class WorldRunnable : public ACE_Based::Runnable
{
public:
	void run() override;
};

void WorldRunnable::run()
{
	WorldDatabase.ThreadStart();
	//sWorld.InitResultQueue();

	uint32 realCurrTime = 0;
	uint32 realPrevTime = WorldTimer::tick();

	uint32 prevSleepTime = 0;

	while(!World::IsStopped())
	{
		++World::m_worldLoopCounter;
		realCurrTime = WorldTimer::getMSTime();

		uint32 diff = WorldTimer::tick();
		sWorld.Update(diff);
		realPrevTime = realCurrTime;

		if(diff <= WORLD_SLEEP_CONST + prevSleepTime)
		{
			prevSleepTime = WORLD_SLEEP_CONST + prevSleepTime - diff;
			ACE_Based::Thread::Sleep(prevSleepTime);
		}
		else
			prevSleepTime = 0;

#ifdef WIN32
		if(m_ServiceStatus ==0) World::StopNow(SHUTDOWN_EXIT_CODE);
		while(m_ServiceStatus ==2) Sleep(1000);
#endif
	}
	//sWorld.KickAll();

	sWorld.UpdateSessions(1);

	//sBattleGroundMgr.DeleteAllBattlenGrounds();

	sWorldSocketMgr->StopNetwork();

	//MapManager::Instance()->UnloadAll();

	WorldDatabase.ThreadEnd();
}

从上述代码中可以看出,我们的游戏主线程的主要的流程,首先肯定是一个大的无限循环函数了,并且定时更新我们的游戏服务器相关状态(通过sWorld.update接口来实现),之后针对游戏主线程关闭时进行了相关的清理工作,大致的流程就是这样,代码如下:

void World::Update(uint32 diff)
{
    ///- Update the different timers
    for (int i = 0; i < WUPDATE_COUNT; ++i)
    {
        if (m_timers[i].GetCurrent() >= 0)
            m_timers[i].Update(diff);
        else
            m_timers[i].SetCurrent(0);
    }

    ///- Update the game time and check for shutdown time
    _UpdateGameTime();

    ///-Update mass mailer tasks if any
    sMassMailMgr.Update();

    /// Handle daily quests reset time
    if (m_gameTime > m_NextDailyQuestReset)
        ResetDailyQuests();

    /// Handle weekly quests reset time
    if (m_gameTime > m_NextWeeklyQuestReset)
        ResetWeeklyQuests();

    /// Handle monthly quests reset time
    if (m_gameTime > m_NextMonthlyQuestReset)
        ResetMonthlyQuests();

    /// <ul><li> Handle auctions when the timer has passed
    if (m_timers[WUPDATE_AUCTIONS].Passed())
    {
        m_timers[WUPDATE_AUCTIONS].Reset();

        ///- Update mails (return old mails with item, or delete them)
        //(tested... works on win)
        if (++mail_timer > mail_timer_expires)
        {
            mail_timer = 0;
            sObjectMgr.ReturnOrDeleteOldMails(true);
        }

        ///- Handle expired auctions
        sAuctionMgr.Update();
    }

    /// <li> Handle AHBot operations
    if (m_timers[WUPDATE_AHBOT].Passed())
    {
        sAuctionBot.Update();
        m_timers[WUPDATE_AHBOT].Reset();
    }

    /// <li> Handle session updates
    UpdateSessions(diff);

    /// <li> Handle weather updates when the timer has passed
    if (m_timers[WUPDATE_WEATHERS].Passed())
    {
        ///- Send an update signal to Weather objects
        for (WeatherMap::iterator itr = m_weathers.begin(); itr != m_weathers.end();)
        {
            ///- and remove Weather objects for zones with no player
            // As interval > WorldTick
            if (!itr->second->Update(m_timers[WUPDATE_WEATHERS].GetInterval()))
            {
                delete itr->second;
                m_weathers.erase(itr++);
            }
            else
                ++itr;
        }

        m_timers[WUPDATE_WEATHERS].SetCurrent(0);
    }
    /// <li> Update uptime table
    if (m_timers[WUPDATE_UPTIME].Passed())
    {
        uint32 tmpDiff = uint32(m_gameTime - m_startTime);
        uint32 maxClientsNum = GetMaxActiveSessionCount();

        m_timers[WUPDATE_UPTIME].Reset();
        LoginDatabase.PExecute("UPDATE uptime SET uptime = %u, maxplayers = %u WHERE realmid = %u AND starttime = " UI64FMTD, tmpDiff, maxClientsNum, realmID, uint64(m_startTime));
    }

    /// <li> Handle all other objects
    ///- Update objects (maps, transport, creatures,...)
    sMapMgr.Update(diff);
    sBattleGroundMgr.Update(diff);
    sOutdoorPvPMgr.Update(diff);

    ///- Delete all characters which have been deleted X days before
    if (m_timers[WUPDATE_DELETECHARS].Passed())
    {
        m_timers[WUPDATE_DELETECHARS].Reset();
        Player::DeleteOldCharacters();
    }

    // execute callbacks from sql queries that were queued recently
    UpdateResultQueue();

    ///- Erase corpses once every 20 minutes
    if (m_timers[WUPDATE_CORPSES].Passed())
    {
        m_timers[WUPDATE_CORPSES].Reset();

        sObjectAccessor.RemoveOldCorpses();
    }

    ///- Process Game events when necessary
    if (m_timers[WUPDATE_EVENTS].Passed())
    {
        m_timers[WUPDATE_EVENTS].Reset();                   // to give time for Update() to be processed
        uint32 nextGameEvent = sGameEventMgr.Update();
        m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent);
        m_timers[WUPDATE_EVENTS].Reset();
    }

    /// </ul>
    ///- Move all creatures with "delayed move" and remove and delete all objects with "delayed remove"
    sMapMgr.RemoveAllObjectsInRemoveList();

    // update the instance reset times
    sMapPersistentStateMgr.Update();

    // And last, but not least handle the issued cli commands
    ProcessCliCommands();

    // cleanup unused GridMap objects as well as VMaps
    sTerrainMgr.Update(diff);
}

这个函数包括了游戏主线程的主要工作流程了,在这里面我们需要关注一下UpdateSessoin和ProcessCliCommand这两个函数,UpdateSession主要更新目前处在游戏中角色的一些游戏状态(主要是一些收发消息以及相关的处理),这个函数很重要,游戏中角色的所有的操作都跟session有关系,这部分内容会在后续的博文中进行分析,在此只需要其大致的作用即可,另一个ProcessCliCommand函数主要是接收和处理客户端发送过来的一些GM指令之类的消息,好了,基本上我们的游戏主逻辑线程大致的流程就是这样,首先创建线程,设置线程的执行函数并执行,在执行函数中囊括了游戏主线程的基本的一些操作,这部分内容表面上其实很简单,但是如果具体仔细分析的话,有些地方还是需要好好地体会一下的,好了,游戏主线程就分析到这里,下面我们来看看底层的I/O线程吧,代码如下:

	uint16 port = 17777;
	std::string ip = sConfig.GetStringDefault("BindIP","0.0.0.0");
	if(sWorldSocketMgr->StartNetwork(port,ip) == -1)
	{
		printf("Failed to start network\n");
		World::StopNow(ERROR_EXIT_CODE);
	}
	sWorldSocketMgr->Wait();

int WorldSocketMgr::StartReactiveIO(ACE_UINT16 port,const char* address)
{
	m_UseNoDelay = sConfig.GetBoolDefault("Network.TcpNodelay",true);
	int num_threads = sConfig.GetIntDefault("Network.Threads",1);

	if(num_threads <0)
	{
		printf("Network.Threads is wrong in you config file\n");
		return -1;
	}

	m_NetThreadCount = static_cast<size_t>(num_threads+1);
	m_NetThreads = new ReactorRunnable[m_NetThreadCount];
	printf("Max allowed socket connections %d\n",ACE::max_handles());

	m_SockOutKBuff = sConfig.GetIntDefault("Network.OutKBuff",-1);
	m_SockOutUBuff = sConfig.GetIntDefault("Network.OutUBuff",65535);

	if(m_SockOutUBuff<0)
	{
		printf("Network.OUTBUFF is wrong in your config file\n");
		return -1;
	}

	WorldSocket::Acceptor* acc = new WorldSocket::Acceptor();
	m_Acceptor = acc;

	ACE_INET_Addr listen_addr(port,address);
	if(acc->open(listen_addr,m_NetThreads[0].GetReactor(),ACE_NONBLOCK) == -1)
	{
		printf("Failed to open acceptor,check if the port is free\n");
		return -1;
	}
	for(size_t i =0;i<m_NetThreadCount;++i)
		m_NetThreads[i].Start();
	return 0;
}

int WorldSocketMgr::StartNetwork(ACE_UINT16 port,std::string& address)
{
	m_port = port;
	m_addr = address;

	if(StartReactiveIO(port,address.c_str()) ==-1)
		return -1;
	return 0;
}

首先是在Master.cpp中调用WorldSocketMgr.cpp中的StartNetwork函数,其实这个函数主要是作为一个封装函数而存在,真正起作用的是StartReactiveIO函数,在这个函数中根据配置创建了多个线程,并且每个线程的创建流程类似于我们的游戏主逻辑线程,而其中一个线程主要作为监听和接收客户端的请求而存在,其他的线程会处理相应的消息事件,我们来稍微看看reactorRunnable这个类,这个就是我们的I/O线程主要流程,代码如下:

class ReactorRunnable : protected ACE_Task_Base
{
public:
	ReactorRunnable():
		m_Reactor(0),
		m_Connections(0),
		m_ThreadId(-1)
	{
		ACE_Reactor_Impl* imp = NULL;
#if defined (ACE_HAS_EVENT_POLL) || defined(ACE_HAS_DEV_POLL)
		imp = new ACE_Dev_Poll_Reactor();
		imp->max_notify_iterations(128);
		imp->restart(1);
#else
		imp = new ACE_TP_Reactor();
		imp->max_notify_iterations(128);
#endif
		m_Reactor = new ACE_Reactor(imp,1);
	}

	virtual ~ReactorRunnable()
	{
		Stop();
		Wait();
		delete m_Reactor;
	}

	void Stop()
	{
		m_Reactor->end_reactor_event_loop();
	}

	int Start()
	{
		if(m_ThreadId != -1)
			return -1;
		return (m_ThreadId = activate());
	}

	void Wait()
	{
		ACE_Task_Base::wait();
	}

	long Connections()
	{
		return static_cast<long>(m_Connections.value());
	}

	int AddSocket(WorldSocket* sock)
	{
		ACE_GUARD_RETURN(ACE_Thread_Mutex,Guard,m_NewSockets_lock,-1);
		++m_Connections;
		sock->AddReference();
		sock->reactor(m_Reactor);
		m_NewSockets.insert(sock);
		return 0;
	}

	ACE_Reactor* GetReactor()
	{
		return m_Reactor;
	}

protected:
	void AddNewSockets()
	{
		ACE_GUARD(ACE_Thread_Mutex,Guard,m_NewSockets_lock);
		if(m_NewSockets.empty())
			return;

		for(SocketSet::const_iterator iter = m_NewSockets.begin();iter != m_NewSockets.end();iter++)
		{
			WorldSocket* sock = *iter;
			if(sock->IsClosed())
			{
				sock->RemoveReference();
				--m_Connections;
			}
			else
				m_Sockets.insert(sock);
		}
		m_NewSockets.clear();
	}

	virtual int svc()
	{
		printf("NetWork Thread starting\n");
		WorldDatabase.ThreadStart();
		assert(m_Reactor);

		SocketSet::iterator i,t;
		while(!m_Reactor->reactor_event_loop_done())
		{
			ACE_Time_Value interval(0,10000);
			if(m_Reactor->run_reactor_event_loop(interval) == -1)
				break;

			AddNewSockets();

			for(i=m_Sockets.begin();i != m_Sockets.end();i++)
			{
				if((*i)->Update() == -1)
				{
					t = i;
					++i;
					(*t)->CloseSocket();
					(*t)->RemoveReference();
					m_Sockets.erase(t);
				}
				else
					++i;
			}
		}
		WorldDatabase.ThreadEnd();
		printf("NetWork Thread Exiting\n");
		return 0;
	}
	private:
		typedef ACE_Atomic_Op<ACE_SYNCH_MUTEX,long> AtomicInt;
		typedef std::set<WorldSocket*> SocketSet;

		ACE_Reactor* m_Reactor;
		AtomicInt m_Connections;
		int m_ThreadId;

		SocketSet m_Sockets;
		SocketSet m_NewSockets;

		ACE_Thread_Mutex m_NewSockets_lock;
};

上述代码中可以看出,我们的每个I/O线程都包含了一个reactor对象,这个对象的作用想必大部分人应该还是很清楚的,当有事件到来时,负责响应在其上注册的事件,并通知执行有关事件回调函数,在这里面我们需要关注一下activate这函数,这个函数的作用就是创建i/o线程,并且调用svc函数,这个函数就是本i/o线程的主体了,负责处理在其reactor对象注册的I/O事件,在这个函数里面我们需要注意一下,里面其实包含了两类操作,第一类就是数据包的接收,第二类就是数据报的发送,第一类主要是通过我们的

run_reactor_event_loop函数来实现的,其底层通过调用handle_event接口,并最终会调用注册在本线程的reactor对象事件句柄也就是WorldSocket对象,其中会分别调用handle_input函数,第二类数据包的发送就是靠WorldSocket->update函数来触发,有人可能会有疑惑就是当有客户端请求来时,是如何处理的呢,其实很简单,由于我们已经在第一个I/O线程中注册了acceptor事件,所有当有客户端请求来时,会在第一个线程中进行处理,处理完之后,会将已经建立的新WorldSocket注册到最小连接数的I/O线程上,而各自的线程会负责处理每个注册在自己的reactor对象上的I/O事件了,至于如何处理的流程请参考WorldSocket.cpp文件,在这个流程中我们需要注意的是这里面会有将收到发送给上层消息转存在每个session队列里面,原理很简单,在此不再多说了,好了,接下去就是我们的第三点:设置游戏主线程的优先级了,设置这个的目的就是确保我们的游戏逻辑线程能够以较高的效率来处理游戏相关消息以及事件,具体的代码请参见Threading.cpp,

好了针对mangos的游戏服务器(世界服务器)介绍就到这里了,其实主要是依靠ACE提供好的一套事件处理框架,我们只需要通过继承和引用的方式即可,例如在mangos里面的Acceptor以及我们的I/O线程,基本上理解了这两个对象,mangos底层的一些消息通信机制基本就掌握了,其他的基本就是消息的末端处理了以及相关的时间同步更新之类的,这些都应该算是上层应用了,在I/O层我们只需要关注消息的走向即可,好了,说的有点多,大家可以自行分析一下,还是蛮有收获的,下一篇我们会针对mangos内使用的数据库进行简要的分析研究,谢谢了。

如果需要,请注明转载,多谢

时间: 2024-11-05 13:23:14

Mangos服务器框架设计分析(二)的相关文章

Skynet服务器框架(二) C源码剖析启动流程

引言: 之前我们已经完成了在Linux下配置安装 skynet 的环境,并成功启动了 skynet 服务框架,为了从底层更好地理解整个框架的实现过程,我们有必要剖析一下源码,由于底层的源码都是用C语言写的,lua脚本基本是用来进行业务层开发,所以我们从C源码开始解读框架.打开下载包的 skynet-src 目录,这里是skynet框架的核心C源码,接下来我们就要来解读 skynet_main.c 和 skynet_start.c 这两个与skynet启动相关的C源码. 1.入口函数和初始化: 我

跨平台网络通信与服务器框架 acl 3.2.0 发布

acl 3.2.0 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/FreeBsd/MacOS 平台:整个 acl 项目主要包含三个函数库:lib_acl(纯C开发的基础库,主要包含网络通信及服务器编程框架以及其它丰富的功能).lib_protocol(包含 HTTP/PING/SMTP 通信协议的C语言实现).lib_fiber(网络协程库).lib_acl_cpp

acl 网络通信与服务器框架库示例列表

跨平台网络通信及服务器框架库 --- "acl" 项目里有大量的测试及应用示例,主要有三个示例集合,如下: 1.acl/samples:该目录下的例子主要是基于 lib_acl 及 lib_protocol 两个库的例子-    1.1 acl: 打印当前 acl 库版本号程序-    1.2 aio/client: 非阻塞 io 客户端-    1.3 aio/server: 非阻塞 io 服务器-    1.4 base64: base64 编/解码程序-    1.5 btree

跨平台网络通信与服务器框架(acl) 新版本发布

acl 3.1.0 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/FreeBsd/MacOS 平台:整个 acl 项目主要包含三个函数库:lib_acl(纯C开发的基础库,主要包含网络通信及服务器编程框架以及其它丰富的功能).lib_protocol(包含 HTTP/PING/SMTP 通信协议的C语言实现).lib_acl_cpp(基于 lib_acl 及 lib

协作半驻留式服务器程序开发框架 --- 基于 Postfix 服务器框架改造

一.概述 现在大家在和Java, PHP, .net写应用程序时,都会用到一些成熟的服务框架,所以开发效率是比较高的.而在用C/C++写服务器程序时,用的就五花八门了,有些人用ACE, 有些人用ICE(号称比ACE强许多),等等,这类服务器框架及库比较丰富,但入门门槛比较高,所以更多的人是自己直接写服务器程序,初始写时觉得比较简 单,可时间久了,便会觉得难以扩展,性能低,容易出错.其实,Postfix 作者为我们提供了一个高效.稳定.安全的服务器框架模型,虽然Postfix主要用作邮件系统的 m

游戏服务器框架分析

一个大型的网落游戏服务器应该包含几个模块:网络通讯,业务逻辑,数据存储,守护监控(不是必须),其中业务逻辑可能根据具体需要,又划分为好几个子模块. 这里说的模块可以指一个进程,或者一个线程方式存在,本质上就是一些类的封装. 对于服务器的并发性,要么采用单进程多线程,要么采用多进程单线程的方式,说说两种方式的优缺点: 一.单进程多线程的服务器设计模式,只有一个进程,但一个进程包好多个线程: 网络通讯层,业务逻辑,数据存储,分别在独立的线程中,无守护进程. 优点: 1.数据共享和交换方便,使用全局变

写自己的socket框架(二)

1.开始正常监听以后,就要开始接受数据了,整体流程图如下: 2.上一节看到我们在程序初始化的时候,初始化了很多个SocketConnection,用于管理客户端的链接,那应用层如何来操作,又什么时候来接受数据?于是我们便有了SocketSession,用于给应用层来管理整个会话过程,代码如下: public class SocketSession : IDisposable { public string SessionId { get; private set; } private Syste

基于JDK7 NIO2的高性能web服务器实践之二(转)

前一篇博客,我简单提了下怎么为NIO2增加TransmitFile支持,文件传送吞吐量是一个性能关注点,此外,并发连接数也是重要的关注点. 不过JDK7中又一次做了简单的实现,不支持同时投递多个AcceptEx请求,只支持一次一个,返回后再投递.这样,客户端连接的接受速度必然大打折扣.不知道为什么sun会做这样的实现,WSASend()/WSAReceive()一次只允许一个还是可以理解,毕竟简化了编程,不用考虑封包乱序问题.也降低了内存耗尽的风险.AcceptEx却没有这样的理由了. 于是再一

Skynet服务器框架(一) Linux下的安装和启动

根据云风博客的描述,Skynet 的核心功能就是解决一个问题: 把一个符合规范的 C 模块,从 动态库(so文件)中启动起来,绑定一个永不重复(即使模块退出)的数字id做为其 handle.模块 被称为 服务(Service),服务间可以自由发送消息. 每个 模块 可以向 Skynet 框架注册一个 callback 函数,用来接收发给它的消息: 每个服务都是被一个个 消息包 驱动,当没有包到来的时候,它们就会处于 挂起状态,此状态对 CPU 资源零消耗.如果需要自主逻辑,则可以利用 Skyne