前面我们已经学习了用UDP进行网络编程。接下来我们就来学习利用另一个重要网络协议——TCP,来进行网络项目编程

        相关代码已经上传至作者的个人gitee:楼田莉子/Linux学习喜欢请关注

目录

套接字地址结构体

        sockaddr_in 结构体

        in_addr 结构体

TCP相关的接口

        socket()

        bind() 

        listen() 

        accept() 

        connect()

        send() 和 recv() 

        close()

        套接字选项 (setsockopt)

日志

网络客户端代码

TCP服务器(单线程)

        服务器代码

        服务器运行

        客户端运行

TCP服务器(多进程版本)

        版本一:(最佳实践)

        版本二:(邪修)

TCP服务器(多线程版本)

        版本一(正常多线程)

        版本二(线程池)

        线程池相关


套接字地址结构体

        在进行任何网络通信前,首先需要指定通信的对端。在TCP/IPv4编程中,最关键的结构体是 sockaddr_in 和 in_addr,它们用于存储和传递地址信息。

        sockaddr_in 结构体

这是进行IPv4地址编程时最常用的结构体,它专门用来处理IP地址和端口号。为了方便编程,它被设计成与通用的 sockaddr 结构体在内存布局上兼容,因此在使用相关函数时,通常需要将 sockaddr_in* 强制转换为 sockaddr* 类型。

        作用: 用于标识一个IPv4的套接字地址,包含地址族、端口号和IP地址。

        表达式 (C/C++定义):

struct sockaddr_in {
    short            sin_family;   // 地址族,对于IPv4,必须设置为 AF_INET
    unsigned short   sin_port;     // 端口号(16位),需要使用网络字节序
    struct in_addr   sin_addr;     // IPv4地址结构体(32位)
    char             sin_zero[8];  // 填充字段,通常设置为0,以确保与struct sockaddr大小一致
};
  • 参数选项详解:

    • sin_family: 必须设置为 AF_INET,表明这是一个IPv4地址。

    • sin_port: 16位端口号。关键点:需要调用 htons()(host to network short)函数将主机字节序转换为网络字节序(大端序)。

    • sin_addr: 这是一个 in_addr 结构体,专门用来存放32位的IPv4地址。

    • sin_zero: 仅作为填充,使 sockaddr_in 的大小与通用的 sockaddr 结构体一致。编程时应使用 bzero() 或 memset() 将其置零。

        in_addr 结构体

        这个结构体仅用于表示一个32位的IPv4地址。

        作用: 存储IPv4地址。

        表达式 (C/C++定义):

struct in_addr {
    union {
        struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
        struct { u_short s_w1,s_w2; } S_un_w;
        u_long S_addr; // 最常用的方式
    } S_un;
};
// 为了方便,通常直接使用 s_addr 宏
// #define s_addr S_un.S_addr

在实际代码中,我们通常直接操作 s_addr 字段,它被定义为一个 unsigned long 类型的整数。

  • 参数选项详解:

    • s_addr: 32位的IPv4地址。同样需要是网络字节序。可以通过 inet_addr() 函数将点分十进制字符串(如 "192.168.1.1")直接转换为此格式,或者使用更现代、更安全的 inet_pton() 函数进行转换。

使用示例

以下代码片段展示了如何填充一个服务器的地址结构体:

struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr)); // 将整个结构体清零
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("服务器IP地址"); // 或使用 INADDR_ANY 表示本机所有IP
server_addr.sin_port = htons(服务器端口号);

TCP相关的接口

        socket()

  一切的开始,先要创建一个套接字描述符。

  • 作用: 创建一个新的套接字,可以理解为向操作系统申请一个用于网络通信的文件描述符。

  • 表达式

    int socket(int domain, int type, int protocol);

  • 参数选项详解:

    • domain (协议域): 选择通信的协议族。对于IPv4,使用 AF_INET(或 PF_INET,二者在Linux中通常等价)。

    • type (套接字类型): 指定通信方式。对于TCP,使用 SOCK_STREAM(面向连接的字节流)。

    • protocol (协议): 通常设为0,表示根据前两个参数选择默认协议。对于 SOCK_STREAM,默认协议就是TCP。

  • 返回值:

    • 成功: 返回一个非负整数,即新套接字的文件描述符。

    • 失败: 返回 -1,并设置 errno 以指示错误。

        bind() 

        服务器端需要将创建好的套接字与一个具体的IP地址和端口号绑定起来,这样客户端才能找到它。

        作用: 将套接字描述符与一个本地协议地址(IP和端口)绑定。

        表达式

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数选项详解:

    • sockfd: 上一步 socket() 返回的文件描述符。

    • addr: 指向一个 sockaddr 结构体的指针。实际传递的通常是 sockaddr_in 结构体的地址,需要强制类型转换。

    • addrlen: 传入的地址结构体的大小,通常使用 sizeof(struct sockaddr_in)

  • 返回值:

    • 成功: 返回0。

    • 失败: 返回 -1,并设置 errno。常见错误如 EADDRINUSE(地址已被使用)。

        listen() 

        服务器端在绑定地址后,需要告知内核自己将作为一个服务端,准备接受来自客户端的连接请求。

        作用: 将套接字由主动套接字转化为被动监听套接字,并为它维护两个队列:存放未完成三次握手的连接请求(SYN_RCVD状态)的队列,以及已完成三次握手、等待 accept() 的连接(ESTABLISHED状态)的队列。

  • 表达式

    int listen(int sockfd, int backlog);

  • 参数选项详解:

    • sockfd: 已绑定的套接字描述符。

    • backlog: 指定已完成连接队列的最大长度。即允许有多少个客户端已经完成了TCP三次握手,但在被服务器应用程序调用 accept() 取走之前,处于挂起状态。如果队列满了,新的连接请求可能会被拒绝或延迟。

  • 返回值:

    • 成功: 返回0。

    • 失败: 返回 -1,并设置 errno

        accept() 

        这是一个阻塞函数,服务器调用它来从已完成连接队列的队首取走一个连接。如果没有已完成的连接,进程会进入睡眠状态直到有新的连接到来。

  • 作用: 从监听套接字的已完成连接队列中取出第一个连接,如果队列为空则阻塞。成功时,它会创建一个新的套接字描述符,这个新描述符用于和这个特定的客户端进行通信。

  • 表达式

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

  • 参数选项详解:

    • sockfd: 监听套接字描述符(listen 的那个)。

    • addr: 这是一个传出参数,指向一个 sockaddr 结构体,用于接收客户端的协议地址。如果不关心客户端地址,可以设为 NULL

    • addrlen: 这是一个值-结果参数。调用前,应初始化为 addr 指向的缓冲区大小;返回时,包含内核存放的客户端地址的实际字节数。

  • 返回值:

    • 成功: 返回一个全新的套接字描述符(已连接套接字),后续的 send() 和 recv() 都使用这个新描述符。

    • 失败: 返回 -1,并设置 errno

        connect()

        这是客户端主动连接服务器的函数。

  • 作用: 客户端调用此函数,向服务器发起TCP连接请求,触发三次握手过程。

  • 表达式

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • 参数选项详解:

    • sockfd: 客户端的套接字描述符。

    • addr: 指向服务器的地址结构体(包含服务器的IP和端口)。

    • addrlen: 地址结构体的大小。

  • 返回值:

    • 成功: 返回0。注意,对于阻塞式套接字,connect 只有在连接成功建立(三次握手完成)或出错时才会返回。

    • 失败: 返回 -1,并设置 errno

        send() 和 recv() 

        连接建立后,就可以使用这两个函数来交换数据了。

  • 作用: 在已连接的套接字上发送和接收数据。

  • 表达式:

    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    
    ssize_t recv(int sockfd, void *buf, size_t len, int flags);

  • 参数选项详解:

    • sockfd: 已连接套接字描述符(服务器端由 accept 返回的,客户端是自己的那个)。

    • buf: 指向要发送数据的缓冲区,或接收数据的缓冲区。

    • len: 缓冲区的大小。

    • flags: 一般设为0。可以选择 MSG_OOB 处理带外数据,或 MSG_DONTWAIT 进行非阻塞操作等。

  • 返回值:

    • send() 成功: 返回实际发送的字节数。注意,这个返回值可能小于请求发送的长度 len,需要在循环中继续发送剩余部分。

    • recv() 成功: 返回实际接收到的字节数。关键点:如果返回0,表示对端已经正常关闭了连接(发送了FIN)。

    • 失败: 两者失败均返回 -1,并设置 errno

        close()

        通信结束后,需要关闭套接字以释放资源。

  • 作用: 关闭一个套接字描述符,终止TCP连接。

  • 表达式:

     int close(int sockfd);

  • 参数选项详解:

    • sockfd: 要关闭的套接字描述符。

  • 返回值:

    • 成功: 返回0。

    • 失败: 返回 -1。

        套接字选项 (setsockopt)

        除了以上内容,也可以通过 setsockopt 和 getsockopt 来设置或获取这些选项。

  SO_REUSEADDR作用:允许重用本地地址和端口。场景:服务器重启时,可以立即再次使用同一个端口,而不必等待 TIME_WAIT 状态结束,避免"Address already in use"错误。

  SO_KEEPALIVE作用:定期探测连接是否存活。场景:对于长时间无数据交换的连接,可以自动检测客户端是否崩溃或网络是否中断。

  SO_LINGER作用:设置 close() 关闭连接时的行为。场景:控制 close() 返回前是否需要等待未发送数据发送完成,以及如何处理未发送数据。

  SO_RCVBUF / SO_SNDBUF作用:调整套接字接收和发送缓冲区的大小。场景:根据网络环境和应用需求,优化吞吐量。

