Linux 进程池代码

cllient.cc

#include "comm.hpp"
#include "log.hpp"

using namespace std;

// 管理管道文件
int main()
{
    Init init;
    Log log;
    // log.Enable(Onefile);
    log.Enable(Onefile);

    // 打开管道
    int fd = open(FIFO_FILE, O_RDONLY); // 等待写入方打开之后,自己才会打开文件,向后执行, open 阻塞了!
    if (fd < 0)
    {
        log(Fatal, "error string: %s, error code: %d", strerror(errno), errno);
        exit(FIFO_OPEN_ERR);
    }

    log(Info, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
    log(Warning, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
    log(Fatal, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
    log(Debug, "server open file done, error string: %s, error code: %d", strerror(errno), errno);


    // 开始通信
    while (true)
    {
        char buffer[1024] = { 0 };
        int x = read(fd, buffer, sizeof(buffer));
        if (x > 0)
        {
            buffer[x] = 0;
            cout << "client say# " << buffer << endl;
        }
        else if (x == 0)
        {
            log(Debug, "client quit, me too!, error string: %s, error code: %d", strerror(errno), errno);
            break;
        }
        else
            break;
    }

    close(fd);
    return 0;
}

comm.hpp

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include<stdio.h>

#define FIFO_FILE "./myfifo"
#define MODE 0664

enum
{
    FIFO_CREATE_ERR = 1,
    FIFO_DELETE_ERR,
    FIFO_OPEN_ERR
};

class Init
{
public:
    Init()
    {
        // 创建管道
        int n = mkfifo(FIFO_FILE, MODE);
        if (n == -1)
        {
            perror("mkfifo");
            exit(FIFO_CREATE_ERR);
        }
    }
    ~Init()
    {

        int m = unlink(FIFO_FILE);
        if (m == -1)
        {
            perror("unlink");
            exit(FIFO_DELETE_ERR);
        }
    }
};

log.hpp

#pragma once
// 防止头文件重复包含(编译预处理指令)
// 等价于#ifndef XXX_H#define XXX_H#endif,更简洁高效,仅支持C++11及以上
#pragma once

// 包含必要的头文件
#include <iostream>     // 标准输入输出流(用于屏幕输出日志)
#include <time.h>       // 时间相关函数(获取当前时间、格式化时间戳)
#include <stdarg.h>     // 可变参数相关宏(处理日志的格式化输入,如printf风格参数)
#include <sys/types.h>  // 系统类型定义(文件操作相关类型,如mode_t)
#include <sys/stat.h>   // 文件状态相关函数(如判断文件是否存在)
#include <fcntl.h>      // 文件控制相关宏(文件打开标志O_WRONLY、O_CREAT等)
#include <unistd.h>     // 系统调用函数(write、close、access等文件操作)
#include <stdlib.h>     // 标准库函数(如exit,但此处未直接使用,可能为后续扩展预留)

// 缓冲区大小宏定义:日志字符串的临时存储缓冲区大小(1KB)
// 用于存储格式化后的时间、日志级别、自定义信息等
#define SIZE 1024

// 日志级别宏定义:用整数枚举日志的严重程度,便于后续判断和字符串转换
#define Info 0      // 信息级日志(普通运行状态信息)
#define Debug 1     // 调试级日志(开发调试用,输出详细过程)
#define Warning 2   // 警告级日志(非致命错误,可能影响功能但不中断运行)
#define Error 3     // 错误级日志(功能异常,部分模块可能无法正常工作)
#define Fatal 4     // 致命级日志(严重错误,程序可能崩溃或无法继续运行)

// 日志输出方式宏定义:指定日志的输出目的地
#define Screen 1    // 输出到屏幕(标准输出std::cout)
#define Onefile 2   // 输出到单个文件(所有级别日志写入同一个文件)
#define Classfile 3 // 按级别分类输出到多个文件(每个级别对应一个独立文件)

// 默认日志文件名(当输出到单个文件或分类文件时,作为基础文件名)
#define LogFile "log.txt"

// 日志工具类:支持多目的地输出、日志级别区分、时间戳格式化、可变参数日志输入
class Log
{
public:
    // 构造函数:初始化日志类的默认配置
    Log()
    {
        printMethod = Screen;  // 默认输出方式:屏幕输出
        path = "./log/";       // 默认日志文件存储路径:当前目录下的log文件夹
    }

    // 日志输出方式设置接口
    // 参数method:输出方式(取值为Screen/Onefile/Classfile,对应上面的宏定义)
    void Enable(int method)
    {
        printMethod = method;  // 更新成员变量,切换日志输出目的地
    }

    // 日志级别整数转字符串:将日志级别宏(如Debug)转为对应的字符串(如"Debug")
    // 参数level:日志级别整数(Info/Debug/Warning/Error/Fatal)
    // 返回值:对应的日志级别字符串(默认返回"None"表示无效级别)
    std::string levelToString(int level)
    {
        switch (level)
        {
        case Info:
            return "Info";     // 信息级 -> "Info"
        case Debug:
            return "Debug";    // 调试级 -> "Debug"
        case Warning:
            return "Warning";  // 警告级 -> "Warning"
        case Error:
            return "Error";    // 错误级 -> "Error"
        case Fatal:
            return "Fatal";    // 致命级 -> "Fatal"
        default:
            return "None";     // 无效级别 -> "None"
        }
    }

    // (原代码中被注释的日志输出函数,保留注释说明)
    // 功能:接收日志级别和格式化字符串,生成完整日志并输出
    // 注:该函数已被operator()重载替代,此处为预留/废弃代码
    // void logmessage(int level, const char *format, ...)
    // {
    //     time_t t = time(nullptr);  // 获取当前系统时间(秒级时间戳)
    //     struct tm *ctime = localtime(&t);  // 转换为本地时间结构体(包含年、月、日、时、分、秒)
    //     char leftbuffer[SIZE];  // 存储日志前缀(级别+时间戳)的缓冲区
    //     // 格式化日志前缀:[级别][年-月-日 时:分:秒]
    //     snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
    //              ctime->tm_year + 1900,  // tm_year是从1900年开始的偏移量,需+1900得到实际年份
    //              ctime->tm_mon + 1,     // tm_mon是0-11的月份,需+1得到实际月份(1-12)
    //              ctime->tm_mday,        // 当月日期(1-31)
    //              ctime->tm_hour,        // 小时(0-23)
    //              ctime->tm_min,         // 分钟(0-59)
    //              ctime->tm_sec);        // 秒(0-59)

    //     // va_list s;  // 可变参数列表对象(本质是字符指针,用于遍历可变参数)
    //     // va_start(s, format);  // 初始化可变参数列表:从format参数的下一个参数开始
    //     char rightbuffer[SIZE];  // 存储自定义日志信息的缓冲区
    //     vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);  // 格式化可变参数到缓冲区(vsnprintf专门处理可变参数)
    //     // va_end(s);  // 释放可变参数列表资源(避免内存泄漏)

    //     // 拼接完整日志字符串:前缀 + 自定义信息 + 换行符
    //     char logtxt[SIZE * 2];
    //     snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);

    //     // printf("%s", logtxt); // 暂时打印(调试用)
    //     printLog(level, logtxt);  // 调用实际的日志输出函数
    // }

    // 日志输出分发函数:根据当前设置的输出方式(printMethod),将日志输出到对应目的地
    // 参数level:日志级别(用于分类文件输出)
    // 参数logtxt:完整的日志字符串(已包含前缀+自定义信息+换行)
    void printLog(int level, const std::string& logtxt)
    {
        switch (printMethod)
        {
        case Screen:
            // 输出到屏幕:使用std::cout打印日志
            std::cout << logtxt << std::endl;
            break;
        case Onefile:
            // 输出到单个文件:调用printOneFile,写入默认日志文件LogFile
            printOneFile(LogFile, logtxt);
            break;
        case Classfile:
            // 按级别分类输出到多个文件:调用printClassFile,每个级别对应一个文件
            printClassFile(level, logtxt);
            break;
        default:
            // 无效输出方式:不做任何操作
            break;
        }
    }

    // 单个文件写入函数:将日志写入指定的单个文件(支持文件不存在时创建、追加写入)
    // 参数logname:日志文件名(如LogFile定义的"log.txt")
    // 参数logtxt:要写入的完整日志字符串
    void printOneFile(const std::string& logname, const std::string& logtxt)
    {
        // 拼接完整文件路径:日志存储根路径(path) + 文件名(logname)
        std::string _logname = path + logname;

        // 打开文件:O_WRONLY(只写模式)、O_CREAT(文件不存在则创建)、O_APPEND(追加模式,避免覆盖原有日志)
        // 0666:文件权限(所有者、组用户、其他用户均有读写权限,具体生效受umask影响)
        int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);

        if (fd < 0)  // 打开文件失败(如路径不存在、权限不足),直接返回
            return;

        // 写入日志:将logtxt的字符串内容写入文件描述符fd
        // logtxt.size():获取字符串长度(字节数)
        write(fd, logtxt.c_str(), logtxt.size());

        // 关闭文件:释放文件描述符资源(必须调用,否则会导致文件描述符泄漏)
        close(fd);
    }

    // 按级别分类写入文件函数:为不同日志级别创建独立文件,调用printOneFile完成写入
    // 参数level:日志级别(用于生成对应文件名)
    // 参数logtxt:要写入的完整日志字符串
    void printClassFile(int level, const std::string& logtxt)
    {
        std::string filename = LogFile;  // 基础文件名(如"log.txt")
        filename += ".";                 // 拼接分隔符(如"log.txt.")
        filename += levelToString(level); // 拼接日志级别字符串(如"log.txt.Debug")

        // 调用单个文件写入函数,将日志写入对应级别文件
        printOneFile(filename, logtxt);
    }

    // 析构函数:日志类无动态分配资源(如堆内存、打开的文件),无需额外清理操作
    ~Log()
    {
    }

    // 重载()运算符:使Log类对象可像函数一样调用,简化日志输出语法
    // 功能:接收日志级别、格式化字符串、可变参数,生成完整日志并调用printLog输出
    // 参数level:日志级别(Info/Debug/Warning/Error/Fatal)
    // 参数format:日志格式化字符串(如"用户%d登录成功,IP:%s")
    // 参数...:可变参数(对应format中的占位符,如%d、%s等)
    void operator()(int level, const char* format, ...)
    {
        // 1. 获取并格式化时间戳和日志级别(日志前缀部分)
        time_t t = time(nullptr);  // 获取当前系统时间(秒级时间戳,time_t本质是long)
        // localtime:将秒级时间戳转换为本地时间结构体tm(包含年、月、日、时、分、秒等)
        // 注意:localtime是线程不安全函数,多线程环境下建议使用localtime_r(Linux)
        struct tm* ctime = localtime(&t);

        char leftbuffer[SIZE];  // 存储日志前缀的缓冲区(级别+时间戳)
        // 格式化日志前缀:[级别][年-月-日 时:分:秒]
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]",
            levelToString(level).c_str(),  // 日志级别字符串(如"Debug")
            ctime->tm_year + 1900,        // 实际年份 = tm_year(1900偏移)+ 1900
            ctime->tm_mon + 1,           // 实际月份 = tm_mon(0-11)+ 1
            ctime->tm_mday,              // 当月日期(1-31)
            ctime->tm_hour,              // 小时(0-23)
            ctime->tm_min,               // 分钟(0-59)
            ctime->tm_sec);              // 秒(0-59)

        // 2. 处理可变参数(自定义日志信息部分)
        va_list s;  // 可变参数列表对象(用于遍历可变参数,本质是char*指针)
        va_start(s, format);  // 初始化可变参数列表:从format参数的下一个参数开始遍历
        char rightbuffer[SIZE];  // 存储格式化后的自定义日志信息
        // vsnprintf:格式化可变参数到缓冲区
        // 参数:目标缓冲区、缓冲区大小、格式化字符串、可变参数列表
        // 优点:避免缓冲区溢出(第二个参数限制写入长度)
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
        va_end(s);  // 释放可变参数列表资源(必须调用,否则可能导致内存泄漏或野指针)

        // 3. 拼接完整日志字符串(前缀 + 自定义信息 + 换行符)
        char logtxt[SIZE * 2];  // 拼接后的日志缓冲区(大小2*SIZE,避免前缀+自定义信息超出长度)
        snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);

        // 4. 调用日志输出分发函数,将日志输出到指定目的地
        printLog(level, logtxt);
    }

