从零开始:C++ 多线程 TCP 服务器实战(续篇)

文章目录

  • 引言
  • [1. 核心设计:多线程模型的原理与优势](#1. 核心设计:多线程模型的原理与优势)
    • [1.1 多线程模型设计](#1.1 多线程模型设计)
    • [1.2 关键问题与解决](#1.2 关键问题与解决)
  • [2. Server 端改造:多线程版本实现](#2. Server 端改造:多线程版本实现)
    • [2.1 头文件与类结构修改(TcpServer.hpp)](#2.1 头文件与类结构修改(TcpServer.hpp))
    • [2.2 Init 函数扩展:信号忽略(避免进程终止)](#2.2 Init 函数扩展:信号忽略(避免进程终止))
    • [2.3 Start 函数改造:多线程核心逻辑](#2.3 Start 函数改造:多线程核心逻辑)
    • [2.4 线程入口与客户端通信实现](#2.4 线程入口与客户端通信实现)
      • [2.4.1 静态线程入口函数(ThreadRoutine)](#2.4.1 静态线程入口函数(ThreadRoutine))
      • [2.4.2 客户端通信处理(HandleClient)](#2.4.2 客户端通信处理(HandleClient))
    • [2.5 服务器入口(TcpServer.cc)](#2.5 服务器入口(TcpServer.cc))
  • [3. 客户端兼容性:无需修改](#3. 客户端兼容性:无需修改)
  • [4. 编译与多客户端测试](#4. 编译与多客户端测试)
    • [4.1 Makefile 改造(链接线程库)](#4.1 Makefile 改造(链接线程库))
    • [4.2 多客户端并发测试步骤](#4.2 多客户端并发测试步骤)
      • [步骤 1:启动多线程服务器](#步骤 1:启动多线程服务器)
      • [步骤 2:启动多个客户端(模拟并发)](#步骤 2:启动多个客户端(模拟并发))
      • [步骤 3:客户端发送数据,验证并发](#步骤 3:客户端发送数据,验证并发)
      • [步骤 4:查看服务器日志](#步骤 4:查看服务器日志)
  • [5. 多线程方案的优缺点](#5. 多线程方案的优缺点)
    • [5.1 优势(对比多进程)](#5.1 优势(对比多进程))
    • [5.2 局限(需注意的问题)](#5.2 局限(需注意的问题))
  • [6. 后续扩展方向](#6. 后续扩展方向)
    • [6.1 线程池优化(解决线程数量上限问题)](#6.1 线程池优化(解决线程数量上限问题))
    • [6.2 线程安全增强(共享资源保护)](#6.2 线程安全增强(共享资源保护))
    • [6.3 连接超时管理(避免死连接)](#6.3 连接超时管理(避免死连接))
    • [6.4 信号精细化处理(保障主线程稳定)](#6.4 信号精细化处理(保障主线程稳定))
  • [7. 总结](#7. 总结)

引言

上一篇教程中,我们通过多进程模型解决了单客户端 TCP 服务器的阻塞问题 ------ 主进程负责接收连接,子进程(孙子进程)处理客户端通信,利用 init 进程自动回收孤儿进程避免僵尸问题。但多进程方案存在天然局限:进程间地址空间独立,内存拷贝开销大,进程间通信(IPC)需依赖管道、共享内存等复杂机制,且创建 / 销毁进程的系统开销远高于线程。

为进一步优化资源占用与通信效率,本篇将基于原有 TcpServer 类,改造为多线程 TCP 服务器 :采用「主线程监听连接 + 子线程处理客户端」的模型,利用线程共享进程内存的特性降低开销,同时通过线程分离(pthread_detach)避免僵尸线程。教程将延续模块化设计思想,保持与前两篇一致的码风,完整覆盖多线程改造的核心细节与线程安全基础。

1. 核心设计:多线程模型的原理与优势

多线程方案的核心是线程级并发------ 所有线程共享同一进程的地址空间(如代码段、数据段、文件描述符表),仅拥有独立的栈空间和线程上下文,因此创建 / 销毁开销仅为多进程的 1/10~1/100,且线程间通信无需额外 IPC 机制。

1.1 多线程模型设计

基础版多线程服务器采用 "一对一连接" 模型,流程如下:

  1. 主线程 :仅负责核心流程 ------ 创建监听套接字、绑定端口、监听连接,通过 accept 阻塞等待客户端连接;
  2. 子线程 :主线程每接收一个客户端连接(获得 client_fd),立即创建一个子线程,将 client_fd 与客户端信息(IP / 端口)传递给子线程;
  3. 任务处理 :子线程独立处理与客户端的收发数据(recv/send),通信结束后关闭 client_fd 并自动释放线程资源;
  4. 资源回收 :通过 pthread_detach 将子线程设为 "分离态",线程退出后系统自动回收其栈空间与上下文,避免 "僵尸线程"(类似多进程的 init 回收机制)。

1.2 关键问题与解决

  • 线程安全 :基础版中,每个子线程仅操作独立的 client_fd,无共享资源(如全局变量、类成员变量),因此无需互斥锁;若后续扩展连接计数、共享配置等,需通过 pthread_mutex_t 实现同步。
  • 僵尸线程 :若子线程未分离且主线程未调用 pthread_join,线程退出后资源无法回收,形成 "僵尸线程";通过 pthread_detach 或创建线程时设置 PTHREAD_CREATE_DETACHED 属性可解决。
  • 线程函数兼容性pthread_create 要求线程入口函数为 void* (*)(void*) 类型,需通过静态函数封装类的成员方法(非静态成员函数依赖 this 指针,无法直接作为线程入口)。

2. Server 端改造:多线程版本实现

改造基于前两篇的 TcpServer 类,核心新增线程入口函数、客户端通信封装函数,并修改 Start 函数的连接处理逻辑,线程相关操作依赖 POSIX 线程库(pthread)。

2.1 头文件与类结构修改(TcpServer.hpp)

首先补充线程相关头文件,新增线程入口函数(静态)、客户端处理函数,并定义线程参数结构体(传递 this 指针与客户端信息):

cpp 复制代码
// 补充线程相关头文件(POSIX 线程库)
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <csignal>
#include <unistd.h>
#include <functional>
#include <iostream>
#include <string>
#include <cstring>

// 线程参数结构体:传递给线程入口函数的参数(包含this指针与客户端信息)
typedef struct {
    class TcpServer* server;  // 指向TcpServer实例的指针
    int client_fd;            // 客户端通信套接字
    std::string client_ip;    // 客户端IP(点分十进制)
    uint16_t client_port;     // 客户端端口
} ThreadArg;

class TcpServer {
public:
    TcpServer(uint16_t port, func_t handler);
    ~TcpServer();
    bool Init();    // 初始化:创建套接字、绑定、监听(新增信号忽略)
    void Start();   // 启动:主线程accept,子线程处理客户端
    void Stop();    // 停止服务器(与前两篇一致)

private:
    // 新增1:静态线程入口函数(适配pthread_create的函数签名)
    static void* ThreadRoutine(void* arg);
    // 新增2:客户端通信处理(子线程实际执行的逻辑)
    void HandleClient(int client_fd, const std::string& client_ip, uint16_t client_port);

    int _listen_fd;         // 监听套接字(与前两篇一致)
    uint16_t _listen_port;  // 服务器监听端口(与前两篇一致)
    bool _is_running;       // 服务器运行状态(与前两篇一致)
    func_t _data_handler;   // 数据处理回调函数(与前两篇一致)
};

关键说明:

  • ThreadArg 结构体:解决线程入口函数无法直接访问类成员的问题,通过 server 指针间接调用 HandleClient
  • 静态线程入口 ThreadRoutine:必须为静态成员函数(无 this 指针),否则无法匹配 pthread_create 要求的函数签名。

2.2 Init 函数扩展:信号忽略(避免进程终止)

TCP 通信中,若客户端意外断开连接(如断网),服务器继续调用 send 会触发 SIGPIPE 信号,默认行为是终止整个进程。因此在 Init 函数末尾新增信号忽略逻辑,保障服务器稳定性:

cpp 复制代码
bool TcpServer::Init() {
    // 1. 创建 TCP 套接字(与前两篇一致)
    _listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (_listen_fd == -1) {
        perror("socket 创建失败!");
        return false;
    }
    std::cout << "套接字创建成功,listen_fd: " << _listen_fd << std::endl;

    // 2. 填充服务器地址结构(与前两篇一致)
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;         // IPv4 协议
    server_addr.sin_port = htons(_listen_port); // 端口:本地→网络字节序
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有网卡

    // 3. 绑定套接字与地址(与前两篇一致)
    int bind_ret = bind(_listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (bind_ret == -1) {
        perror("绑定失败");
        close(_listen_fd);
        _listen_fd = -1;
        return false;
    }
    std::cout << "绑定成功,成功监听端口:" << _listen_port << std::endl;

    // 4. 监听连接(与前两篇一致)
    int listen_ret = listen(_listen_fd, 5); // backlog=5(未完成连接队列长度)
    if (listen_ret == -1) {
        perror("listen 失败");
        close(_listen_fd);
        _listen_fd = -1;
        return false;
    }
    std::cout << "监听中,等待客户端连接..." << std::endl;

    // 新增:5. 忽略 SIGPIPE 信号(避免客户端断开导致服务器崩溃)
    signal(SIGPIPE, SIG_IGN);

    _is_running = true;
    return true;
}

2.3 Start 函数改造:多线程核心逻辑

Start 函数是多线程改造的核心:主线程循环调用 accept 接收连接,每获得一个客户端连接,创建子线程并传递 ThreadArg 参数,子线程负责后续通信,主线程立即返回继续监听新连接:

cpp 复制代码
void TcpServer::Start() {
    if (!_is_running || _listen_fd == -1) {
        perror("服务器未初始化,无法启动");
        return;
    }

    // 主线程循环:持续接收客户端连接
    while (_is_running) {
        struct sockaddr_in client_addr;
        socklen_t client_addr_len = sizeof(client_addr);

        // 1. 主线程:阻塞接收客户端连接
        int client_fd = accept(_listen_fd, (struct sockaddr*)&client_addr, &client_addr_len);
        if (client_fd == -1) {
            perror("accept 失败!");
            continue;
        }

        // 解析客户端地址(网络字节序→本地字节序)
        std::string client_ip = inet_ntoa(client_addr.sin_addr);
        uint16_t client_port = ntohs(client_addr.sin_port);
        std::cout << "\n客户端连接成功:[" << client_ip << ":" << client_port << "],client_fd: " << client_fd << std::endl;

        // 2. 准备线程参数(动态分配,避免栈内存释放问题)
        ThreadArg* thread_arg = new ThreadArg();
        thread_arg->server = this;          // 传递当前TcpServer实例
        thread_arg->client_fd = client_fd;  // 客户端通信套接字
        thread_arg->client_ip = client_ip;  // 客户端IP
        thread_arg->client_port = client_port; // 客户端端口

        // 3. 创建子线程:处理该客户端的通信
        pthread_t tid;
        int pthread_ret = pthread_create(&tid, nullptr, ThreadRoutine, (void*)thread_arg);
        if (pthread_ret != 0) {
            perror("pthread_create 失败!");
            // 创建线程失败:释放参数内存,关闭客户端套接字
            delete thread_arg;
            close(client_fd);
            continue;
        }

        // 4. 分离线程:子线程结束后自动释放资源(无需主线程pthread_join)
        pthread_detach(tid);
        std::cout << "子线程创建成功(tid: " << tid << "),负责处理客户端[" << client_ip << ":" << client_port << "]" << std::endl;
    }
}

关键细节:

  • 线程参数动态分配:thread_argnew 分配在堆上,避免主线程栈内存被释放后,子线程访问非法内存;
  • pthread_detach:将子线程设为分离态,线程退出时系统自动回收其资源(栈、寄存器等),无需主线程调用 pthread_join 阻塞等待;
  • 错误回滚:若线程创建失败,需手动释放 thread_arg 并关闭 client_fd,避免内存泄漏与文件描述符泄漏。

2.4 线程入口与客户端通信实现

2.4.1 静态线程入口函数(ThreadRoutine)

线程启动后,首先进入静态入口函数,通过 ThreadArg 中的 server 指针调用非静态的 HandleClient 函数,完成业务逻辑转发:

cpp 复制代码
void* TcpServer::ThreadRoutine(void* arg) {
    // 1. 解析线程参数
    ThreadArg* thread_arg = static_cast<ThreadArg*>(arg);
    if (thread_arg == nullptr) {
        std::cerr << "线程参数为空,退出线程" << std::endl;
        return nullptr;
    }

    // 2. 调用客户端处理函数(通过server指针访问非静态成员)
    thread_arg->server->HandleClient(
        thread_arg->client_fd,
        thread_arg->client_ip,
        thread_arg->client_port
    );

    // 3. 释放线程参数内存(避免堆内存泄漏)
    delete thread_arg;
    return nullptr;
}

2.4.2 客户端通信处理(HandleClient)

将原单客户端版本的 "收发数据" 逻辑抽离为 HandleClient 函数,由子线程执行,逻辑与前两篇一致,仅需确保通信结束后关闭 client_fd

cpp 复制代码
void TcpServer::HandleClient(int client_fd, const std::string& client_ip, uint16_t client_port) {
    char recv_buf[1024] = {0}; // 接收缓冲区
    std::cout << "子线程(tid: " << pthread_self() << ")开始处理客户端[" << client_ip << ":" << client_port << "]" << std::endl;

    // 循环收发数据(与客户端保持通信)
    while (true) {
        // 1. 接收客户端数据
        ssize_t recv_len = recv(client_fd, recv_buf, sizeof(recv_buf) - 1, 0);
        if (recv_len == -1) {
            perror("recv 失败");
            break; // 接收错误,退出通信循环
        } else if (recv_len == 0) {
            std::cout << "客户端[" << client_ip << ":" << client_port << "]主动断开连接" << std::endl;
            break; // 客户端关闭连接,退出循环
        }

        // 2. 处理接收的数据(调用自定义回调函数)
        recv_buf[recv_len] = '\0'; // 添加字符串结束符
        std::cout << "子线程(tid: " << pthread_self() << ")收到[" << client_ip << ":" << client_port << "]的数据:" << recv_buf << std::endl;
        std::string response = _data_handler(recv_buf); // 回调函数处理数据

        // 3. 向客户端发送响应
        ssize_t send_len = send(client_fd, response.c_str(), response.size(), 0);
        if (send_len == -1) {
            perror("send 失败");
            break; // 发送错误,退出循环
        }
        std::cout << "子线程(tid: " << pthread_self() << ")向[" << client_ip << ":" << client_port << "]发送响应:" << response << std::endl;

        // 4. 清空接收缓冲区
        memset(recv_buf, 0, sizeof(recv_buf));
    }

    // 5. 通信结束:关闭客户端套接字
    close(client_fd);
    std::cout << "子线程(tid: " << pthread_self() << ")处理完毕,关闭客户端[" << client_ip << ":" << client_port << "]连接" << std::endl;
}

2.5 服务器入口(TcpServer.cc

与前两篇一致,复用 DefaultDataHandler 回调函数,通过命令行参数指定监听端口,核心逻辑无修改:

cpp 复制代码
#include <memory>
#include <iostream>
#include <string>
#include "TcpServer.hpp"

// 打印用法提示
void Usage(std::string proc) {
    std::cerr << "Usage: " << proc << " <listen_port>" << std::endl;
    std::cerr << "示例:" << proc << " 8080" << std::endl;
}

// 自定义数据处理回调:给客户端消息添加前缀
std::string DefaultDataHandler(const std::string& client_data) {
    return "TCP Thread Server Response: " + client_data;
}

int main(int argc, char* argv[]) {
    // 检查命令行参数数量
    if (argc != 2) {
        Usage(argv[0]);
        return 1;
    }

    // 解析监听端口(1024~65535 为有效端口)
    uint16_t listen_port = std::stoi(argv[1]);
    if (listen_port < 1024 || listen_port > 65535) {
        std::cerr << "端口号无效!需在 1024~65535 之间" << std::endl;
        return 2;
    }

    // 创建 TCP 服务器实例(智能指针自动释放资源)
    std::unique_ptr<TcpServer> tcp_server = 
        std::make_unique<TcpServer>(listen_port, DefaultDataHandler);

    // 初始化并启动服务器
    if (!tcp_server->Init()) {
        std::cerr << "服务器初始化失败,退出程序" << std::endl;
        return 3;
    }
    tcp_server->Start();

    return 0;
}

3. 客户端兼容性:无需修改

多线程服务器仅改变 "服务器端并发模型",客户端与服务器的通信协议(TCP 三次握手、收发数据格式)完全不变。因此前两篇实现的 TcpClient.cc 可直接复用,无需任何修改。

4. 编译与多客户端测试

4.1 Makefile 改造(链接线程库)

POSIX 线程库(pthread)并非 C++ 标准库,编译时需通过 -lpthread 显式链接线程库。

bash 复制代码
.PHONY: all clean

# 编译目标:多线程服务器 + 客户端
all: tcpserver tcpclient

# 编译多线程服务器:需链接pthread库
tcpserver: TcpServer.cc
	g++ -o $@ $^ -std=c++14 -lpthread  # -lpthread:链接线程库

# 编译客户端:与前两篇一致
tcpclient: TcpClient.cc
	g++ -o $@ $^ -std=c++14

# 清理编译产物
clean:
	rm -f tcpserver tcpclient

4.2 多客户端并发测试步骤

步骤 1:启动多线程服务器

bash 复制代码
./tcpserver 8888

服务器输出(初始化成功):

bash 复制代码
套接字创建成功,listen_fd: 3
绑定成功,成功监听端口:8888
监听中,等待客户端连接...

步骤 2:启动多个客户端(模拟并发)

打开 3 个终端,分别启动客户端并连接服务器(127.0.0.1 为本地回环地址):

bash 复制代码
# 终端1:客户端1
./tcpclient 127.0.0.1 8888

# 终端2:客户端2
./tcpclient 127.0.0.1 8888

# 终端3:客户端3
./tcpclient 127.0.0.1 8888

客户端输出(连接成功):

bash 复制代码
客户端创建套接字成功,client_fd: 3
已成功连接到服务器[127.0.0.1:8888]

请输入发送给服务器的数据(输入"exit"退出):

步骤 3:客户端发送数据,验证并发

  • 客户端 1 输入:Hello Thread Server! This is Zkp 1
    客户端 1 输出:

    bash 复制代码
    已发送数据Hello Thread Server! This is Zkp 1(字节数:34)
    收到服务器响应:TCP Server Response: Hello Thread Server! This is Zkp 1
    
    请输入发送给服务器的数据(输入"exit"退出)
  • 客户端 2 输入:Hello Thread Server! This is Zkp 2
    客户端 2 输出:

    bash 复制代码
    已发送数据Hello Thread Server! This is Zkp 2(字节数:34)
    收到服务器响应:TCP Server Response: Hello Thread Server! This is Zkp 2
    
    请输入发送给服务器的数据(输入"exit"退出)
  • 客户端 3 输入:Hello Thread Server! This is Zkp 3
    客户端 3 输出:

    bash 复制代码
    已发送数据Hello Thread Server! This is Zkp 3(字节数:34)
    收到服务器响应:TCP Server Response: Hello Thread Server! This is Zkp 3
    
    请输入发送给服务器的数据(输入"exit"退出)

步骤 4:查看服务器日志

服务器输出(多线程并发处理):

bash 复制代码
客户端连接成功:[127.0.0.1:47118],client_fd: 4
子线程创建成功(tid:139722238830144),负责处理客户端[127.0.0.1:47118]
子线程(tid: 139722238830144)开始处理客户端[127.0.0.1:47118]

客户端连接成功:[127.0.0.1:56970],client_fd: 5
子线程创建成功(tid:139722230437440),负责处理客户端[127.0.0.1:56970]
子线程(tid: 139722230437440)开始处理客户端[127.0.0.1:56970]

客户端连接成功:[127.0.0.1:51488],client_fd: 6
子线程创建成功(tid:139722222044736),负责处理客户端[127.0.0.1:51488]
子线程(tid: 139722222044736)开始处理客户端[127.0.0.1:51488]
子线程(tid: 139722238830144)收到[127.0.0.1:47118]的数据:Hello Thread Server! This is Zkp 1
子线程(tid: 139722238830144)向[127.0.0.1:47118]发送响应:TCP Server Response: Hello Thread Server! This is Zkp 1
子线程(tid: 139722230437440)收到[127.0.0.1:56970]的数据:Hello Thread Server! This is Zkp 2
子线程(tid: 139722230437440)向[127.0.0.1:56970]发送响应:TCP Server Response: Hello Thread Server! This is Zkp 2
子线程(tid: 139722222044736)收到[127.0.0.1:51488]的数据:Hello Thread Server! This is Zkp 3
子线程(tid: 139722222044736)向[127.0.0.1:51488]发送响应:TCP Server Response: Hello Thread Server! This is Zkp 3

5. 多线程方案的优缺点

5.1 优势(对比多进程)

  1. 资源开销低:线程共享进程地址空间,无需拷贝代码段、数据段,创建 / 销毁开销仅为多进程的 1/10~1/100;
  2. 线程间通信便捷:可直接访问进程内的共享变量(如全局配置、连接计数器),无需依赖管道、共享内存等复杂 IPC 机制;
  3. 上下文切换快:线程仅需切换栈和寄存器状态,切换开销远低于进程(进程需切换页表、地址空间)。

5.2 局限(需注意的问题)

  1. 线程安全风险:若多个线程修改同一共享资源(如连接数计数器),未加互斥锁会导致 "竞态条件"(数据不一致);
  2. 线程数量上限 :每个线程默认占用 8MB 栈空间(Linux),若客户端数量过多(如上万),会耗尽进程内存,导致 pthread_create 失败;
  3. 进程级崩溃风险:线程共享进程资源,一个线程触发致命错误(如访问空指针)会导致整个进程崩溃,隔离性弱于多进程。

6. 后续扩展方向

基础版多线程服务器可针对 "高并发" 与 "稳定性" 进一步优化,核心扩展方向如下:

6.1 线程池优化(解决线程数量上限问题)

  • 问题:基础版 "一个连接一个线程" 的模型,在客户端数量激增时会频繁创建 / 销毁线程,且线程过多导致内存耗尽;
  • 方案:预先创建固定数量的线程(如 10 个),主线程 accept 后将 client_fd 加入任务队列,线程池中的线程从队列中取任务处理;
    关键技术:任务队列需通过 "互斥锁(pthread_mutex_t)+ 条件变量(pthread_cond_t)" 实现线程安全,避免多个线程同时操作队列。

6.2 线程安全增强(共享资源保护)

场景:若需统计当前在线客户端数量,多个线程会同时修改 online_count 变量;

实现:用互斥锁包裹共享资源的读写操作,确保同一时间仅一个线程访问:

cpp 复制代码
运行
// 类中新增互斥锁成员
pthread_mutex_t _mutex;

// 初始化互斥锁(在Init函数中)
pthread_mutex_init(&_mutex, nullptr);

// 访问共享资源时加锁/解锁
pthread_mutex_lock(&_mutex);
_online_count++; // 共享资源修改
pthread_mutex_unlock(&_mutex);

6.3 连接超时管理(避免死连接)

  • 问题:客户端异常断网后,服务器端连接会成为 "死连接",长期占用 client_fd 与线程资源;
  • 方案:
    1. recv 设置超时(通过 setsockopt 函数设置 SO_RCVTIMEO);
    2. 实现心跳机制:服务器定期向客户端发送心跳包,若超时未收到响应则关闭连接。

6.4 信号精细化处理(保障主线程稳定)

  • 场景:主线程 accept 时若收到 SIGINT(Ctrl+C),需优雅关闭服务器(关闭 _listen_fd、等待子线程处理完毕);
  • 方案:在 Init 函数中注册 SIGINT 信号处理函数,设置 _is_running = false,并关闭 _listen_fd,让主线程退出 accept 循环。

7. 总结

本篇教程基于前两篇的 TCP 服务器基础,实现了多线程版本,核心知识点回顾:

  1. 多线程模型:主线程 accept + 子线程处理客户端,通过 pthread_detach 避免僵尸线程;
  2. 关键技术:静态线程入口函数、线程参数传递、SIGPIPE 信号忽略、线程库链接(-lpthread);
  3. 对比优势:多线程比多进程开销小、通信便捷,适合中高并发场景;
  4. 扩展核心:线程池解决线程数量上限,互斥锁保障线程安全。

通过基础版与扩展方向的结合,可逐步构建出工业级的高并发 TCP 服务器(如简易聊天系统、文件传输服务)。建议先掌握基础版代码的调试与运行,再逐步尝试线程池、线程安全等高级特性。

相关推荐
利刃大大3 小时前
【高并发服务器】十、Connection连接管理模块设计与实现
服务器·c++·高并发·项目
捷米研发三部4 小时前
Profinet 转 TCP/IP 协议转换网关:打破 PLC 与打标卡协议壁垒的工业通讯利器
网络·网络协议·tcp/ip
微露清风5 小时前
系统性学习C++-第八讲-vector类
java·c++·学习
weixin_445251835 小时前
7sch C++ <B> weak_ptr circular reference 1/99
c++
给大佬递杯卡布奇诺6 小时前
FFmpeg 基本数据结构 AVInputFormat 分析
数据结构·c++·ffmpeg·音视频
给大佬递杯卡布奇诺6 小时前
FFmpeg 基本数据结构 AVCodecContext分析
数据结构·c++·ffmpeg·音视频
hd51cc6 小时前
C++ 类的学习(四) 继承
开发语言·c++·学习
紫荆鱼6 小时前
设计模式-命令模式(Command)
c++·后端·设计模式·命令模式
盛世隐者7 小时前
【Linux】定制Linux系统
linux·运维·服务器