日志

#pragma once

#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <string>
#include <memory>
#include <sstream>
#include <chrono>
#include <iomanip>
#include <ctime>   
#include <unistd.h>
#include <filesystem> // C++17
#include <fstream>
//RAII风格代码:资源获取即初始化
// 互斥锁类,封装了pthread_mutex_t
class Mutex
{
    public:
        Mutex()
        {
            pthread_mutex_init(&_lock, nullptr);
        }
        void Lock()
        {
            pthread_mutex_lock(&_lock);
        }
        pthread_mutex_t *Ptr()
        {
            return &_lock;
        }
        void Unlock()
        {
            pthread_mutex_unlock(&_lock);
        }
        ~Mutex()
        {
            pthread_mutex_destroy(&_lock);
        }
    private:
        pthread_mutex_t _lock;
};

class LockGuard // RAII风格代码
{
    public:
        LockGuard(Mutex &lock):_lockref(lock)
        {
            _lockref.Lock();
        }
        ~LockGuard()
        {
            _lockref.Unlock();
        }
    private:
        Mutex &_lockref;
};


//日志本体
namespace Logger
{   
    enum class LogLevel
    {
        INFO,    ///< 信息性消息,记录应用程序的正常运行状态(如启动、配置加载等)
        WARNING, ///< 警告,表示潜在的问题或非预期的情形,但应用程序仍能继续运行
        ERROR,   ///< 错误,表示发生了严重的操作失败,但不影响整个应用程序的继续运行
        FATAL,   ///< 致命错误,表示严重的故障,通常会导致应用程序终止
        DEBUG    ///< 调试信息,用于开发和排错阶段,记录详细的内部状态或流程
    };
    std::string LogLevelToString(LogLevel level)
    {
        switch (level)
        {
            case LogLevel::INFO:
                return "INFO";
            case LogLevel::WARNING:
                return "WARNING";
            case LogLevel::ERROR:
                return "ERROR";
            case LogLevel::FATAL:
                return "FATAL";
            case LogLevel::DEBUG:
                return "DEBUG";
            default:
                return "UNKNOWN";
        }
    }
    std::string GetCurrentDateTime() 
    {
        auto now = std::chrono::system_clock::now();
        std::time_t now_c = std::chrono::system_clock::to_time_t(now);

        std::tm tm_info;                      // 用户提供的缓冲区
        localtime_r(&now_c, &tm_info);         // POSIX 线程安全函数

        std::ostringstream oss;
        oss << std::put_time(&tm_info, "%Y-%m-%d %H:%M:%S");
        return oss.str();
    }
    // 输出角度 -- 刷新策略
    // 1. 显示器打印
    // 2. 文件写入

    // 日志的生成:
    // 1. 构建日志字符串
    // 2. 根据不同的策略,进行刷新

    //策略模式接口
    class LogStrategy
    {
    public:
        virtual void LogRefresh(const std::string &message) = 0;
        virtual ~LogStrategy() = default;
    };
    // 控制台日志刷新策略, 日志将来要向显示器打印
    class ConsoleStrategy : public LogStrategy
    {
    public:
        // 显示器打印策略刷新
        void LogRefresh(const std::string &message) override
        {
            LockGuard lockguard(_mutex);
            std::cerr << message << std::endl; // ??
        }
        ~ConsoleStrategy()
        {
        }

    private:
        Mutex _mutex;
    };

