整个登录服务器逻辑上是单线程的,一个主循环,处理来自客户端的登录数据包,虽然数据库访问部分是多线程,但是带返回结果集的操作都是通过阻塞操作完成的,多线程只是对无返回结果的SQL做缓冲.
main主要为两个部分:
1.初始化部分,包括读取配置文件,初始化数据库,加载游戏服务器列表,初始化网络部分
2.主循环,处理登录包
extern int main(int argc, char** argv)
{
// 解析传入的参数
// 包括指定配置文件,是否以windows服务方式启动
char const* cfg_file = _REALMD_CONFIG;
char const* options = ":c:s:";
ACE_Get_Opt cmd_opts(argc, argv, options);
cmd_opts.long_option("version", 'v');
char serviceDaemonMode = '\0';
int option;
while ((option = cmd_opts()) != EOF)
{
// 启动参数的解析
switch (option)
{
case 'c':
cfg_file = cmd_opts.opt_arg();
break;
case 'v':
printf("%s\n", _FULLVERSION(REVISION_DATE, REVISION_TIME, REVISION_NR, REVISION_ID));
return 0;
case 's':
{
const char* mode = cmd_opts.opt_arg();
if (!strcmp(mode, "run"))
serviceDaemonMode = 'r';
#ifdef WIN32
else if (!strcmp(mode, "install"))
serviceDaemonMode = 'i';
else if (!strcmp(mode, "uninstall"))
serviceDaemonMode = 'u';
#else
else if (!strcmp(mode, "stop"))
serviceDaemonMode = 's';
#endif
else
{
sLog.outError("Runtime-Error: -%c unsupported argument %s", cmd_opts.opt_opt(), mode);
usage(argv[0]);
Log::WaitBeforeContinueIfNeed();
return 1;
}
break;
}
case ':':
sLog.outError("Runtime-Error: -%c option requires an input argument", cmd_opts.opt_opt());
usage(argv[0]);
Log::WaitBeforeContinueIfNeed();
return 1;
default:
sLog.outError("Runtime-Error: bad format of commandline arguments");
usage(argv[0]);
Log::WaitBeforeContinueIfNeed();
return 1;
}
}
#ifdef WIN32 // windows service command need execute before config read
switch (serviceDaemonMode)
{
case 'i':
if (WinServiceInstall())
sLog.outString("Installing service");
return 1;
case 'u':
if (WinServiceUninstall())
sLog.outString("Uninstalling service");
return 1;
case 'r':
WinServiceRun();
break;
}
#endif
// 加载配置文件
if (!sConfig.SetSource(cfg_file))
{
sLog.outError("Could not find configuration file %s.", cfg_file);
Log::WaitBeforeContinueIfNeed();
return 1;
}
#ifndef WIN32 // posix daemon commands need apply after config read
switch (serviceDaemonMode)
{
case 'r':
startDaemon();
break;
case 's':
stopDaemon();
break;
}
#endif
// 初始化日志
sLog.Initialize();
sLog.outString("%s [realm-daemon]", _FULLVERSION(REVISION_DATE, REVISION_TIME, REVISION_NR, REVISION_ID));
sLog.outString("<Ctrl-C> to stop.\n");
sLog.outString("Using configuration file %s.", cfg_file);
///- Check the version of the configuration file
// 检查配置文件版本
uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0);
if (confVersion < _REALMDCONFVERSION)
{
sLog.outError("*****************************************************************************");
sLog.outError(" WARNING: Your realmd.conf version indicates your conf file is out of date!");
sLog.outError(" Please check for updates, as your current default values may cause");
sLog.outError(" strange behavior.");
sLog.outError("*****************************************************************************");
Log::WaitBeforeContinueIfNeed();
}
// 初始化OpenSSL,登录过程使用了SRP6算法,会用到这个库
DETAIL_LOG("%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
if (SSLeay() < 0x009080bfL)
{
DETAIL_LOG("WARNING: Outdated version of OpenSSL lib. Logins to server may not work!");
DETAIL_LOG("WARNING: Minimal required version [OpenSSL 0.9.8k]");
}
DETAIL_LOG("Using ACE: %s", ACE_VERSION);
#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true);
#else
ACE_Reactor::instance(new ACE_Reactor(new ACE_TP_Reactor(), true), true);
#endif
sLog.outBasic("Max allowed open files is %d", ACE::max_handles());
/// realmd PID file creation
std::string pidfile = sConfig.GetStringDefault("PidFile", "");
if (!pidfile.empty())
{
uint32 pid = CreatePIDFile(pidfile);
if (!pid)
{
sLog.outError("Cannot create PID file %s.\n", pidfile.c_str());
Log::WaitBeforeContinueIfNeed();
return 1;
}
sLog.outString("Daemon PID: %u\n", pid);
}
// 连接realmd数据库
if (!StartDB())
{
Log::WaitBeforeContinueIfNeed();
return 1;
}
// 获得服务器列表,realmlist表中读取所有行
sRealmList.Initialize(sConfig.GetIntDefault("RealmsStateUpdateDelay", 20));
if (sRealmList.size() == 0)
{
sLog.outError("No valid realms specified.");
Log::WaitBeforeContinueIfNeed();
return 1;
}
// cleanup query
// set expired bans to inactive
// 将被封到期的号和ip解封
LoginDatabase.BeginTransaction();
LoginDatabase.Execute("UPDATE account_banned SET active = 0 WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
LoginDatabase.CommitTransaction();
///- Launch the listening network socket
// 打开监听套接字,绑定端口
ACE_Acceptor<AuthSocket, ACE_SOCK_Acceptor> acceptor;
uint16 rmport = sConfig.GetIntDefault("RealmServerPort", DEFAULT_REALMSERVER_PORT);
std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0");
ACE_INET_Addr bind_addr(rmport, bind_ip.c_str());
if (acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1)
{
sLog.outError("MaNGOS realmd can not bind to %s:%d", bind_ip.c_str(), rmport);
Log::WaitBeforeContinueIfNeed();
return 1;
}
// 捕获控制台Ctrl+c信号,捕获到则设置停止标识.停止服务器运行
HookSignals();
///- Handle affinity for multiple processors and process priority on Windows
#ifdef WIN32
{
// 为进程指定cpu
HANDLE hProcess = GetCurrentProcess();
uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
if (Aff > 0)
{
ULONG_PTR appAff;
ULONG_PTR sysAff;
if (GetProcessAffinityMask(hProcess, &appAff, &sysAff))
{
ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
if (!curAff)
{
sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for realmd. Accessible processors bitmask (hex): %x", Aff, appAff);
}
else
{
if (SetProcessAffinityMask(hProcess, curAff))
sLog.outString("Using processors (bitmask, hex): %x", curAff);
else
sLog.outError("Can't set used processors (hex): %x", curAff);
}
}
sLog.outString();
}
bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
if (Prio)
{
if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS))
sLog.outString("realmd process priority class set to HIGH");
else
sLog.outError("Can't set realmd process priority class.");
sLog.outString();
}
}
#endif
// 数据库开启异步事务处理
LoginDatabase.AllowAsyncTransactions();
// maximum counter for next ping
uint32 numLoops = (sConfig.GetIntDefault("MaxPingTime", 30) * (MINUTE * 1000000 / 100000));
uint32 loopCounter = 0;
#ifndef WIN32
detachDaemon();
#endif
///- Wait for termination signal
// 主循环,收到中断信号则停止
while (!stopEvent)
{
// dont move this outside the loop, the reactor will modify it
ACE_Time_Value interval(0, 100000);
if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1)
break;
if ((++loopCounter) == numLoops)
{
loopCounter = 0;
DETAIL_LOG("Ping MySQL to keep connection alive");
LoginDatabase.Ping();
}
#ifdef WIN32
if (m_ServiceStatus == 0) stopEvent = true;
while (m_ServiceStatus == 2) Sleep(1000);
#endif
}
///- Wait for the delay thread to exit
// 数据库线程终止
LoginDatabase.HaltDelayThread();
///- Remove signal handling before leaving
UnhookSignals();
sLog.outString("Halting process...");
return 0;
}
/// Handle termination signals
/** Put the global variable stopEvent to 'true' if a termination signal is caught **/
void OnSignal(int s)
{
// 收到中断信号,修改退出标志
switch (s)
{
case SIGINT:
case SIGTERM:
stopEvent = true;
break;
#ifdef _WIN32
case SIGBREAK:
stopEvent = true;
break;
#endif
}
signal(s, OnSignal);
}
/// 初始化数据库连接
bool StartDB()
{
std::string dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", "");
if (dbstring.empty())
{
sLog.outError("Database not specified");
return false;
}
sLog.outString("Login Database total connections: %i", 1 + 1);
// 连接数据库
if (!LoginDatabase.Initialize(dbstring.c_str()))
{
sLog.outError("Cannot connect to database");
return false;
}
// 查询数据库版本
if (!LoginDatabase.CheckRequiredField("realmd_db_version", REVISION_DB_REALMD))
{
///- Wait for already started DB delay threads to end
LoginDatabase.HaltDelayThread();
return false;
}
return true;
}
// 进程捕获中断信号,注册中断信号处理函数OnSignal
void HookSignals()
{
signal(SIGINT, OnSignal);
signal(SIGTERM, OnSignal);
#ifdef _WIN32
signal(SIGBREAK, OnSignal);
#endif
}
// 设置中断信号系统默认处理(SIG_DFL)
void UnhookSignals()
{
signal(SIGINT, 0);
signal(SIGTERM, 0);
#ifdef _WIN32
signal(SIGBREAK, 0);
#endif
}