【Linux】37.网络版本计算器

发布于:2025-03-11 ⋅ 阅读:(19) ⋅ 点赞:(0)


1. Log.hpp-日志记录器

Log.hpp

// 1. 头文件和宏定义
#pragma once  // 防止头文件重复包含

// 系统头文件
#include <iostream>    // 标准输入输出
#include <time.h>      // 时间相关函数
#include <stdarg.h>    // 可变参数函数
#include <sys/types.h> // 基本系统数据类型
#include <sys/stat.h>  // 文件状态
#include <fcntl.h>     // 文件控制
#include <unistd.h>    // POSIX系统调用
#include <stdlib.h>    // 标准库函数

// 缓冲区大小
#define SIZE 1024

// 日志级别定义
#define Info 0      // 普通信息
#define Debug 1     // 调试信息
#define Warning 2   // 警告信息
#define Error 3     // 错误信息
#define Fatal 4     // 致命错误

// 日志输出方式
#define Screen 1     // 输出到屏幕
#define Onefile 2    // 输出到单个文件
#define Classfile 3  // 根据日志级别输出到不同文件

// 默认日志文件名
#define LogFile "log.txt"

// 2. 日志类定义
class Log {
private:
    int printMethod;      // 日志输出方式
    std::string path;     // 日志文件路径

public:
    // 2.1 构造函数:设置默认输出方式
    Log() {
        printMethod = Screen;  // 默认输出到屏幕
        path = "./log/";       // 默认日志目录
    }

    // 2.2 设置日志输出方式
    void Enable(int method) {
        printMethod = method;
    }

    // 2.3 日志级别转字符串
    std::string levelToString(int level) {
        switch (level) {
            case Info:    return "Info";
            case Debug:   return "Debug";
            case Warning: return "Warning";
            case Error:   return "Error";
            case Fatal:   return "Fatal";
            default:      return "None";
        }
    }

    // 2.4 日志输出函数
    void printLog(int level, const std::string &logtxt) {
        switch (printMethod) {
            case Screen:    // 输出到屏幕
                std::cout << logtxt << std::endl;
                break;
            case Onefile:   // 输出到单个文件
                printOneFile(LogFile, logtxt);
                break;
            case Classfile: // 根据日志级别输出到不同文件
                printClassFile(level, logtxt);
                break;
        }
    }

    // 2.5 输出到单个文件
    void printOneFile(const std::string &logname, const std::string &logtxt) {
        std::string _logname = path + logname;
        // 打开文件:写入、创建(如果不存在)、追加模式
        int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
        if (fd < 0) return;
        
        write(fd, logtxt.c_str(), logtxt.size());
        close(fd);
    }

    // 2.6 根据日志级别输出到不同文件
    void printClassFile(int level, const std::string &logtxt) {
        std::string filename = LogFile;
        filename += ".";
        filename += levelToString(level); // 例如: "log.txt.Debug"
        printOneFile(filename, logtxt);
    }

    // 2.7 重载函数调用运算符
    void operator()(int level, const char *format, ...) {
        // 1. 获取当前时间
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        
        // 2. 格式化时间和日志级别信息
        char leftbuffer[SIZE];
        snprintf(leftbuffer, sizeof(leftbuffer), 
                "[%s][%d-%d-%d %d:%d:%d]", 
                levelToString(level).c_str(),
                ctime->tm_year + 1900, 
                ctime->tm_mon + 1, 
                ctime->tm_mday,
                ctime->tm_hour, 
                ctime->tm_min, 
                ctime->tm_sec);

        // 3. 处理可变参数
        va_list s;
        va_start(s, format);
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
        va_end(s);

        // 4. 组合完整的日志信息
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);

        // 5. 输出日志
        printLog(level, logtxt);
    }
};

// 3. 创建全局日志对象
Log lg;

2. Daemon.hpp-守护进程工具

Daemon.hpp

将进程转换为守护进程的工具类

#pragma once  // 防止头文件重复包含