    const std::string defaultpath = "./log";
    const std::string defaultfilename = "log.txt";
    namespace fs = std::filesystem;

    
    // 文件策略
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string &path = defaultpath, const std::string &name = defaultfilename)
            : _logpath(path),
              _logfilename(name)
        {
            if (std::filesystem::exists(_logpath))
                return;

            try
            {
                std::filesystem::create_directories(_logpath);
            }
            catch(const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
            if(!_logpath.empty()&&_logpath.back() != '/')
                _logpath += '/';
        }
        // 文件策略刷新
        void LogRefresh(const std::string &message) override
        {
            {
                std::string targetlogfile = _logpath +  _logfilename;
                LockGuard lockguard(_mutex);
                std::ofstream logFile(targetlogfile, std::ios::app);// 以追加模式打开文件
                if (!logFile.is_open())
                {
                    std::cerr << "无法打开日志文件: " << targetlogfile << std::endl;
                    return;
                }        
                logFile << message << "\n";
                logFile.close();
            }
            
            
        }

        ~FileLogStrategy()
        {
        }

    private:
        std::string _logpath;
        std::string _logfilename;
        Mutex _mutex;
    };

    // 根据日志等级分类保存的策略类
    class LevelFileLogStrategy : public LogStrategy {
    public:
        /**
         * @brief 构造函数,指定日志根目录
         * @param log_dir 存放等级日志文件的目录,默认为 "./logs"
         */
        LevelFileLogStrategy(const std::string &log_dir = "./logs") : _log_dir(log_dir) {
            // 确保目录存在
            if (!std::filesystem::exists(_log_dir)) {
                std::filesystem::create_directories(_log_dir);
            }
            // 规范化目录路径,末尾添加 '/'
            if (!_log_dir.empty() && _log_dir.back() != '/') {
                _log_dir += '/';
            }
        }

        /**
         * @brief 刷新日志:根据等级写入对应文件
         * @param message 完整的日志消息(格式由 Logger::LogMessage 生成)
         */
        void LogRefresh(const std::string &message) override {
            // 1. 从消息中提取日志等级
            LogLevel level = extractLogLevel(message);
            // 2. 构造对应的文件名(例如:INFO.log)
            std::string filename = _log_dir + LogLevelToString(level) + ".log";

            // 3. 线程安全地追加写入文件
            LockGuard lockguard(_mutex);
            std::ofstream logFile(filename, std::ios::app);
            if (!logFile.is_open()) {
                std::cerr << "无法打开日志文件: " << filename << std::endl;
                return;
            }
            logFile << message << "\n";
            logFile.close();
        }

    private:
        std::string _log_dir;      // 日志根目录
        Mutex _mutex;              // 文件写入互斥锁

        /**
         * @brief 从日志消息中解析出等级
         * @param msg 完整日志消息,格式为 "[时间][等级][PID][文件:行号] 用户内容"
         * @return 对应的 LogLevel 枚举值,解析失败时默认返回 INFO
         */
        LogLevel extractLogLevel(const std::string &msg) {
            // 寻找第一个 ']' 的位置
            size_t first_close = msg.find(']');
            if (first_close == std::string::npos) {
                return LogLevel::INFO;   // 格式错误,默认 INFO
            }
            // 寻找第二个 '[' 的位置
            size_t second_open = msg.find('[', first_close);
            if (second_open == std::string::npos) {
                return LogLevel::INFO;
            }
            // 寻找第二个 ']' 的位置
            size_t second_close = msg.find(']', second_open);
            if (second_close == std::string::npos) {
                return LogLevel::INFO;
            }
            // 提取等级字符串(如 "INFO")
            std::string level_str = msg.substr(second_open + 1, second_close - second_open - 1);

            // 映射到 LogLevel 枚举
            if (level_str == "INFO")    return LogLevel::INFO;
            if (level_str == "WARNING") return LogLevel::WARNING;
            if (level_str == "ERROR")   return LogLevel::ERROR;
            if (level_str == "FATAL")   return LogLevel::FATAL;
            if (level_str == "DEBUG")   return LogLevel::DEBUG;
            return LogLevel::INFO;      // 未知等级,默认 INFO
        }
    };
    class Logger
    {
        public:
            Logger()
            {}
            void UseConsoleStrategy()
            {
                _strategy = std::make_unique<ConsoleStrategy>();
            }
            void UseFileStrategy()
            {
                _strategy = std::make_unique<FileLogStrategy>();
            }
            void UseLevelFileStrategy(const std::string &log_dir = "./logs") 
            {
                _strategy = std::make_unique<LevelFileLogStrategy>(log_dir);
            }
            void Debug(const std::string &message)
            {
                if (_strategy!= nullptr)
                {
                    _strategy->LogRefresh("[DEBUG] " + GetCurrentDateTime() + " - " + message);
                }
            }
            //日志内容
            //一条完整的日志信息=> [日志级别] + 当前时间 + 进程ID + 文件名 + 行号 + 日志信息
            //我们想以RAII形式刷新日志信息
            class LogMessage
            {
                public:
                    LogMessage(LogLevel level, const std::string &filename, size_t line, Logger &logger)
                        : _level(level), _filename(filename), _line(line),_logger(logger)
                    {
                        _cur_time = GetCurrentDateTime();
                        _pid=getpid();
                        // 构建日志左半部分信息
                        std::stringstream oss;
                        oss <<"["<<_cur_time<<"]"
                            <<"["<<LogLevelToString(_level)<<"]"
                            <<"["<<_pid<<"]"
                            <<"[" << filename << ":" 
                            << line << "]";
                        _LogInfo = oss.str();
                    }
                    template<typename T>
                    LogMessage& operator<<(const T&info)
                    {
                        std::stringstream oss;
                        oss<< info;
                        _LogInfo += oss.str();
                        return *this;
                    }
                    ~LogMessage()
                    {
                        if (_logger._strategy != nullptr)
                        {
                            _logger._strategy->LogRefresh(_LogInfo);
                        }
                    }
                private:
                    std::string _cur_time;
                    LogLevel _level; 
                    pid_t _pid;
                    std::string _filename;
                    size_t _line;
                    std::string _LogInfo;
                    Logger &_logger ;//方便进行后续策略方式刷新
            };
            //对LogMessage进行()重载
            //必须用拷贝,否则会导致<<重载的时候内容消失
            LogMessage operator()(LogLevel level, const std::string filename, size_t line)
            {
                return LogMessage(level, filename, line, *this);
            }
            ~Logger()
            {}
        private:
            std::unique_ptr<LogStrategy> _strategy;
    };
    //日志对象全局使用
    Logger logger;
    #define ENABLE_LOG_CONSOLE() logger.UseConsoleStrategy()
    #define ENABLE_LOG_FILE() logger.UseFileStrategy()
    #define Log(level) logger(level, __FILE__, __LINE__)
    #define ENABLE_LOG_LEVEL_FILE(log_dir) logger.UseLevelFileStrategy(log_dir)
}



网络客户端代码

