【tag-003】Linux C++ 网络编程之配置文件

本文介绍了一个简单的配置文件读写功能实现方法,并通过一个CConfig类演示了如何从配置文件中读取日志相关配置进行初始化。

本章节开始配置文件的读写功能

1. 配置文件格式

对于我们自己开发的软件,可以自行规定格式,然后实现读写功能即可;在本工程中规定如下格式:

  • #、; 开头为注释
  • 忽略开头的 ‘\t’,‘\n’ 和空格字符
  • [] 内容为节点标记
  • 忽略空行
  • ‘=’ 区分左右两侧配置项和内容

在 bin 目录下新建一个 nginx_sim.conf 文件,内容暂时只需要配置与log相关的配置项

# 注释行
# 每个有效配置项用 = 处理,= 前不超过40字符,= 后不超过400字符

# 以 [ 开头表示组信息,也等价于注释行
Log = logs/error2.log
LogLevel = 5

2. 实现配置文件内容载入

新建一个 CConfig 类,专门用于对配置文件的读取;在 include 目录新建 ngx_conf.h 文件,在此文件声明类

class CConfig
{
private:
    CConfig();
    static CConfig* m_instance;
public:
    ~CConfig();
    //单例模式。线程不安全【如果需要线程安全,则要加锁;本工程不会有多线程竞争,因此无关紧要】
    static CConfig* GetInstance()
    {
        if(nullptr == m_instance)
        {
            m_instance = new CConfig();
            static CFreeInstance freeInstance; 
        }

        return m_instance;
    } 
    //用于释放对象
    class CFreeInstance
    {
    public:
        ~CFreeInstance()
        {
            if(CConfig::m_instance)
            {
                delete CConfig::m_instance;
                CConfig::m_instance = nullptr;
            }
        }
    };
    //对外的接口
    bool LoadConfig(const char* pConfigName);
    std::string GetString(const char* pItemName);
    int GetIntDefault(const char* pItemName,const int def);
private:
    std::map<std::string,std::string>m_configList;
};

对类中方法的实现放在 src/ngx_conf.cxx

//静态成员初始化
CConfig* CConfig::m_instance = nullptr;

CConfig::CConfig()
{

}

CConfig::~CConfig()
{
}

bool CConfig::LoadConfig(const char* pConfigName)
{
    FILE *fp;
    fp = fopen(pConfigName,"r");
    if(nullptr == fp)
    {
        return false;
    }

    char lineBuf[501];

    while(!feof(fp))
    {
        if(nullptr == fgets(lineBuf,500,fp))
        {
            continue;
        }

        if(0 == *lineBuf || ' ' == *lineBuf || '#' == *lineBuf || ';' == *lineBuf  || '\t' == *lineBuf  || '\n' == *lineBuf)
        {
            continue;
        }
    loop:
        auto size = strlen(lineBuf);
        if(size > 0)
        {
            //抛掉末尾的换行,回车和空格
            if(10 == lineBuf[size-1] || 13 == lineBuf[size-1] || 32 == lineBuf[size-1])
            {
                lineBuf[size-1] = 0;
                goto loop;
            }
        }

        if(0 == *lineBuf || '[' == *lineBuf)
        {
            continue;
        }

        //开始读取配置内容
        char *pTmp = strchr(lineBuf,'=');
        if(pTmp)
        {
            CConfItem pConfigItem;
            memset(&pConfigItem,0,sizeof(CConfItem));
            strncpy(pConfigItem.ItemName,lineBuf,(int)(pTmp - lineBuf));
            strcpy(pConfigItem.ItemContent,pTmp + 1);

            //去除首尾空格
            Rtrim(pConfigItem.ItemName);
            Ltrim(pConfigItem.ItemName);
            Rtrim(pConfigItem.ItemContent);
            Ltrim(pConfigItem.ItemContent);

            m_configList[pConfigItem.ItemName] = pConfigItem.ItemContent;
        }
    }
    fclose(fp);
    return true;
}

std::string CConfig::GetString(const char* pItemName)
{
    return m_configList[pItemName];
}

int CConfig::GetIntDefault(const char* pItemName,const int def)
{
    auto iter = m_configList.find(std::string(pItemName));
    if(iter != m_configList.end())
    {
        return atoi(iter->second.c_str());
    }
    return def;
}

3. log 配置载入初始化

现在可以通过 CConfig 类载入 log 的配置进行初始化工作了,修改 ngx_log_init 函数

void ngx_log_init()
{
    u_char *plogname = NULL;

    //从配置文件中读取和日志相关的配置信息
    CConfig *p_config = CConfig::GetInstance();
    std::string strPath = p_config->GetString("Log");
    plogname = (u_char *)strPath.c_str();
    if(strPath.empty())
    {
        //没读到,就要给个缺省的路径文件名了
        plogname = (u_char *) NGX_ERROR_LOG_PATH; //"logs/error.log" ,logs目录需要提前建立出来
    }
    ngx_log.log_level = p_config->GetIntDefault("LogLevel",NGX_LOG_NOTICE);//缺省日志等级为6【注意】 ,如果读失败,就给缺省日志等级

    //只写打开|追加到末尾|文件不存在则创建【这个需要跟第三参数指定文件访问权限】
    //mode = 0644:文件访问权限, 6: 110    , 4: 100: 
    ngx_log.fd = open((const char *)plogname,O_WRONLY|O_APPEND|O_CREAT,0644);  
    if (ngx_log.fd == -1)  //如果有错误,则直接定位到 标准错误上去 
    {
        ngx_log_stderr(errno,"[alert] could not open error log file: open() \"%s\" failed", plogname);
        ngx_log.fd = STDERR_FILENO; //直接定位到标准错误去了        
    } 
    return;
}

4. 编译、运行、测试

至此,我们已经可以从配置文件中读入log 的配置了;在程序启动时需要先载入配置文件,如下

int main(int argc, char* const* argv)
{
    ngx_pid = getpid();       
    //加载配置文件内容
    if(false == CConfig::GetInstance()->LoadConfig("nginx_sim.conf"))
    {
        //日志初始化(创建/打开日志文件)
        ngx_log_init(); 
        ngx_log_stderr(0,"配置文件[%s]载入失败,退出!","nginx_sim.conf");
        //找不到文件
        goto lblexit;
    }
    //日志初始化(创建/打开日志文件)
    ngx_log_init(); 
    ngx_log_stderr(0, "从配置文件中读到的 log 目录:%s",(CConfig::GetInstance()->GetString("Log")).c_str());
    ngx_log_stderr(0, "从配置文件中读到的 log 等级:%d",CConfig::GetInstance()->GetIntDefault("LogLevel",NGX_LOG_NOTICE));
lblexit:
    ngx_log_stderr(0,"程序退出!");
    //该释放的资源要释放掉
    freereSource();  //一系列的main返回前的释放动作函数  
    return 0;
}

编译运行查看结果
在这里插入图片描述
在bin/ogs目录下可以看出到成成了error2.log文件,并且log目录与等级和我们在配置文件中的配置是一致的;

5. 结语

至此,我们的工程最基础的功能已经搭建完毕了,拥有了log记录的功能和配置的能力。

接下来,我们将进入进程相关的话题。

Note: 到此的内容可以独立将代码抽取出来运动到其他项目,需要工程源码请评论留言

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值