#include <iostream>    // 标准输入输出
#include <cstdlib>     // exit()函数
#include <unistd.h>    // fork(), setsid(), chdir()等系统调用
#include <signal.h>    // 信号处理
#include <string>      // 字符串类
#include <sys/types.h> // 基本系统数据类型
#include <sys/stat.h>  // 文件状态
#include <fcntl.h>     // 文件控制选项

// 定义空设备文件路径
const std::string nullfile = "/dev/null";

// 守护进程化函数,参数cwd为工作目录
void Daemon(const std::string &cwd = "")
{
    // 1. 忽略一些可能的干扰信号
    signal(SIGCLD, SIG_IGN);  // 忽略子进程状态改变信号
    signal(SIGPIPE, SIG_IGN); // 忽略管道破裂信号
    signal(SIGSTOP, SIG_IGN); // 忽略停止进程信号

    // 2. 创建守护进程
    if (fork() > 0)  // 父进程退出
        exit(0);
    setsid();        // 创建新会话,使进程成为会话组长

    // 3. 改变工作目录
    if (!cwd.empty())              // 如果指定了工作目录
        chdir(cwd.c_str());        // 切换到指定目录

    // 4. 重定向标准输入输出到/dev/null
    int fd = open(nullfile.c_str(), O_RDWR); // 以读写方式打开/dev/null
    if(fd > 0)
    {
        dup2(fd, 0);  // 重定向标准输入
        dup2(fd, 1);  // 重定向标准输出
        dup2(fd, 2);  // 重定向标准错误
        close(fd);    // 关闭文件描述符
    }
}

3. Protocol.hpp-通信协议解析器

Protocol.hpp

定义客户端服务器间通信协议,处理消息的序列化和反序列化

#pragma once

#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>  // JSON序列化支持

// #define MySelf 1  // 自定义协议开关

// 定义分隔符
const std::string blank_space_sep = " ";  // 空格分隔符
const std::string protocol_sep = "\n";    // 协议分隔符

// 协议编码函数:将内容封装成格式化的报文
std::string Encode(std::string &content)
{
    std::string package = std::to_string(content.size());  // 内容长度
    package += protocol_sep;   // 添加分隔符
    package += content;        // 添加内容
    package += protocol_sep;   // 添加分隔符
    return package;
}

// 协议解码函数:从报文中提取内容
// 格式:"len"\n"x op y"\nXXXXXX
bool Decode(std::string &package, std::string *content)
{
    // 查找第一个分隔符位置
    std::size_t pos = package.find(protocol_sep);
    if(pos == std::string::npos) return false;
    
    // 获取长度字符串并转换
    std::string len_str = package.substr(0, pos);
    std::size_t len = std::stoi(len_str);
    
    // 计算完整报文长度
    std::size_t total_len = len_str.size() + len + 2;
    if(package.size() < total_len) return false;

    // 提取内容
    *content = package.substr(pos+1, len);
    // 移除已处理的报文
    package.erase(0, total_len);

    return true;
}

// 请求类:处理计算请求
class Request
{
public:
    // 构造函数
    Request(int data1, int data2, char oper) : x(data1), y(data2), op(oper) {}
    Request() {}

public:
    // 序列化:将请求对象转换为字符串
    bool Serialize(std::string *out)
    {
#ifdef MySelf
        // 自定义协议格式:"x op y"
        std::string s = std::to_string(x);
        s += blank_space_sep;
        s += op;
        s += blank_space_sep;
        s += std::to_string(y);
        *out = s;
#else
        // JSON格式
        Json::Value root;
        root["x"] = x;
        root["y"] = y;
        root["op"] = op;
        Json::StyledWriter w;
        *out = w.write(root);
#endif
        return true;
    }

    // 反序列化:将字符串解析为请求对象
    bool Deserialize(const std::string &in)
    {
#ifdef MySelf
        // 解析自定义协议格式
        std::size_t left = in.find(blank_space_sep);
        if (left == std::string::npos) return false;
        std::string part_x = in.substr(0, left);

        std::size_t right = in.rfind(blank_space_sep);
        if (right == std::string::npos) return false;
        std::string part_y = in.substr(right + 1);

        if (left + 2 != right) return false;
        op = in[left + 1];
        x = std::stoi(part_x);
        y = std::stoi(part_y);
#else
        // 解析JSON格式
        Json::Value root;
        Json::Reader r;
        r.parse(in, root);
        x = root["x"].asInt();
        y = root["y"].asInt();
        op = root["op"].asInt();
#endif
        return true;
    }