#pragma once 
#include<iostream>
#include<string>
#include<cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//网络客户端封装
class InetAddr
{
    private:
        std::string IP_;
        uint16_t port_;
        struct sockaddr_in address_;
        socklen_t len_;
    public:
        InetAddr()=default;
        InetAddr(const struct sockaddr_in& address) : address_(address),len_(sizeof(address))
        {
            IP_ = inet_ntoa(address_.sin_addr);   // 将二进制IP转换为字符串
            port_ = ntohs(address_.sin_port);      // 端口转换正确
        }
        InetAddr(uint16_t port,const std::string ip="0.0.0.0"):IP_(ip),port_(port)
        {
            bzero(&address_, sizeof(address_));
            address_.sin_family = AF_INET;
            address_.sin_addr.s_addr = inet_addr(IP_.c_str());
            address_.sin_port = htons(port_);
            len_=sizeof(address_);
        }
        struct sockaddr_in*GetNetAddress()
        {
            return &address_;
        }
        ~InetAddr()=default;
        socklen_t len()
        {
            return len_;
        }
        bool operator==(const InetAddr&addrs)
        {
            return (this-> IP_==addrs.IP_)&&(this->port_==addrs.port_);
        }
        std::string ToString()
        {
            return "[" + IP_ + ":" + std::to_string(port_) + "]";
        }

};

TCP服务器(单线程)

        服务器代码

#pragma once
#include"Log.h"
#include"InetAddr.h"
#include<unistd.h>
using namespace Logger;
enum
{
    SUCCESS=0,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,
    USAGE_ERR
};

//链接队列长度
static const int gblocklist=16;
//默认端口号
static const uint16_t  defaultport=8080;
class TCPServer
{
    private:
        uint16_t port_;//端口号
        //不需要显式包含IP
        int ListenSocketfd_;
    public:
        TCPServer(uint16_t port=defaultport):port_(port)
        {}
        void Init()
        {
            //创建套接字
            ListenSocketfd_=socket(AF_INET,SOCK_STREAM,0);
            if(ListenSocketfd_<0)
            {
                Log(LogLevel::ERROR)<<"服务器创建套接字失败";
                exit(SOCKET_ERR);
            }
            Log(LogLevel::DEBUG)<<"创建套接字成功"<<ListenSocketfd_;
            //初始化网络信息
            struct sockaddr_in local;
            memset(&local,0,sizeof(local));
            local.sin_family=AF_INET;
            local.sin_port=htons(port_);
            local.sin_addr.s_addr=INADDR_ANY;//任意IPbind
            //bind
            int n=bind(ListenSocketfd_,(struct sockaddr*)&local,sizeof(local));
            if(n<0)
            {
                Log(LogLevel::ERROR)<<"绑定失败";
                exit(BIND_ERR);
            }
            Log(LogLevel::DEBUG)<<"绑定成功";
            //tcp是面向连接的,所以必须出于监听状态
            int m=listen(ListenSocketfd_,gblocklist);
            if(m<0)
            {
                Log(LogLevel::ERROR)<<"监听失败";
                exit(LISTEN_ERR);
            }
            Log(LogLevel::DEBUG)<<"监听成功";
        }
        void ServerIO(int fd,InetAddr&address)
        {
            Log(LogLevel::DEBUG)<<"客户端信息"<<address.ToString();
            while(1)
            {
                char inbuffer[1024]={0};
                //读
                ssize_t n=read(fd,inbuffer,sizeof(inbuffer)-1);
                //写
                if(n>0)
                {
                    inbuffer[n]={0};
                    Log(LogLevel::INFO)<<address.ToString()<<"say#"<<inbuffer;
                    std::string echo_server="server echo# ";
                    echo_server+=inbuffer;

                    write(fd,echo_server.c_str(),echo_server.size());
                }
                else if(n==0)
                {
                    Log(LogLevel::INFO)<<"客户端退出"<<address.ToString();
                    break;
                }
                //读出错
                else
                {
                    Log(LogLevel::WARNING)<<"客户端出错"<<address.ToString();
                    break;
                }

            }
        }
        void Start()
        {
            while(1)
            {
                struct sockaddr_in clientaddr;
                socklen_t len=sizeof(clientaddr);
                int socketfd=accept(ListenSocketfd_,(struct sockaddr*)&clientaddr,&len);
                if(socketfd<0)
                {
                    Log(LogLevel::WARNING)<<"监听失败";
                    continue;
                }
                Log(LogLevel::DEBUG)<<"监听成功";
                //处理ListenSocketfd_
                InetAddr clientaddress(clientaddr);
                ServerIO(socketfd,clientaddress);
                close(socketfd);
            }
        }
        ~TCPServer()
        {
            close(ListenSocketfd_);
        }
};

        服务器运行

#include"EchoTCPServer.hpp"
#include<memory>

inline static void Usage(const std::string &proc)
{
    std::cerr<<"输入错误\n\t";
    std::cerr<<proc<<"你应该告诉我你的端口号"<<std::endl;
}

int main(int argc,char*argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    ENABLE_LOG_CONSOLE();
    // std::string ip=argv[1];
    uint16_t port=std::atoi(argv[1]);
    auto tsvr=std::make_unique<TCPServer>(port);
    tsvr->Init();
    tsvr->Start();
    return 0;
}

        客户端运行

#include"Log.h"
#include"InetAddr.h"
#include<iostream>
#include<cstdlib>
#include <sys/socket.h>
#include <netinet/in.h> 
#include <arpa/inet.h>  
#include <cstring>

using namespace Logger;
inline static void Usage(const std::string &proc)
{
    std::cerr<<"输入错误\n\t";
    std::cerr<<proc<<"你应该告诉我服务器的IP和端口号"<<std::endl;
}
//我们如何获取server的IP地址和端口号?
int main(int argc,char *argv[])
{   
    if(argc!=3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string ServerIp=argv[1];
    uint16_t ServerPort=std::atoi(argv[2]);

    
    //创建socket
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
    {
        std::cerr<<"客户端套接字创建失败"<<std::endl;
        exit(-1);
    }
    //客户端TCP绑定必须需要显示绑定
    //服务器TCP不需要自己手动显示的进行bind,OS会自动bind的!
    //OS 帮助我们进行随机端口,防止端口号冲突
    // 应该干什么?bind端口号ip,发起建立连接的工作
    // {client ip, client port} <-> {server ip, server port}
    InetAddr serveraddress(ServerPort,ServerIp);
    int n=connect(sockfd,(struct sockaddr*)serveraddress.GetNetAddress(),serveraddress.len());
    if(n<0)
    {
        std::cerr << "连接服务器 " << serveraddress.GetNetAddress() << " 失败, 错误码: " << errno << " (" << strerror(errno) << ")" << std::endl;
    exit(3);
        exit(3);
    }
    std::cerr<<"链接服务器"<<serveraddress.GetNetAddress()<<"成功"<<std::endl;
    //通信
    while(1)
    {
        std::string line;
        std::cout << "please Enter# ";
        std::getline(std::cin, line);

        write(sockfd, line.c_str(), line.size());

        char inbuffer[1024];
        ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer));
        if(n > 0)
        {
            inbuffer[n] = 0;
            std::cout << inbuffer << std::endl;
        }
        else if(n == 0)
        {
            std::cout << "read enff of file!" << std::endl;
            break;
        }
        else
        {
            std::cerr << "read error!" << std::endl;
            break;
        }
    }
    


    return 0;
}

        结果为:

TCP服务器(多进程版本)

        只有服务器代码改了,所以这里只发服务器代码

        版本一:(最佳实践)

#pragma once
#include"Log.h"
#include"InetAddr.h"
#include<unistd.h>
#include<signal.h>
using namespace Logger;
enum
{
    SUCCESS=0,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,
    USAGE_ERR,
    FORK_ERR
};

//链接队列长度
static const int gblocklist=16;
//默认端口号
static const uint16_t  defaultport=8080;
class TCPServer
{
    private:
        uint16_t port_;//端口号
        //不需要显式包含IP
        int ListenSocketfd_;
    public:
        TCPServer(uint16_t port=defaultport):port_(port)
        {}
        void Init()
        {
            //创建套接字
            ListenSocketfd_=socket(AF_INET,SOCK_STREAM,0);
            if(ListenSocketfd_<0)
            {
                Log(LogLevel::ERROR)<<"服务器创建套接字失败";
                exit(SOCKET_ERR);
            }
            Log(LogLevel::DEBUG)<<"创建套接字成功"<<ListenSocketfd_;
            //初始化网络信息
            struct sockaddr_in local;
            memset(&local,0,sizeof(local));
            local.sin_family=AF_INET;
            local.sin_port=htons(port_);
            local.sin_addr.s_addr=INADDR_ANY;//任意IPbind
            //bind
            int n=bind(ListenSocketfd_,(struct sockaddr*)&local,sizeof(local));
            if(n<0)
            {
                Log(LogLevel::ERROR)<<"绑定失败";
                exit(BIND_ERR);
            }
            Log(LogLevel::DEBUG)<<"绑定成功";
            //tcp是面向连接的,所以必须出于监听状态
            int m=listen(ListenSocketfd_,gblocklist);
            if(m<0)
            {
                Log(LogLevel::ERROR)<<"监听失败";
                exit(LISTEN_ERR);
            }
            Log(LogLevel::DEBUG)<<"监听成功";
        }
        void ServerIO(int fd,InetAddr&address)
        {
            Log(LogLevel::DEBUG)<<"客户端信息"<<address.ToString();
            while(1)
            {
                char inbuffer[1024]={0};
                //读
                ssize_t n=read(fd,inbuffer,sizeof(inbuffer)-1);
                //写
                if(n>0)
                {
                    inbuffer[n]={0};
                    Log(LogLevel::INFO)<<address.ToString()<<"say#"<<inbuffer;
                    std::string echo_server="server echo# ";
                    echo_server+=inbuffer;

                    write(fd,echo_server.c_str(),echo_server.size());
                }
                else if(n==0)
                {
                    Log(LogLevel::INFO)<<"客户端退出"<<address.ToString();
                    break;
                }
                //读出错
                else
                {
                    Log(LogLevel::WARNING)<<"客户端出错"<<address.ToString();
                    break;
                }

            }
        }
        void Start()
        {
            signal(SIGCHLD,SIG_IGN);
            while(1)
            {
                struct sockaddr_in clientaddr;
                socklen_t len=sizeof(clientaddr);
                int socketfd=accept(ListenSocketfd_,(struct sockaddr*)&clientaddr,&len);
                if(socketfd<0)
                {
                    Log(LogLevel::WARNING)<<"监听失败";
                    continue;
                }
                Log(LogLevel::DEBUG)<<"监听成功";
                // //处理ListenSocketfd_
                // InetAddr clientaddress(clientaddr);
                // ServerIO(socketfd,clientaddress);
                //多进程版本
                pid_t id=fork();
                if(id<0)//fork失败
                {
                    Log(LogLevel::ERROR)<<"fork失败";
                    exit(FORK_ERR);
                }
                else if(id==0) //子进程
                {
                    InetAddr clientaddress(clientaddr);
                    ServerIO(socketfd, clientaddress);
                    close(socketfd);
                    exit(0); // 重要
                }
                else        //父进程
                {

                }

                close(socketfd);
            }
        }
        ~TCPServer()
        {
            close(ListenSocketfd_);
        }
};

        版本二:(邪修)

#pragma once
#include"Log.h"
#include"InetAddr.h"
#include<unistd.h>
#include<sys/wait.h>
using namespace Logger;
enum
{
    SUCCESS=0,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,
    USAGE_ERR,
    FORK_ERR
};

