【flamingo源码阅读】XTalkServer开发(1)

该文章介绍了如何参考Flamingo即时聊天软件来开发一个简单的聊天后台服务器,涉及主函数流程,包括信号处理、守护进程设置、配置文件读取、单例模式的XTalkServer初始化及异步日志记录。代码示例中,还展示了ConfigFileReader类用于读取配置文件内容。
摘要由CSDN通过智能技术生成

参考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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值