    void DebugPrint()
    {
        std::cout << "新请求构建完成: " << x << op << y << "=?" << std::endl;
    }

public:
    int x;      // 第一个操作数
    int y;      // 第二个操作数
    char op;    // 运算符
};

// 响应类:处理计算响应
class Response
{
    // [响应类的实现与Request类似,只是处理result和code两个字段]
    // result: 计算结果
    // code: 状态码,0表示成功,非0表示各种错误
};

4. ServerCal.hpp-计算器服务处理器

ServerCal.hpp

实现服务器端的核心计算逻辑

#pragma once
#include <iostream>
#include "Protocol.hpp"

// 定义错误码枚举
enum
{
    Div_Zero = 1,    // 除零错误
    Mod_Zero,        // 取模零错误
    Other_Oper       // 未知运算符错误
};

// 服务器端计算器类
class ServerCal
{
public:
    ServerCal() {}

    // 核心计算功能辅助函数
    Response CalculatorHelper(const Request &req)
    {
        Response resp(0, 0);  // 初始化响应对象,默认结果0,状态码0
        
        // 根据运算符进行相应计算
        switch (req.op)
        {
        case '+':    // 加法运算
            resp.result = req.x + req.y;
            break;
        case '-':    // 减法运算
            resp.result = req.x - req.y;
            break;
        case '*':    // 乘法运算
            resp.result = req.x * req.y;
            break;
        case '/':    // 除法运算
        {
            if (req.y == 0)   // 处理除零错误
                resp.code = Div_Zero;
            else
                resp.result = req.x / req.y;
        }
        break;
        case '%':    // 取模运算
        {
            if (req.y == 0)   // 处理取模零错误
                resp.code = Mod_Zero;
            else
                resp.result = req.x % req.y;
        }
        break;
        default:     // 未知运算符
            resp.code = Other_Oper;
            break;
        }

        return resp;
    }

    // 主计算函数:处理完整的请求-响应流程
    // 输入格式示例:"len"\n"10 + 20"\n
    std::string Calculator(std::string &package)
    {
        // 1. 解码请求包
        std::string content;
        bool r = Decode(package, &content);  // 解析出实际内容
        if (!r)
            return "";

        // 2. 反序列化请求内容
        Request req;
        r = req.Deserialize(content);  // 将内容转换为请求对象
        if (!r)
            return "";

        // 3. 执行计算
        content = "";  // 清空content准备存储响应
        Response resp = CalculatorHelper(req);  // 调用计算辅助函数

        // 4. 构建响应包
        resp.Serialize(&content);   // 序列化响应对象
        content = Encode(content);  // 编码响应内容

        return content;  // 返回完整的响应包
    }

    ~ServerCal() {}
};

5. Socket.hpp-Socket通信封装类

Socket.hpp

封装底层Socket网络通信功能

#pragma once

#include <iostream>
#include <string>
#include <unistd.h>     // Unix标准函数
#include <cstring>      // memset等字符串操作
#include <sys/types.h>  // 基本系统数据类型
#include <sys/stat.h>   // 文件状态
#include <sys/socket.h> // Socket接口
#include <arpa/inet.h>  // IP地址转换函数
#include <netinet/in.h> // IPv4地址结构
#include "Log.hpp"      // 日志功能

// 错误码枚举
enum
{
    SocketErr = 2,  // Socket创建错误
    BindErr,        // 绑定错误
    ListenErr,      // 监听错误
};

// 监听队列长度
const int backlog = 10;