//链接队列长度
static const int gblocklist=16;
//默认端口号
static const uint16_t  defaultport=8080;
class TCPServer
{
    private:
        uint16_t port_;//端口号
        //不需要显式包含IP
        int ListenSocketfd_;
    public:
        TCPServer(uint16_t port=defaultport):port_(port)
        {}
        void Init()
        {
            //创建套接字
            ListenSocketfd_=socket(AF_INET,SOCK_STREAM,0);
            if(ListenSocketfd_<0)
            {
                Log(LogLevel::ERROR)<<"服务器创建套接字失败";
                exit(SOCKET_ERR);
            }
            Log(LogLevel::DEBUG)<<"创建套接字成功"<<ListenSocketfd_;
            //初始化网络信息
            struct sockaddr_in local;
            memset(&local,0,sizeof(local));
            local.sin_family=AF_INET;
            local.sin_port=htons(port_);
            local.sin_addr.s_addr=INADDR_ANY;//任意IPbind
            //bind
            int n=bind(ListenSocketfd_,(struct sockaddr*)&local,sizeof(local));
            if(n<0)
            {
                Log(LogLevel::ERROR)<<"绑定失败";
                exit(BIND_ERR);
            }
            Log(LogLevel::DEBUG)<<"绑定成功";
            //tcp是面向连接的,所以必须出于监听状态
            int m=listen(ListenSocketfd_,gblocklist);
            if(m<0)
            {
                Log(LogLevel::ERROR)<<"监听失败";
                exit(LISTEN_ERR);
            }
            Log(LogLevel::DEBUG)<<"监听成功";
        }
        void ServerIO(int fd,InetAddr&address)
        {
            Log(LogLevel::DEBUG)<<"客户端信息"<<address.ToString();
            while(1)
            {
                char inbuffer[1024]={0};
                //读
                ssize_t n=read(fd,inbuffer,sizeof(inbuffer)-1);
                //写
                if(n>0)
                {
                    inbuffer[n]={0};
                    Log(LogLevel::INFO)<<address.ToString()<<"say#"<<inbuffer;
                    std::string echo_server="server echo# ";
                    echo_server+=inbuffer;

                    write(fd,echo_server.c_str(),echo_server.size());
                }
                else if(n==0)
                {
                    Log(LogLevel::INFO)<<"客户端退出"<<address.ToString();
                    break;
                }
                //读出错
                else
                {
                    Log(LogLevel::WARNING)<<"客户端出错"<<address.ToString();
                    break;
                }

            }
        }
        void Start()
        {
            while(1)
            {
                struct sockaddr_in clientaddr;
                socklen_t len=sizeof(clientaddr);
                int socketfd=accept(ListenSocketfd_,(struct sockaddr*)&clientaddr,&len);
                if(socketfd<0)
                {
                    Log(LogLevel::WARNING)<<"监听失败";
                    continue;
                }
                Log(LogLevel::DEBUG)<<"监听成功";
                // //处理ListenSocketfd_
                // InetAddr clientaddress(clientaddr);
                // ServerIO(socketfd,clientaddress);
                //多进程版本
                pid_t id=fork();
                if(id<0)//fork失败
                {
                    Log(LogLevel::ERROR)<<"fork失败";
                    exit(FORK_ERR);
                }
                else if(id == 0)
                {
                    close(ListenSocketfd_);
                    // 子进程退出
                    if(fork() > 0) exit(0); // A
                    //孙子进程执行——孤儿进程,执行完毕后被OS回收
                    InetAddr clientaddress(clientaddr);
                    ServerIO(socketfd, clientaddress);
                    close(socketfd);
                    exit(0); // 重要
                }
                else
                {   
                    close(socketfd);
                    // 父进程
                    pid_t rid = waitpid(id, nullptr, 0);
                    (void)rid;
                }

                
            }
        }
        ~TCPServer()
        {
            close(ListenSocketfd_);
        }
};

TCP服务器(多线程版本)

        版本一(正常多线程)

#pragma once
#include"Log.h"
#include"InetAddr.h"
#include<unistd.h>
#include<memory>
#include<pthread.h>
using namespace Logger;
enum
{
    SUCCESS=0,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,
    USAGE_ERR
};

//链接队列长度
static const int gblocklist=16;
//默认端口号
static const uint16_t  defaultport=8080;
class TCPServer
{
    private:
        uint16_t port_;//端口号
        //不需要显式包含IP
        int ListenSocketfd_;
    public:
        TCPServer(uint16_t port=defaultport):port_(port)
        {}
        void Init()
        {
            //创建套接字
            ListenSocketfd_=socket(AF_INET,SOCK_STREAM,0);
            if(ListenSocketfd_<0)
            {
                Log(LogLevel::ERROR)<<"服务器创建套接字失败";
                exit(SOCKET_ERR);
            }
            Log(LogLevel::DEBUG)<<"创建套接字成功"<<ListenSocketfd_;
            //初始化网络信息
            struct sockaddr_in local;
            memset(&local,0,sizeof(local));
            local.sin_family=AF_INET;
            local.sin_port=htons(port_);
            local.sin_addr.s_addr=INADDR_ANY;//任意IPbind
            //bind
            int n=bind(ListenSocketfd_,(struct sockaddr*)&local,sizeof(local));
            if(n<0)
            {
                Log(LogLevel::ERROR)<<"绑定失败";
                exit(BIND_ERR);
            }
            Log(LogLevel::DEBUG)<<"绑定成功";
            //tcp是面向连接的,所以必须出于监听状态
            int m=listen(ListenSocketfd_,gblocklist);
            if(m<0)
            {
                Log(LogLevel::ERROR)<<"监听失败";
                exit(LISTEN_ERR);
            }
            Log(LogLevel::DEBUG)<<"监听成功";
        }
        void ServerIO(int fd,InetAddr&address)
        {
            Log(LogLevel::DEBUG)<<"客户端信息"<<address.ToString();
            while(1)
            {
                char inbuffer[1024]={0};
                //读
                ssize_t n=read(fd,inbuffer,sizeof(inbuffer)-1);
                //写
                if(n>0)
                {
                    inbuffer[n]={0};
                    Log(LogLevel::INFO)<<address.ToString()<<"say#"<<inbuffer;
                    std::string echo_server="server echo# ";
                    echo_server+=inbuffer;

                    write(fd,echo_server.c_str(),echo_server.size());
                }
                else if(n==0)
                {
                    Log(LogLevel::INFO)<<"客户端退出"<<address.ToString();
                    break;
                }
                //读出错
                else
                {
                    Log(LogLevel::WARNING)<<"客户端出错"<<address.ToString();
                    break;
                }

            }
        }
        class ThreadData
        {
        public:
            ThreadData( TCPServer *ts, int sockfd,InetAddr addr)
                :_this(ts), _sockfd(sockfd),_addr(addr)
            {}
            ~ThreadData(){
            }
        public:
            TCPServer *_this;
            int _sockfd;
            InetAddr _addr;
        };
        static void*route(void*arg)
        {
            std::unique_ptr<ThreadData> td(static_cast<ThreadData*>(arg));
            pthread_detach(pthread_self());
            td->_this->ServerIO(td->_sockfd,td->_addr);
            return nullptr;
        }
        void Start()
        {
            while(1)
            {
                struct sockaddr_in clientaddr;
                socklen_t len=sizeof(clientaddr);
                int socketfd=accept(ListenSocketfd_,(struct sockaddr*)&clientaddr,&len);
                if(socketfd<0)
                {
                    Log(LogLevel::WARNING)<<"监听失败";
                    continue;
                }
                Log(LogLevel::DEBUG)<<"监听成功";
                pthread_t tid;
                InetAddr clientaddress(clientaddr);
                std::unique_ptr<ThreadData> td=std::make_unique<ThreadData>(this,socketfd,clientaddress);
                pthread_create(&tid,nullptr,route,(void*)td.get());
            }
        }
        ~TCPServer()
        {
            close(ListenSocketfd_);
        }
};

        版本二(线程池)

#pragma once
#include"Log.h"
#include"InetAddr.h"
#include"ThreadPool/ThreadPool.hpp"
#include<unistd.h>
#include<functional>
using namespace Logger;
using namespace ThreadPool;

using task_t =std::function<void()>;
enum
{
    SUCCESS=0,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,
    USAGE_ERR
};