private:
    int printMethod;  // 日志输出方式(存储Screen/Onefile/Classfile,由Enable函数设置)
    std::string path; // 日志文件存储根路径(默认./log/,可后续扩展为可配置)
};

// 可变参数函数示例:计算n个整数的和(用于演示stdarg.h的使用,与日志类功能无关)
// 参数n:可变参数的个数(必须显式传入,因为可变参数列表无法自动获取个数)
// 参数...:n个int类型的参数(如sum(3, 1, 2, 3)表示计算1+2+3)
// 返回值:n个整数的和
// int sum(int n, ...)
// {
//     va_list s; // 可变参数列表对象(char*类型,用于指向可变参数的起始地址)
//     va_start(s, n); // 初始化:s指向第一个可变参数(n的下一个参数)

//     int sum = 0;
//     while(n)
//     {
//         // va_arg(s, int):获取当前可变参数(类型为int),并将s指向-next-个可变参数
//         sum += va_arg(s, int); 
//         // 示例:printf("hello %d, hello %s, hello %c, hello %d,", 1, "hello", 'c', 123);
//         // 需显式知道每个参数的类型,否则会出现类型不匹配错误
//         n--; // 剩余参数个数减1,直到处理完所有n个参数
//     }

//     va_end(s); // 清理:将s置为NULL,释放可变参数列表资源(避免野指针)
//     return sum;
// }

server.cc

#include <iostream>
#include "comm.hpp"

using namespace std;

int main()
{
    int fd = open(FIFO_FILE, O_WRONLY);
    if (fd < 0)
    {
        perror("open");
        exit(FIFO_OPEN_ERR);
    }

    cout << "client open file done" << endl;

    string line;
    while (true)
    {
        cout << "Please Enter@ ";
        getline(cin, line);

        write(fd, line.c_str(), line.size());
    }

    close(fd);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值