// Socket封装类
class Sock
{
public:
    Sock() {}
    ~Sock() {}

public:
    // 创建Socket
    void Socket()
    {
        // 创建TCP Socket
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd_ < 0)
        {
            // 创建失败,记录错误并退出
            lg(Fatal, "socker error, %s: %d", strerror(errno), errno);
            exit(SocketErr);
        }
    }

    // 绑定端口
    void Bind(uint16_t port)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;          // IPv4
        local.sin_port = htons(port);        // 主机字节序转网络字节序
        local.sin_addr.s_addr = INADDR_ANY;  // 绑定所有网卡

        // 绑定地址和端口
        if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, %s: %d", strerror(errno), errno);
            exit(BindErr);
        }
    }

    // 开始监听
    void Listen()
    {
        if (listen(sockfd_, backlog) < 0)
        {
            lg(Fatal, "listen error, %s: %d", strerror(errno), errno);
            exit(ListenErr);
        }
    }

    // 接受新连接
    int Accept(std::string *clientip, uint16_t *clientport)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        // 接受客户端连接
        int newfd = accept(sockfd_, (struct sockaddr*)&peer, &len);
        if(newfd < 0)
        {
            lg(Warning, "accept error, %s: %d", strerror(errno), errno);
            return -1;
        }
        // 获取客户端IP和端口
        char ipstr[64];
        inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));
        *clientip = ipstr;
        *clientport = ntohs(peer.sin_port);

        return newfd;
    }

    // 连接服务器
    bool Connect(const std::string &ip, const uint16_t &port)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        peer.sin_family = AF_INET;
        peer.sin_port = htons(port);
        inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));

        // 连接服务器
        int n = connect(sockfd_, (struct sockaddr*)&peer, sizeof(peer));
        if(n == -1) 
        {
            std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;
            return false;
        }
        return true;
    }

    // 关闭Socket
    void Close()
    {
        close(sockfd_);
    }

    // 获取文件描述符
    int Fd()
    {
        return sockfd_;
    }

private:
    int sockfd_;  // Socket文件描述符
};

6. TcpServer.hpp-TCP服务器框架

TcpServer.hpp

实现TCP服务器的主框架

#pragma once
#include <functional>
#include <string>
#include <signal.h>
#include "Log.hpp"
#include "Socket.hpp"

// 定义回调函数类型:接收字符串参数,返回字符串
using func_t = std::function<std::string(std::string &package)>;

// TCP服务器类
class TcpServer
{
public:
    // 构造函数:初始化端口和回调函数
    TcpServer(uint16_t port, func_t callback) : port_(port), callback_(callback)
    {
    }

    // 初始化服务器
    bool InitServer()
    {
        listensock_.Socket();      // 创建Socket
        listensock_.Bind(port_);   // 绑定端口
        listensock_.Listen();      // 开始监听
        lg(Info, "init server .... done");
        return true;
    }

    // 启动服务器
    void Start()
    {
        // 忽略子进程退出和管道破裂信号
        signal(SIGCHLD, SIG_IGN);
        signal(SIGPIPE, SIG_IGN);

        // 主循环
        while (true)
        {
            // 接受新的客户端连接
            std::string clientip;
            uint16_t clientport;
            int sockfd = listensock_.Accept(&clientip, &clientport);
            if (sockfd < 0)
                continue;

            // 记录新连接信息
            lg(Info, "accept a new link, sockfd: %d, clientip: %s, clientport: %d", 
               sockfd, clientip.c_str(), clientport);

            // 创建子进程处理客户端请求
            if (fork() == 0)
            {
                listensock_.Close();  // 子进程关闭监听socket
                std::string inbuffer_stream;  // 输入缓冲区

                // 处理客户端数据
                while (true)
                {
                    // 读取客户端数据
                    char buffer[1280];
                    ssize_t n = read(sockfd, buffer, sizeof(buffer));
                    if (n > 0)  // 读取成功
                    {
                        buffer[n] = 0;  // 字符串结束符
                        inbuffer_stream += buffer;  // 追加到输入缓冲区

                        // 调试日志
                        lg(Debug, "debug:\n%s", inbuffer_stream.c_str());

                        // 处理所有完整的请求
                        while (true)
                        {
                            // 调用回调函数处理请求
                            std::string info = callback_(inbuffer_stream);
                            if (info.empty())  // 没有完整请求则退出循环
                                break;

                            // 调试日志
                            lg(Debug, "debug, response:\n%s", info.c_str());
                            lg(Debug, "debug:\n%s", inbuffer_stream.c_str());

                            // 发送响应给客户端
                            write(sockfd, info.c_str(), info.size());
                        }
                    }
                    else if (n == 0)  // 客户端关闭连接
                        break;
                    else  // 读取错误
                        break;
                }

                exit(0);  // 子进程退出
            }
            close(sockfd);  // 父进程关闭客户端socket
        }
    }