//链接队列长度
static const int gblocklist=16;
//默认端口号
static const uint16_t  defaultport=8080;
class TCPServer
{
    private:
        uint16_t port_;//端口号
        //不需要显式包含IP
        int ListenSocketfd_;
    public:
        TCPServer(uint16_t port=defaultport):port_(port)
        {}
        void Init()
        {
            //创建套接字
            ListenSocketfd_=socket(AF_INET,SOCK_STREAM,0);
            if(ListenSocketfd_<0)
            {
                Log(LogLevel::ERROR)<<"服务器创建套接字失败";
                exit(SOCKET_ERR);
            }
            Log(LogLevel::DEBUG)<<"创建套接字成功"<<ListenSocketfd_;
            //初始化网络信息
            struct sockaddr_in local;
            memset(&local,0,sizeof(local));
            local.sin_family=AF_INET;
            local.sin_port=htons(port_);
            local.sin_addr.s_addr=INADDR_ANY;//任意IPbind
            //bind
            int n=bind(ListenSocketfd_,(struct sockaddr*)&local,sizeof(local));
            if(n<0)
            {
                Log(LogLevel::ERROR)<<"绑定失败";
                exit(BIND_ERR);
            }
            Log(LogLevel::DEBUG)<<"绑定成功";
            //tcp是面向连接的,所以必须出于监听状态
            int m=listen(ListenSocketfd_,gblocklist);
            if(m<0)
            {
                Log(LogLevel::ERROR)<<"监听失败";
                exit(LISTEN_ERR);
            }
            Log(LogLevel::DEBUG)<<"监听成功";
        }
        void ServerIO(int fd,InetAddr address)
        {
            Log(LogLevel::DEBUG)<<"客户端信息"<<address.ToString();
            while(1)
            {
                char inbuffer[1024]={0};
                //读
                ssize_t n=read(fd,inbuffer,sizeof(inbuffer)-1);
                //写
                if(n>0)
                {
                    inbuffer[n]={0};
                    Log(LogLevel::INFO)<<address.ToString()<<"say#"<<inbuffer;
                    std::string echo_server="server echo# ";
                    echo_server+=inbuffer;

                    write(fd,echo_server.c_str(),echo_server.size());
                }
                else if(n==0)
                {
                    Log(LogLevel::INFO)<<"客户端退出"<<address.ToString();
                    break;
                }
                //读出错
                else
                {
                    Log(LogLevel::WARNING)<<"客户端出错"<<address.ToString();
                    break;
                }

            }
        }
        class ThreadData
        {
        public:
            ThreadData( TCPServer *ts, int sockfd,InetAddr addr)
                :_this(ts), _sockfd(sockfd),_addr(addr)
            {}
            ~ThreadData(){
            }
        public:
            TCPServer *_this;
            int _sockfd;
            InetAddr _addr;
        };
        void Start()
        {
            while(1)
            {
                struct sockaddr_in clientaddr;
                socklen_t len=sizeof(clientaddr);
                int socketfd=accept(ListenSocketfd_,(struct sockaddr*)&clientaddr,&len);
                if(socketfd<0)
                {
                    Log(LogLevel::WARNING)<<"监听失败";
                    continue;
                }
                Log(LogLevel::DEBUG)<<"监听成功";
                //处理ListenSocketfd_
                InetAddr clientaddress(clientaddr);
                threadpool<task_t>::Instance()->Enqueue([this,socketfd,&clientaddress](){
                    this->ServerIO(socketfd,clientaddress);
                });
            }
        }
        ~TCPServer()
        {
            close(ListenSocketfd_);
        }
};

        线程池相关

        Mutex.hpp

#pragma once

#include <iostream>
#include <string>
#include <pthread.h>
#include "../Log.h"
namespace LockModule
{
    // 对锁进行封装,可以独立使用
    class Mutex
    {
    public:
        // 删除不需要的拷贝和赋值
        Mutex(const Mutex &) = delete;
        const Mutex &operator=(const Mutex &) = delete;
        
        // 构造函数
        Mutex()
        {
            int n = pthread_mutex_init(&_mutex, nullptr);
            (void)n;
        }
        
        // 加锁
        void Lock()
        {
            int n = pthread_mutex_lock(&_mutex);
            (void)n;
        }
        
        // 解锁
        void Unlock()
        {
            int n = pthread_mutex_unlock(&_mutex);
            (void)n;
        }
        
        // 获取原始指针
        pthread_mutex_t *GetMutexOriginal()
        {
            return &_mutex;
        }
        
        // 析构函数
        ~Mutex()
        {
            int n = pthread_mutex_destroy(&_mutex);
            (void)n;
        }
        
    private:
        pthread_mutex_t _mutex;
    };
    
    // 采用RAII风格,进行锁管理
    class LockGuard
    {
    public:
        // 构造函数,创建时自动加锁
        LockGuard(Mutex &mutex) : _mutex(mutex)
        {
            _mutex.Lock();
        }
        
        // 析构函数,自动解锁
        ~LockGuard()
        {
            _mutex.Unlock();
        }
        
    private:
        Mutex &_mutex;  // 引用被管理的互斥锁
    };
}

        Cond.hpp

#pragma once

#include <iostream>
#include <string>
#include <pthread.h>
#include "Mutex.hpp"

namespace CondModule
{
    class Cond
    {
    public:
        Cond()
        {
            pthread_cond_init(&_cond, nullptr);
        }
        void Wait(LockModule::Mutex &mutex)
        {
            int n = pthread_cond_wait(&_cond, mutex.GetMutexOriginal());
            (void)n;
        }
        void Signal()
        {
            int n = pthread_cond_signal(&_cond);
            (void)n;
        }
        void Broadcast()
        {
            int n = pthread_cond_broadcast(&_cond);
            (void)n;
        }
        ~Cond()
        {
            pthread_cond_destroy(&_cond);
        }
    private:
        pthread_cond_t _cond;
    };
}

        Thread.hpp

#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>
#include "../Log.h"

namespace ThreadPool
{
    using namespace Logger;
    static int gnumber = 1;
    using callback_t = std::function<void()>;

    enum class TSTATUS
    {
        THREAD_NEW,
        THREAD_RUNNING,
        THREAD_STOP
    };

    std::string Status2String(TSTATUS s)
    {
        switch (s)
        {
        case TSTATUS::THREAD_NEW:
            return "THREAD_NEW";
        case TSTATUS::THREAD_RUNNING:
            return "THREAD_RUNNING";
        case TSTATUS::THREAD_STOP:
            return "THREAD_STOP";
        default:
            return "UNKNOWN";
        }
    }

    std::string IsJoined(bool joinable)
    {
        return joinable ? "true" : "false";
    }

