参考flamingo即时聊天软件,在阅读源码的同时,开发一个简单的聊天后台服务器。
主函数流程
下面是主函数代码:
int main(int argc, char* argv[])
{
// 设置信号处理函数
// 忽略子进程退出,这样的话子进程也不会变成僵尸进程
signal(SIGCHLD, SIG_DFL);
// 忽略向写端口关闭的socket中写数据时导致的错误
signal(SIGPIPE, SIG_IGN);
// 处理键盘输入ctrl+c导致的主进程退出
signal(SIGINT, xtalk_exit);
// 处理通过终端kill命令导致的主进程退出
signal(SIGTERM, xtalk_exit);
// 判断是否需要开启守护进程,需要一开始就开启,这样才能让子进程处理之后所有的逻辑
bool daemon = false;
// 从第二个参数开始,因为第一个参数是程序名字
for (int num = 1; num < argc; ++num)
{
if (strcmp(argv[num], "-daemon") == 0 ||
strcmp(argv[num], "--daemon") == 0 ||
strcmp(argv[num], "daemon") == 0)
{
std::cout << "set daemon on" << std::endl;
daemon = true;
break;
}
}
if (daemon)
{
run_daemon();
}
// 读取XTalk服务器的配置文件
// 这样写是因为运行目录和配置文件目录不一致,需要跳出来
ConfigFileReader config("../../../src/xtalk.conf");
// 第一次初始化单例模式
Singleton<XTalkServer>::Instance();
const char* logfilepath = config.getConfigName("logfiledir");
if (logfilepath == nullptr)
{
LOGF("logdir is not set in the config file");
return 1;
}
//如果log目录不存在则创建之
DIR* dp = opendir(logfilepath);
if (dp == NULL)
{
if (mkdir(logfilepath, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
{
LOGF("create base dir error, %s , errno: %d, %s", logfilepath, errno, strerror(errno));
return 1;
}
}
closedir(dp);
// 配置log记录有一些复杂,直接用现有的
std::string logFileFullPath = logfilepath;
const char* logfilename = config.getConfigName("logfilename");
logFileFullPath += logfilename;
AsyncLog::init(logFileFullPath.c_str());
/*
功能待完善
*/
getchar();
return 0;
}
上面是主函数部分流程,主要有以下几点:
- 设置信号处理函数:对于子进程退出保持默认处理方式,对于往socket写错误保持忽略,对于主进程退出调用处理函数
- 判断主进程运行时参数,是否要开启守护进程。有关守护进程的内容可以看之前的文章【flamingo源码阅读】守护进程
- 读取服务器配置文件,这里使用了一个工具类
ConfigFileReader
。配置文件中主要包括socket建立连接的ip以及监听端口,还包括日志文件保存的路径、MySQL数据库连接用到的ip地址、密码。
#client listener
listenip=192.168.91.128
listenport=20000
#monitor listener
monitorlistenip=192.168.91.128
monitorlistenport=8888
monitortoken=123
#http listener
httplistenip=192.168.91.128
httplistenport=12345
logfiledir=logs/
logfilename=XTalkServer
logbinarypackage=0
#mysql config
dbserver=192.168.91.128
dbuser=root
dbpassword=12345
dbname=flamingo
-
使用单例模式初始化XTalkServer类。XTalkServer的功能在后续开发时补充,这里只是简单地定义了一下这个类。单例模式参考之前的文章【flamingo源码阅读】单例模式
-
创建log目录,初始化异步日志记录类,这里有些复杂,我就直接使用flamingo中的源码了,没有仔细阅读逻辑。
代码到这里还是比较简单的,下一篇文章将会介绍MySQL相关的类以及操作。
代码附录
ConfigFileReader.h
这个类读取conf文件的每一行,如果读取到#开头的注释就跳过,否则将当前数据按照=分隔开,并保存在m_config_map中,方便后续读取。
#pragma once
/*
读取配置文件类
*/
#include <map>
#include <string>
#include <string.h>
class ConfigFileReader
{
public:
ConfigFileReader(const char* filename);
~ConfigFileReader();
const char* getConfigName(const char* name);
private:
void loadFile(const char* filename);
void parseLine(char* line);
char* trimSpace(char* name);
bool m_load_ok;
std::map<std::string, std::string> m_config_map;
std::string m_config_file;
};
ConfigFileReader.cpp
#include "ConfigFileReader.h"
ConfigFileReader::ConfigFileReader(const char* filename)
{
m_config_file = std::string(filename);
loadFile(filename);
}
ConfigFileReader::~ConfigFileReader()
{
}
const char* ConfigFileReader::getConfigName(const char* name)
{
auto iter = m_config_map.find(name);
if (iter != m_config_map.end())
return m_config_map[name].c_str();
else
return nullptr;
}
void ConfigFileReader::loadFile(const char* filename)
{
FILE* fp = fopen(filename, "r");
if (!fp)
{
return;
}
char buf[256];
while (1)
{
char* p = fgets(buf, 256, fp);
if (!p)
break;
size_t len = strlen(buf);
if (buf[len - 1] == '\n')
buf[len - 1] = 0; // remove \n at the end
char* ch = strchr(buf, '#'); // remove string start with #
if (ch)
*ch = 0;
if (strlen(buf) == 0)
continue;
parseLine(buf);
}
fclose(fp);
m_load_ok = true;
}
void ConfigFileReader::parseLine(char* line)
{
char* p = strchr(line, '=');
if (p == NULL)
return;
*p = 0;
char* key = trimSpace(line);
char* value = trimSpace(p + 1);
if (key && value)
{
m_config_map.insert(std::pair<std::string, std::string>(key, value));
}
}
char* ConfigFileReader::trimSpace(char* name)
{
// remove starting space or tab
char* start_pos = name;
while ((*start_pos == ' ') || (*start_pos == '\t') || (*start_pos == '\r'))
{
start_pos++;
}
if (strlen(start_pos) == 0)
return NULL;
// remove ending space or tab
char* end_pos = name + strlen(name) - 1;
while ((*end_pos == ' ') || (*end_pos == '\t') || (*end_pos == '\r'))
{
*end_pos = 0;
end_pos--;
}
int len = (int)(end_pos - start_pos) + 1;
if (len <= 0)
return NULL;
return start_pos;
}