    ~TcpServer()
    {
    }

private:
    uint16_t port_;      // 服务器端口
    Sock listensock_;    // 监听socket
    func_t callback_;    // 处理请求的回调函数
};

7. ClientCal.cc-计算器客户端

ClientCal.cc

实现客户端程序,发送计算请求

#include <iostream>
#include <string>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include "Socket.hpp"
#include "Protocol.hpp"

// 打印使用方法
static void Usage(const std::string &proc)
{
    std::cout << "\nUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

// 客户端主程序:./clientcal ip port
int main(int argc, char *argv[])
{
    // 检查命令行参数
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(0);
    }
    // 获取服务器IP和端口
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    // 创建并连接Socket
    Sock sockfd;
    sockfd.Socket();
    bool r = sockfd.Connect(serverip, serverport);
    if(!r) return 1;

    // 初始化随机数种子(使用时间和进程ID)
    srand(time(nullptr) ^ getpid());
    int cnt = 1;
    // 定义可用的运算符
    const std::string opers = "+-*/%=-=&^";

    // 输入缓冲区
    std::string inbuffer_stream;
    // 进行10次测试
    while(cnt <= 10)
    {
        std::cout << "===============第" << cnt << "次测试....., " << "===============" << std::endl;
        
        // 随机生成测试数据
        int x = rand() % 100 + 1;
        usleep(1234);  // 微秒级延迟
        int y = rand() % 100;
        usleep(4321);
        char oper = opers[rand()%opers.size()];  // 随机选择运算符
        
        // 创建请求对象
        Request req(x, y, oper);
        req.DebugPrint();  // 打印请求信息

        // 序列化请求
        std::string package;
        req.Serialize(&package);
        // 编码请求包
        package = Encode(package);

        // 发送请求到服务器
        write(sockfd.Fd(), package.c_str(), package.size());

        // 读取服务器响应
        char buffer[128];
        ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;  // 字符串结束符
            inbuffer_stream += buffer;  // 追加到输入缓冲区
            std::cout << inbuffer_stream << std::endl;

            // 解码响应
            std::string content;
            bool r = Decode(inbuffer_stream, &content);
            assert(r);  // 确保解码成功

            // 反序列化响应
            Response resp;
            r = resp.Deserialize(content);
            assert(r);  // 确保反序列化成功

            // 打印响应结果
            resp.DebugPrint();
        }

        std::cout << "=================================================" << std::endl;
        sleep(1);  // 延时1秒

        cnt++;
    }

    // 关闭连接
    sockfd.Close();
    return 0;
}

8. ServerCal.cc-计算器服务器

ServerCal.cc

实现服务器程序,处理客户端请求

#include "TcpServer.hpp"
#include "ServerCal.hpp"
#include <unistd.h>
// #include "Daemon.hpp"

// 打印使用方法
static void Usage(const std::string &proc)
{
    std::cout << "\nUsage: " << proc << " port\n" << std::endl; 
}

// 服务器主程序:./servercal 8080
int main(int argc, char *argv[])
{
    // 检查命令行参数
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }

    // 获取端口号
    uint16_t port = std::stoi(argv[1]);

    // 创建计算器服务对象
    ServerCal cal;

    // 创建TCP服务器对象
    // 使用std::bind绑定Calculator方法作为回调函数
    TcpServer *tsvp = new TcpServer(port, 
        std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));

    // 初始化服务器
    tsvp->InitServer();

    // 将进程变成守护进程
    // Daemon();  // 自定义守护进程函数
    daemon(0, 0); // 系统提供的守护进程函数
                  // 第一个参数0:切换工作目录到根目录
                  // 第二个参数0:关闭标准输入输出和错误流

    // 启动服务器
    tsvp->Start();

    /* 以下是测试代码,已注释
    // 测试响应序列化和反序列化
    Response resp(1000, 0);
    std::string content;
    resp.Serialize(&content);
    std::cout << content << std::endl;
    std::string package = Encode(content);
    std::cout << package;

    content = "";
    bool r = Decode(package, &content);
    std::cout << content << std::endl;

    Response temp;
    temp.Deserialize(content);

    std::cout << temp.result << std::endl;
    std::cout << temp.code << std::endl;

    // 测试请求序列化和反序列化
    Request req(12364566, 43454356, '+');
    std::string s;
    req.Serialize(&s);
    s = Encode(s);
    std::cout << s;

    std::string content;
    bool r = Decode(s, &content);
    std::cout << content << std::endl;
    Request temp;
    temp.Deserialize(content);

    std::cout << temp.x << std::endl;
    std::cout << temp.op << std::endl;
    std::cout << temp.y << std::endl;
    */

    return 0;
}

