CppGuide社区 CppGuide社区
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
  • C++语言面试问题集锦
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • 🔥C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 🔥从零用C语言写一个Redis
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 🔥使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
  • C++语言面试问题集锦
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • 🔥C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 🔥从零用C语言写一个Redis
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 🔥使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
  • 第1章高频C++11重难点知识解析

  • 第2章Linux GDB高级调试指南

  • 第3章C++多线程编程从入门到进阶

  • 第4章C++网络编程重难点解析

  • 第5章网络通信故障排查常用命令

  • 第6章高性能网络通信协议设计精要

  • 第7章高性能服务结构设计

    • 7.1 网络通信组件的效率问题
    • 7.2 最原始的服务器结构
    • 7.3 一个连接一个线程模型
    • 7.4 Reactor 模式
    • 7.5 one thread one loop 思想
    • 7.6 收数据与发数据的正确姿势
    • 7.7 发送/接收缓冲区设计要点
    • 7.8 网络库的分层设计
    • 7.9 后端服务中的定时器设计
    • 7.10 业务数据处理一定要单独开线程吗
    • 7.11 侵入式程序结构与非侵入式程序结构
    • 7.12 带有网络通信模块的服务器的经典结构
  • 第8章Redis 网络通信模块源码分析

  • 第9章后端服务重要模块设计探索

  • C++后端开发进阶
  • 第7章高性能服务结构设计
zhangxf
2023-04-05

7.3 一个连接一个线程模型

正因为最原始的服务器结构不支持并发,随着计算机引入多线程模型后,软件开发者想出了另外一种服务器结构,即给每一个客户端连接创建一个线程,这样多个线程就可以并行执行了,可以同时为多个客户端服务。

示例代码如下:

//侦听线程
UINT WINAPI MyMainThread(LPVOID lPvoid)
{
    LOG_NORMAL("Start MyMainThread successfully, ThreadID = %u.", ::GetCurrentThreadId());
    
    UINT		nThreadID = 0;
    SOCKET		sListenSocket = (SOCKET)lPvoid;
    SOCKET		sClientSocket = 0;
    while (1)
    {
        //等待客户连接
        sockaddr_in clientAddr = { 0 };
        int clientAddrLength = sizeof(clientAddr);
        if ((sClientSocket = accept(sListenSocket, (struct sockaddr*)&clientAddr, &clientAddrLength)) == INVALID_SOCKET)
            break;

        LOG_NORMAL("New client connected: %s:%d", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));

        //启动客户签到线程
        _beginthreadex(NULL, 0, MyChildThread, (LPVOID)sClientSocket, 0, &nThreadID);
    }

    closesocket(sListenSocket);
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//接收连接线程
UINT WINAPI MyChildThread(LPVOID lPvoid)
{
    LOG_NORMAL("Start MyChildThread successfully, ThreadID = %u.", ::GetCurrentThreadId());
    
    //交易处理
    SOCKET sClientSocket = (SOCKET)lPvoid;
    CLIENTITEM	clientItem = { 0 };
    int nCmd = HandleClientMain(sClientSocket, &clientItem);

    LOG_NORMAL("Client cmd = %d", nCmd);
    if (nCmd == -1)
        closesocket(sClientSocket);
    else if (nCmd == CONN_MAIN)
        LoginTrans(sClientSocket, &clientItem);
    else
        InterTrans(sClientSocket, &clientItem, nCmd);
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

上述代码中,在某个线程 MyMainThread 中(可以是主线程也可以是非主线程)调用 accept 接受客户端连接,成功接受连接后,为每一个新连接创建一个工作线程(MyChildThread)。当然,为了能让工作线程可以正常处理所负责的连接上的来往数据,利用线程函数参数将 socket 句柄传给工作线程(注意上述代码中参数 sClientSocket 的传递方法)。

上次更新: 2025/04/01, 20:53:14
7.2 最原始的服务器结构
7.4 Reactor 模式

← 7.2 最原始的服务器结构 7.4 Reactor 模式→

最近更新
01
第二章 关键字static及其不同用法
03-27
02
第一章 auto与类型推导
03-27
03
C++语言面试问题集锦 目录与说明
03-27
更多文章>
Copyright © 2024-2025 沪ICP备2023015129号 张小方 版权所有
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式