## 11) 配置文件读写功能
配置文件读写这里就不详细介绍了,主要的配置文件格式如下
```conf
[reactor]
ip = 127.0.0.1
port = 7777
maxConn = 1024
threadNum = 5
```
一个主题,下面很多key-value的键值对.
代码如下。
### 11.1 功能实现
> lars_reactor/include/config_file.h
```c
#pragma once
#include <string>
#include <vector>
#include <map>
//定义一个存放配置信息的map
//key 是string 存放一个标题section
//value 是一个map 存放该标题下面的所有key-value键值对
typedef std::map<std::string, std::map<std::string, std::string> *> STR_MAP;
typedef STR_MAP::iterator STR_MAP_ITER;
//设计成单例模式
class config_file {
public:
~config_file();
//获取字符串类型配置信息
std::string GetString(const std::string& section, const std::string& key, const std::string& default_value = "");
//字符串集合配置信息
std::vector<std::string> GetStringList(const std::string& section, const std::string& key);
//获取整型类型配置信息
unsigned GetNumber(const std::string& section, const std::string& key, unsigned default_value = 0);
//获取布尔类型配置信息
bool GetBool(const std::string& section, const std::string& key, bool default_value = false);
//获取浮点类型配置信息
float GetFloat(const std::string& section, const std::string& key, const float& default_value);
//设置配置文件所在路径
static bool setPath(const std::string& path);
//获取单例
static config_file *instance();
private:
config_file() { } //构造私有
//字符串配置文件解析基础方法
bool isSection(std::string line, std::string& section);
unsigned parseNumber(const std::string& s);
std::string trimLeft(const std::string& s);
std::string trimRight(const std::string& s);
std::string trim(const std::string& s);
bool Load(const std::string& path);
static config_file *config;//唯一读取配置文件实例
STR_MAP _map;
};
```
> lars_reactor/src/config_file.cpp
```c
#include "config_file.h"
#include <map>
#include <fstream>
#include <iostream>
#include <sstream>
#include <assert.h>
#include <strings.h>
config_file* config_file::config = NULL;
config_file::~config_file()
{
for (STR_MAP_ITER it = _map.begin(); it != _map.end(); ++it)
{
delete it->second;
}
}
//获取字符串类型键值对
std::string config_file::GetString(const std::string& section, const std::string& key, const std::string& default_value)
{
STR_MAP_ITER it = _map.find(section);
if (it != _map.end())
{
std::map<std::string, std::string>::const_iterator it1 = it->second->find(key);
if (it1 != it->second->end())
{
return it1->second;
}
}
return default_value;
}
//获取浮点类型键值对
float config_file::GetFloat(const std::string& section, const std::string& key, const float& default_value)
{
std::ostringstream convert1;
convert1 << default_value;
//将浮点转换成字符串,然后按照字符串业务处理
std::string default_value_str = convert1.str();
std::string text = GetString(section, key, default_value_str);
std::istringstream convert2(text);
float fresult;
if (!(convert2 >> fresult)) //如果Result放不下text对应的数字,执行将返回0;
fresult = 0;
return fresult;
}
//价值配置文件
bool config_file::Load(const std::string& path)
{
std::ifstream ifs(path.c_str());
if (!ifs.good())
{
return false;
}
std::string line;
std::map<std::string, std::string> *m = NULL;
while (!ifs.eof() && ifs.good())
{
getline(ifs, line);
std::string section;
if (isSection(line, section))
{
STR_MAP_ITER it = _map.find(section);
if (it == _map.end())
{
m = new std::map<std::string, std::string>();
_map.insert(STR_MAP::value_type(section, m));
}
else
{
m = it->second;
}
continue;
}
size_t equ_pos = line.find('=');
if (equ_pos == std::string::npos)
{
continue;
}
std::string key = line.substr(0, equ_pos);
std::string value = line.substr(equ_pos + 1);
key = trim(key);
value = trim(value);
if (key.empty())
{
continue;
}
if (key[0] == '#' || key[0] == ';') // skip comment
{
continue;
}
std::map<std::string, std::string>::iterator it1 = m->find(key);
if (it1 != m->end())
{
it1->second = value;
}
else
{
m->insert(std::map<std::string, std::string>::value_type(key, value));
}
}
ifs.close();
return true;
}
std::vector<std::string> config_file::GetStringList(const std::string& section, const std::string& key)
{
std::vector<std::string> v;
std::string str = GetString(section, key, "");
std::string sep = ", \t";
std::string substr;
std::string::size_type start = 0;
std::string::size_type index;
while ((index = str.find_first_of(sep, start)) != std::string::npos)
{
substr = str.substr(start, index - start);
v.push_back(substr);
start = str.find_first_not_of(sep, index);
if (start == std::string::npos)
{
return v;
}
}
substr = str.substr(start);
v.push_back(substr);
return v;
}
//获取整型类型键值对
unsigned config_file::GetNumber(const std::string& section, const std::string& key, unsigned default_value)
{
STR_MAP_ITER it = _map.find(section);
if (it != _map.end())
{
std::map<std::string, std::string>::const_iterator it1 = it->second->find(key);
if (it1 != it->second->end())
{
return parseNumber(it1->second);
}
}
return default_value;
}
//获取布尔类型键值对
bool config_file::GetBool(const std::string& section, const std::string& key, bool default_value)
{
STR_MAP_ITER it = _map.find(section);
if (it != _map.end())
{
std::map<std::string, std::string>::const_iterator it1 = it->second->find(key);
if (it1 != it->second->end())
{
if (strcasecmp(it1->second.c_str(), "true") == 0)
{
return true;
}
}
}
return default_value;
}
bool config_file::isSection(std::string line, std::string& section)
{
section = trim(line);
if (section.empty() || section.length() <= 2)
{
return false;
}
if (section.at(0) != '[' || section.at(section.length() - 1) != ']')
{
return false;
}
section = section.substr(1, section.length() - 2);
section = trim(section);
return true;
}
unsigned config_file::parseNumber(const std::string& s)
{
std::istringstream iss(s);
long long v = 0;
iss >> v;
return v;
}
std::string config_file::trimLeft(const std::string& s)
{
size_t first_not_empty = 0;
std::string::const_iterator beg = s.begin();
while (beg != s.end())
{
if (!isspace(*beg))
{
break;
}
first_not_empty++;
beg++;
}
return s.substr(first_not_empty);
}
std::string config_file::trimRight(const std::string& s)
{
size_t last_not_empty = s.length();
std::string::const_iterator end = s.end();
while (end != s.begin())
{
end--;
if (!isspace(*end))
{
break;
}
last_not_empty--;
}
return s.substr(0, last_not_empty);
}
std::string config_file::trim(const std::string& s)
{
return trimLeft(trimRight(s));
}
config_file *config_file::instance()
{
assert(config != NULL);
return config;
}
//设置配置文件所在路径
bool config_file::setPath(const std::string& path)
{
assert(config == NULL);
//创建对象
config = new config_file();
//加载文件
return config->Load(path);
}
```
### 11.2 完成Lars Reactor V0.9开发
> serv.conf
```conf
[reactor]
ip = 127.0.0.1
port = 7777
maxConn = 1024
threadNum = 5
```
> server.cpp
```c
#include "tcp_server.h"
#include <string>
#include <string.h>
#include "config_file.h"
//回显业务的回调函数
void callback_busi(const char *data, uint32_t len, int msgid, net_connection *conn, void *user_data)
{
printf("callback_busi ...\n");
//直接回显
conn->send_message(data, len, msgid);
}
//打印信息回调函数
void print_busi(const char *data, uint32_t len, int msgid, net_connection *conn, void *user_data)
{
printf("recv client: [%s]\n", data);
printf("msgid: [%d]\n", msgid);
printf("len: [%d]\n", len);
}
//新客户端创建的回调
void on_client_build(net_connection *conn, void *args)
{
int msgid = 101;
const char *msg = "welcome! you online..";
conn->send_message(msg, strlen(msg), msgid);
}
//客户端销毁的回调
void on_client_lost(net_connection *conn, void *args)
{
printf("connection is lost !\n");
}
int main()
{
event_loop loop;
//加载配置文件
config_file::setPath("./serv.conf");
std::string ip = config_file::instance()->GetString("reactor", "ip", "0.0.0.0");
short port = config_file::instance()->GetNumber("reactor", "port", 8888);
printf("ip = %s, port = %d\n", ip.c_str(), port);
tcp_server server(&loop, ip.c_str(), port);
//注册消息业务路由
server.add_msg_router(1, callback_busi);
server.add_msg_router(2, print_busi);
//注册链接hook回调
server.set_conn_start(on_client_build);
server.set_conn_close(on_client_lost);
loop.event_process();
return 0;
}
```
---
### 关于作者:
作者:`Aceld(刘丹冰)`
mail: [danbing.at@gmail.com](mailto:danbing.at@gmail.com)
github: [https://github.com/aceld](https://github.com/aceld)
原创书籍: [https://www.kancloud.cn/@aceld](https://www.kancloud.cn/@aceld)
![](https://img.kancloud.cn/b0/d1/b0d11a21ba62e96aef1c11d5bfff2cf8_227x227.jpg)
>**原创声明:未经作者允许请勿转载, 如果转载请注明出处**
- 一、Lars系统概述
- 第1章-概述
- 第2章-项目目录构建
- 二、Reactor模型服务器框架
- 第1章-项目结构与V0.1雏形
- 第2章-内存管理与Buffer封装
- 第3章-事件触发EventLoop
- 第4章-链接与消息封装
- 第5章-Client客户端模型
- 第6章-连接管理及限制
- 第7章-消息业务路由分发机制
- 第8章-链接创建/销毁Hook机制
- 第9章-消息任务队列与线程池
- 第10章-配置文件读写功能
- 第11章-udp服务与客户端
- 第12章-数据传输协议protocol buffer
- 第13章-QPS性能测试
- 第14章-异步消息任务机制
- 第15章-链接属性设置功能
- 三、Lars系统之DNSService
- 第1章-Lars-dns简介
- 第2章-数据库创建
- 第3章-项目目录结构及环境构建
- 第4章-Route结构的定义
- 第5章-获取Route信息
- 第6章-Route订阅模式
- 第7章-Backend Thread实时监控
- 四、Lars系统之Report Service
- 第1章-项目概述-数据表及proto3协议定义
- 第2章-获取report上报数据
- 第3章-存储线程池及消息队列
- 五、Lars系统之LoadBalance Agent
- 第1章-项目概述及构建
- 第2章-主模块业务结构搭建
- 第3章-Report与Dns Client设计与实现
- 第4章-负载均衡模块基础设计
- 第5章-负载均衡获取Host主机信息API
- 第6章-负载均衡上报Host主机信息API
- 第7章-过期窗口清理与过载超时(V0.5)
- 第8章-定期拉取最新路由信息(V0.6)
- 第9章-负载均衡获取Route信息API(0.7)
- 第10章-API初始化接口(V0.8)
- 第11章-Lars Agent性能测试工具
- 第12章- Lars启动工具脚本