9. 代码时序

1. 服务器启动时序

ServerCal.cc (主程序)
    ↓
1. 解析命令行参数(端口号)
    ↓
2. 创建ServerCal对象
    ↓
3. 创建TcpServer对象
    |→ 绑定Calculator回调函数
    ↓
4. 初始化服务器(InitServer)
    |→ 创建Socket
    |→ 绑定端口
    |→ 开始监听
    ↓
5. 守护进程化
    |→ 后台运行
    |→ 重定向标准IO
    ↓
6. 启动服务器(Start)
    |→ 注册信号处理
    |→ 进入主循环

2. 客户端连接时序

TcpServer::Start (主循环)
    ↓
1. Accept等待连接
    ↓
2. 收到新连接
    |→ 获取客户端信息(IP/端口)
    |→ 记录连接日志
    ↓
3. Fork子进程
    |→ 子进程:处理客户端请求
    |→ 父进程:继续Accept新连接

3. 请求处理时序

子进程处理流程
    ↓
1. 读取客户端数据
    |→ 追加到输入缓冲区
    ↓
2. 解析协议(Protocol::Decode)
    |→ 提取消息长度
    |→ 检查完整性
    ↓
3. 调用回调函数(Calculator)
    |→ 反序列化请求
    |→ 执行计算
    |→ 序列化响应
    ↓
4. 发送响应
    |→ 编码响应包
    |→ 写入socket

4. 完整的请求-响应时序

客户端                    服务器                    子进程
  |                        |                         |
  |------ 连接请求 ------>|                         |
  |                        |--- fork() ------------->|
  |                        |                         |
  |------ 计算请求 ----------------------→          |
  |                        |                         |
  |                        |        1. 解析请求      |
  |                        |        2. 执行计算      |
  |                        |        3. 构造响应      |
  |                        |                         |
  |<----- 计算结果 ----------------------           |
  |                        |                         |
  |------ 关闭连接 ------>|                         |
  |                        |                         |

5. 数据处理时序

Request数据流
    ↓
1. 序列化(Serialize)
    |→ JSON格式或自定义格式
    ↓
2. 协议封装(Encode)
    |→ 添加长度和分隔符
    ↓
3. 网络传输
    |→ write/read
    ↓
4. 协议解析(Decode)
    |→ 提取有效载荷
    ↓
5. 反序列化(Deserialize)
    |→ 还原对象数据

6. 日志记录时序

Log系统
    ↓
1. 生成日志内容
    |→ 时间戳
    |→ 日志级别
    |→ 具体信息
    ↓
2. 根据配置输出
    |→ 屏幕显示
    |→ 单一文件
    |→ 分级文件

7. 资源释放时序

程序退出流程
    ↓
1. 子进程退出
    |→ 关闭客户端socket
    |→ exit(0)
    ↓
2. 父进程清理
    |→ SIGCHLD信号处理
    |→ 僵尸进程回收

这种时序设计的优点:

  1. 多进程并发处理请求
  2. 父子进程职责明确
  3. 协议设计清晰
  4. 资源管理完善
  5. 错误处理周到

主要的时序特点是采用了经典的多进程并发服务器模型,每个客户端连接由独立的子进程处理,保证了请求处理的隔离性和可靠性。


网站公告

今日签到

点亮在社区的每一天
去签到