    class Thread
    {
    private:
        void ToRunning()
        {
            _status = TSTATUS::THREAD_RUNNING;
        }
        void ToStop()
        {
            _status = TSTATUS::THREAD_STOP;
        }
        static void *ThreadRoutine(void *args)
        {
            Thread *self = static_cast<Thread *>(args);
            pthread_setname_np(self->_tid, self->_name.c_str());
            self->_cb();
            self->ToStop();
            return nullptr;
        }

    public:
        Thread(callback_t cb)
            : _tid(-1), _status(TSTATUS::THREAD_NEW), _joinable(true), _cb(cb), _result(nullptr)
        {
            _name = "New-Thread-" + std::to_string(gnumber++);
        }
        bool Start()
        {
            int n = pthread_create(&_tid, nullptr, ThreadRoutine, this);
            if (n != 0)
                return false;

            ToRunning();
            return true;
        }
        void Join()
        {
            if (_joinable)
            {
                int n = pthread_join(_tid, &_result);
                if (n != 0)
                {
                    std::cerr << "join error: " << n << std::endl;
                    return;
                }
                (void)_result;
                _status = TSTATUS::THREAD_STOP;
                Log(LogLevel::DEBUG) << _name << " join success";
            }
            else
            {
                std::cerr << "error, thread join status: " << IsJoined(_joinable) << std::endl;
            }
        }
        // 暂停
        // void Stop() // restart()
        // {
        //     // 让线程暂停
        // }
        void Die()
        {
            if (_status == TSTATUS::THREAD_RUNNING)
            {
                pthread_cancel(_tid);
                _status = TSTATUS::THREAD_STOP;
            }
        }
        void Detach()
        {
            if (_status == TSTATUS::THREAD_RUNNING && _joinable)
            {
                pthread_detach(_tid);
                _joinable = false;
            }
            else
            {
                std::cerr << "detach " << _name << " failed" << std::endl;
            }
        }
        void PrintInfo()
        {
            std::cout << "thread name : " << _name << std::endl;
            std::cout << "thread _tid : " << _tid << std::endl;
            std::cout << "thread _status : " << Status2String(_status) << std::endl;
            std::cout << "thread _joinable : " << IsJoined(_joinable) << std::endl;
        }

        ~Thread()
        {
        }

    private:
        std::string _name;
        pthread_t _tid;
        TSTATUS _status;
        bool _joinable;
        // 线程要有自己的任务处理,即回调函数
        callback_t _cb;

        // 线程退出信息
        void *_result;
    };
}

        ThreadPool.hpp

#pragma once 
#include "../Log.h"
#include "Thread.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include <iostream>
#include <memory>
#include <vector>
#include <queue>

namespace ThreadPool
{   
    using namespace Logger;
    const int default_thread_num = 5;

    // 测试用函数
    void test()
    {
        while(1)
        {
            Log(LogLevel::DEBUG) << "我是一个新线程,我再运行ing....";
            sleep(1);
        }
    }

    template<typename T>
    class threadpool
    {
    private:
        void HandlerTask()
        {
            char name[128];
            pthread_getname_np(pthread_self(), name, sizeof(name));
            while (true)
            {
                T task;
                {
                    LockModule::LockGuard lockguard(_mutex);   // 使用 RAII 管理锁,避免手动解锁
                    while (_tasks.empty() && _isRunning)
                    {
                        _Slaves_Sleep_Count++;
                        _cond.Wait(_mutex);
                        _Slaves_Sleep_Count--;
                    }

                    if (!_isRunning && _tasks.empty())
                    {
                        // 退出前解锁(LockGuard 析构时会自动解锁,无需手动)
                        break;
                    }

                    task = _tasks.front();
                    _tasks.pop();
                }

                Log(LogLevel::INFO) << name << "处理任务:";
                task();
                // Log(LogLevel::DEBUG) << task.Result();
            }
            Log(LogLevel::INFO) << name << " quit...";
        }

        // 构造函数私有,禁止外部直接创建
        threadpool(int slave_num = default_thread_num)
            : _isRunning(false), _Slavernum(slave_num), _Slaves_Sleep_Count(0) 
        {
            _Slaves.reserve(_Slavernum);
            auto fun = std::bind(&threadpool::HandlerTask, this);
            for (size_t i = 0; i < _Slavernum; ++i)
            {
                _Slaves.emplace_back(fun);
            }
        }

        // 禁止拷贝和赋值(修正:类名应为 threadpool)
        threadpool(const threadpool &) = delete;
        threadpool &operator=(const threadpool &) = delete;

    public:
        static threadpool<T>* Instance() 
        {
            if (_instance == nullptr) 
            {
                LockModule::LockGuard lockguard(_lock);
                if (_instance == nullptr) 
                {
                    // 使用 reset(new ...) 替代 std::make_unique,因为构造函数私有
                    _instance.reset(new threadpool<T>());
                    _instance->Init();               // 调用 Init 而非 Start
                    Log(LogLevel::INFO) << "第一次使用线程池,创建线程池对象";
                }
            }
            return _instance.get();
        }

        ~threadpool()
        {
        }

        void Init()
        {
            if (_isRunning)
            {
                Log(LogLevel::WARNING) << "线程池已经运行";
                return;
            }
            _isRunning = true;
            for (auto &slave : _Slaves)
            {
                slave.Start();
            }
        }

        void Stop()
        {
            if (!_isRunning)
            {
                Log(LogLevel::WARNING) << "线程池未运行";
                return;
            }
            // 停止线程池
            _mutex.Lock();
            _isRunning = false;
            if (_Slaves_Sleep_Count > 0)
                _cond.Broadcast();
            _mutex.Unlock();

            // 等待所有线程退出(可选)
            // Wait();
        }

        void Wait()
        {
            for (auto &slave : _Slaves)
            {
                slave.Join();
            }
        }

        void Enqueue(T in)
        {
            _mutex.Lock();
            _tasks.push(in);
            if (_Slaves_Sleep_Count > 0)
                _cond.Signal();
            _mutex.Unlock();
        }

    private:
        bool _isRunning;
        size_t _Slavernum;
        std::vector<Thread> _Slaves;
        std::queue<T> _tasks;                // 临界资源
        LockModule::Mutex _mutex;
        CondModule::Cond _cond;
        size_t _Slaves_Sleep_Count;          // 休眠的线程数量

        // 单例模式静态成员
        static std::unique_ptr<threadpool<T>> _instance;
        static LockModule::Mutex _lock;       // 类型改为 LockModule::Mutex
    };

    // 静态成员定义(必须放在命名空间内)
    template<typename T>
    std::unique_ptr<threadpool<T>> threadpool<T>::_instance = nullptr;

    template<typename T>
    LockModule::Mutex threadpool<T>::_lock;

} // namespace ThreadPool

        

        本期内容到这里先结束了,后续我们还会学习更多的网络相关的内容,敬请期待

封面图如下:

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