【高性能服务器】多线程并发模型

高性能IO并发模型(Reactor/Proactor) 针对 阅读详情

🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

​​

对于常见的C/S模型,一个服务端通常需要服务多个客户端。如果使用单行的处理模型,当新的客户端请求服务端的服务时,就必须等待比它先到的客户端的请求全部完成。

因此引入多进程并发服务器模型。多进程并发服务器模型的简单流程图如下所示。父进程创建一个套接字,然后与自己的IP地址、端口号进行绑定。之后调用开始监听来自客户端的敲门,当有客户端来敲门时,accept()接收客户端的连接并创建一个新套接字用于与客户端通信。接下来调用fork()函数,当调用fork()函数时,操作系统会复制当前进程的一个副本,包括进程的代码、数据和状态等信息。如果其返回值为负数,表示创建子进程失败。否则他在父子进程中有不同的返回值:如果返回值为0,表示当前代码正在子进程中执行。如果返回值大于0,表示当前代码正在父进程中执行,返回的值是子进程的进程ID。因此可以使用if-else语句来编写子进程的处理代码。

在子进程中,先关闭从父进程中复制下来监听套接字,这个套接字在子进程中没有用了,纯属浪费资源,之后再进行与客户端的通信。而在父进程中,同理关闭accept()创建的新套接字,然后继续监听客户端的连接请求。

而同一进程的所有线程共享相同的内存空间,线程数据共享和通信更加方便,创建和管理线程的资源消耗较少,尤其是内存开销较小。由于线程间通信和进程间通信,多线程模型在处理大量任务时响应速度较快。

在多进程+多线程代码的基础上,将进行accept的进程修改为创建的线程工作,socket通信的功能放在线程工作函数内。

​

使用服务器测试业务:

客户端向标准输入发送小写字符串,服务端响应回复对应大写字符,"abcAS"->"ABCAS"

客户端向服务端发送关键字localtime,服务端响应回复系统时间、

服务端: 

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <ctype.h>
#define _SERVER_IP "xxx.xxx.xxx.xxx"
#define _PORT 8080
#define _BACKLOG 128
#define _SHUTDOWN 1
#define _TRUE 1
#define _FALSE 0
#define _IPSIZE 16
#define _RECVLEN 1500

struct client_info
{
    int client_fd;
    struct sockaddr_in clientAddr;
};

void sig_wait(int n)
{
    pid_t zpid;
    while ((zpid = waitpid(-1, NULL, WNOHANG)) > 0)
    {
        printf("wait Thread Tid [0x%x] Wait Successfully,Zombie %d\n", (unsigned int)pthread_self(), zpid);
    }
}

void *thread_wait(void *arg)
{
    // 设定信号捕捉
    pthread_detach(pthread_self());
    struct sigaction act, oact;
    act.sa_handler = sig_wait;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGCHLD, &act, &oact);
    // 解除屏蔽
    sigprocmask(SIG_SETMASK, &act.sa_mask, NULL);
    printf("wait Thread [0x%x] is Waiting...\n", (unsigned int)pthread_self());
    while (1)
        sleep(1);
    pthread_exit(NULL);
}
void *thread_recv(void *arg)
{
    int recvlen;
    char Result[_RECVLEN];
    char client_ip[_IPSIZE];
    time_t tp;
    char time_buf[100]; // 存放当前系统时间
    int toupper_flag;

    struct client_info cinfo = *((struct client_info *)arg);
    // Socket通信
    bzero(Result, sizeof(Result));
    bzero(client_ip, sizeof(client_ip));
    inet_ntop(AF_INET, &cinfo.clientAddr.sin_addr.s_addr, client_ip, _IPSIZE);
    printf("Connection From :IP[%s],PORT[%d]\n", client_ip, ntohs(cinfo.clientAddr.sin_port));
    sprintf(Result, "Hi [%s] Welcome to my TCP test server!service version 1.1.0...", client_ip);
    send(cinfo.client_fd, Result, strlen(Result), 0);
    bzero(Result, sizeof(Result));

    // 读取用户数据,如果用户发的是普通小写字符字符串,转换为大写,如果发送的是local关键字,响应时间
    // 持续响应,循环读写
    while ((recvlen = recv(cinfo.client_fd, Result, sizeof(Result), 0)) > 0)
    { // 处理客户端业务
        printf("Client Say:%s\n", Result);
        if (strcmp(Result, "localtime") == 0)
        {
            tp = time(NULL); // 获取时间种子
            ctime_r(&tp, time_buf);
            time_buf[strcspn(time_buf, "\n")] = '\0';
            printf("[%s]Response SysTime Successfully!\n", client_ip);
            send(cinfo.client_fd, time_buf, strlen(time_buf) + 1, 0);
        }
        else
        {
            toupper_flag = 0;
            while (recvlen > toupper_flag)
            {
                Result[toupper_flag] = toupper(Result[toupper_flag]);
                ++toupper_flag;
            }
            printf("[%s]Response Toupper Successfully!\n", client_ip);
            send(cinfo.client_fd, Result, recvlen, 0);
        }
    }
    if (recvlen == 0) // 客户端退出
    {
        close(cinfo.client_fd);
        printf("[%s] is Exiting,Kill Child\n", client_ip);
        exit(0);
    }

    close(cinfo.client_fd);
}

int main()
{

    struct sockaddr_in serverAddr, clientAddr;
    int server_fd;
    int client_fd;

    // 主线程设置屏蔽
    sigset_t set, oldset;
    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigprocmask(SIG_SETMASK, &set, &oldset);
    pthread_t tid;
    pthread_create(&tid, NULL, thread_wait, NULL); // 创建回收线程

    socklen_t Addrlen;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(_PORT);
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    bind(server_fd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    listen(server_fd, _BACKLOG);
    printf("Test TCP Server Version 1.1.0 is Running...\n");

    while (_SHUTDOWN)
    {
        Addrlen = sizeof(clientAddr);
        if ((client_fd = accept(server_fd, (struct sockaddr *)&clientAddr, &Addrlen)) > 0)
        {

            struct client_info cinfo;
            cinfo.client_fd = client_fd;
            cinfo.clientAddr = clientAddr;
            pthread_create(&tid, NULL, thread_recv, &cinfo); // 创建工作线程
        }
        else
        {
            // accept失败,测试中断
            close(server_fd);
            if (errno == EINTR)
            {
                printf("Accept ERROR Eintr...\n");
                exit(0);
            }
        }
    }
    close(server_fd);
    return 0;
}

客户端:

#ifndef _MYSOCK_H_
#define _MYSOCK_H_

#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

int SOCKET(int domain, int type, int protocol);
int BIND(int sockfd, struct sockaddr* addr, socklen_t addrlen);
ssize_t RECV(int sockfd, void* buf, size_t len, int flags);
ssize_t SEND(int sockfd, void* buf, size_t len, int flags);
int CONNECT(int sockfd, struct sockaddr* addr, socklen_t addrlen);
int ACCEPT(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
int LISTEN(int sockfd, int backlog);
char* FGETS(char* s, int size, FILE* stream);
int SELECT(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
           struct timeval* timeout);
int socket_init();
int return_response(int clientfd, const char* clientip);
//void strDeal(int *client_fd);

// 全局变量声明
char recv_buf[1024];
char time_buf[64];
int serverFd, clientFd;
struct sockaddr_in clientAddr;
fd_set set, oset;
int client_array[1020];
int maxfd, ready;
socklen_t addrlen;
char clientip[16];
time_t tp;
ssize_t recvlen;
int toupper_flag;
#define SHUTDOWN 1
#endif
#include "MySock.h"


//客户端源码编写,连接服务器成功,服务器反馈信息

#define _IP "xxx.xxx.xxx.xxx"
#define _PORT 8080
int main()
{
    struct sockaddr_in ServerAddr;
    bzero(&ServerAddr,sizeof(ServerAddr));
    ServerAddr.sin_family=AF_INET;
	ServerAddr.sin_port=htons(_PORT);
    inet_pton(AF_INET,_IP,&ServerAddr.sin_addr.s_addr);
    
    int Myfd=SOCKET(AF_INET,SOCK_STREAM,0);
    //看需求决定是否要绑定
    char Response[1024];//存放服务端反馈信息
    ssize_t recvlen;
    bzero(Response,sizeof(Response));
    char sendbuf[1024];
    
    if((CONNECT(Myfd,(struct sockaddr *)&ServerAddr,sizeof(ServerAddr)))==0)
    {
    while(1)
    {    
     if((recvlen=RECV(Myfd,Response,sizeof(Response),0))>0)
     {
         printf("%s\n",Response);
     }
    
    printf("Please Type Some text:");//读取标准输入发送给服务端
    FGETS(sendbuf,sizeof(sendbuf),stdin);    
    sendbuf[strcspn(sendbuf,"\n")]='\0';
    SEND(Myfd,sendbuf,sizeof(sendbuf),0);
    }
    }
    close(Myfd);
    printf("Client is Over\n");
    return 0;
}

Rust语言的多线程编程 Rust是一种系统编程语言,旨在提供内存安全、并发性和高性能。与C/C++相比,Rust通过所有权系统(Ownership System)和借用检查器(Borrow Checker)来实现内存安全。这个特性使得Rust能够在没有垃圾回收的情况下,保证内存的安全管理,从而减少了多线程编程中常见的竞争条件(Race Condition)问题。Rust语言提供了强大的多线程编程能力,结合其内存安全性,使得并发编程变得更加高效和安全。 阅读详情

相关推荐

利用IOCP实现高性能Socket服务器端源码

本文还有配套的精品资源,点击获取 简介:IOCP是Windows中的高效I/O模型,适用于大量并发I/O操作,通过分离I/O操作与通知机制,实现非阻塞式处理请求,提高服务器性能。该示例源码展示了一个基于IOCP和Socket技术的游戏服务器端程序,支持TCP/IP和UDP协议,并且在VC++环境中开发。项目中关键组件包括服务器的启动和初始化、接受新连接、数据收发、错误处理与...

weixin_31961675的博客 974

互联网架构多线程并发编程高级教程

1、并发 2、线程 3、线程安全性 4、锁 5、线程间的通信 6、原子类 7、容器 8、并发工具类 9、线程池及Executor框架 10、jvm与并发 11、实战

东风麦芽糖 1092

构建高性能C++多线程服务器框架

线程池是一组可重用的线程集合,用于执行任务集合。它能够有效地管理线程生命周期,减少线程创建和销毁的开销,提高程序处理并发任务的效率。

weixin_35391606的博客 734

【并发技术系列】「多线程并发编程」技术体系和并发模型的基础探究(夯实基础)

让我们通过本篇文章一同进入并发编程技术的世界里面,相信通过这篇文文章一定会对话你的并发技术体系有一定帮助以及夯实你的基础功底。

世界上并没有完美的程序,但是我们并不因此而沮丧,因为写程序就是一个不断追求完美的过程。 3093

基于多线程的高性能服务器程序的设计

主要讲解了目前高性能服务器的高并发和大流量应对的服务端开发注意事项。

AtmelStudio 7 ASF库学习笔记一:新建工程、配置时钟、下载程序和查看官方例程

由于需要学习一下atmel的atsaml 系列,使用AtmelStudio 7开发,之前主要用STM32,现在看到Atmel的库函数,其实是有些类相似的。 ASF3的库对应ST的标准库。ASF4对应ST的HAL库,Ateml Start对应STM32CubeMX。但在这里还是用ASF3来开发,因为Ateml Start还不支持我的这个芯片,而且和STM32的HAL库类似,ASF4为了好移植都是底...

yxy244的博客 8653

Linux服务器多进程模型

Linux多进程服务器真的很给力,赞一个! Linux多进程一般是master负责侦听,worker接受和伺服client。 一个使用了以下技术的多进程模型: 1. sigset:安全信号,信号屏蔽和接受。 2. epoll:异步io模型。 master进程使用信号模型,侦听用户信号和程序信号,并和worker交流。它的主循环是sigsuspend。 worker进程使用事件模型,使用

OSSRS 5068

FPGA实战:AXI BRAM Controller写时序避坑指南(附Vivado配置步骤)

本文深入探讨了FPGA开发中AXI BRAM Controller的写时序实战要点与常见陷阱。文章聚焦于写地址与写数据通道的握手机制,详细解析了AWREADY、WVALID等关键信号的处理,并提供了Vivado中流水线深度、ECC等关键参数的配置步骤与优化建议,旨在帮助开发者有效规避时序问题,提升系统性能。

weixin_32306683的博客 170

并发模型详解:从多进程、多线程到协程与事件驱动

并发模型(Concurrency Model)是程序处理多个任务“同时进行”的方式。并发 ≠ 并行并发(Concurrency):多个任务交替执行,宏观上“同时”,微观上可能串行(单核 CPU);并行(Parallelism):多个任务真正同时执行(多核 CPU)。在高并发服务(如 Web API、消息队列消费者)中,选择合适的并发模型直接影响系统的吞吐量、延迟和资源消耗。模型核心思想最佳场景Python 工具多进程多个独立执行单元CPU 密集型多线程共享内存的轻量级任务低并发 I/O。

Aerkui的博客 1225

面板数据门槛归回分析,xthreg的安装,xthreg2安装包

我用的是Stata17,数据是不平衡的面板数据,需要用到xthreg2,虽然我找到了xthreg2.ado,但是还需要安装xthreg,因为运行xthreg2需要xthreg包顺带安装的lxthreg.mlib文件。但是!我后来发现还是不行,最后是去买了一个真正能用到xthreg2的lxthreg.mlib文件,才可以运行。

weixin_55252589的博客 1万+

RK3588+TRL8367s 四网口千兆交换机配置与优化实战

本文详细介绍了基于RK3588高性能SoC与TRL8367s千兆交换芯片构建四网口交换机的完整实战流程。内容涵盖硬件连接、设备树配置、内核驱动加载、性能优化及常见问题排查,重点解析了RGMII时序调试、DSA架构配置等关键步骤,为嵌入式网络设备开发提供实用指南。

weixin_29266647的博客 669

【网络编程】高性能并发服务器源码剖析

hello!大家好呀!欢迎大家来到我的网络编程系列之洪水网络攻击,在这篇文章中,希望这篇文章能对你有所帮助,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!

weixin_73951752的博客 4789

STM32H7B0VBT6使用Free RTOS配置SD卡+Fatfs文件管理系统

STM32H7B0VBT6使用Free RTOS配置SD卡+Fatfs文件管理系统,记录配置过程

Jack的小窝 5942

Netty的线程模型?

【代码】Netty的线程模型?

IT枫斗者的博客 796

UG NX与PLC-1500的在环虚拟测试

TIA、PLCSIM、NetToPLCsim、KEPServerEX以及UG NX的在环虚拟测试

lvtlvtlvtlvt的博客 4216

【系统架构设计师】2025年下半年真题论文回忆版及写作要点

本文摘要围绕云原生数据库、性能测试、无服务器架构和秒杀场景等关键技术进行论述。云原生数据库通过容器化、微服务和自动化运维实现弹性扩展;性能测试需要规划测试场景并关注响应时间等关键指标;无服务器架构以事件驱动为核心,简化开发流程;秒杀场景需组合运用扩容、缓存、限流等技术应对高并发。这些技术方案为构建现代高可用系统提供了重要参考。

数据知道的博客 6154

多线程模型真的能够提高效率吗?

   多线程模型真的能够提高效率吗? 原文地址http://blog.csai.cn/user1/16236/archives/2007/11679.html         Post  by  acqy 发表于 2007-1-15 21:51:00早段时间在网上看到一篇文章,其中就写了使用多线程模型实现文件的

woyaowenzi的专栏 2909

高通camx hal进程dump日志分析四:Node DumpDebugInfo原理分析

摘要: 本文讲解了高通Camx架构中DumpDebugInfo的原理,重点分析了Node组件的DumpDebugInfo()方法。该方法会输出Node自身调试信息,并调用DumpFenceErrors()接口输出Fence错误信息。具体包括:检查请求队列状态、输出未完成请求的元数据/完成状态/未处理Fence数量等关键信息,以及Fence错误原因(如延迟、未触发、结果失败等)。通过代码注释详细解析了各字段含义,为后续分析Dump异常日志提供理论基础。

782

基于主从Reactor模式的高性能服务器

基于主从Reactor高性能服务器组件设计分析

gma999的博客 1511

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

kama-webserver:高性能Web服务器的典范

kama-webserver:高性能Web服务器的典范 在当今互联网高速发展的时代,Web服务器的性能和稳定性至关重要。今天,我们将为您介绍一个开源项目——kama-webserver,这是一款高性能的Web服务器,使用C++实现,具备优秀的多线程处理能力和高效的网络模型。 项目介绍 kama-webserver是一个采用C++语言编写的高性能Web服务器,其设计理念源于muduo库,采用了多线程...

gitblog_00617的博客 370

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Swoole是如何实现并发处理的?

Swoole实现并发处理主要依赖于它的特殊设计,它能让服务器同时处理很多请求,而不需要一个个来。就像有多个小朋友同时向你提问,你可以同时回答他们,而不是回答完一个再回答下一个。总的来说,Swoole就像一个超级高效的助手,帮助我们更好地处理各种网络任务,让我们的生活更加便捷。

长风破浪会有时的博客 564

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

猫抓 cat-catch 免费浏览器视频嗅探指南:3 步跑通第一次网页视频下载

猫抓(cat-catch)是一款免费开源的浏览器资源嗅探扩展,它把页面播放过的视频、音频登记成一份清单,供你预览并一键保存。本指南面向零基础新手:读完后你能在 3 步内存下第一个文件,并会用 M3U8 合并下载最关键的几个参数。 ## 环境与获取:安装猫抓的 3 条路 - **官方商店**:在 Chrome、Edge 或 Firefox 商店搜索 "Cat-Catch" 安装。注意商店出现过夹

gitblog_01016的博客 801

nginx并发模型与traffic_server并发模型简单比较

nginx并发模型: nginx 的进程模型采用的是prefork方式,预先分配的worker子进程数量由配置文件指定,默认为1,不超过1024。master主进程创建监听套接口,fork子进程以后,由worker进程监听客户连接,每个worker子进程独自尝试accept已连接套接口,accept是否上锁可以配置,默认会上锁,如果操作系统支持原子整型,才会使用共享内存实现原子上锁,否则...

weixin_30889885的博客 264

C++ 并发编程实战第二版:深入理解C++并发编程的艺术

C++ 并发编程实战第二版:深入理解C++并发编程的艺术 【下载地址】C并发编程实战第二版 《C++ 并发编程实战 第二版》是一本全面解析 C++ 并发编程的权威指南,深入探讨了 std::thread、std::mutex、std::async 等核心工具的使用,以及原子操作和内存模型的复杂机制。本书从基础概念到高级技...

gitblog_06740的博客 369

Reactor模型&Proactor模型

与 Reactor 模式不同,Proactor 模式包括应用进程(包括初始化线程Proactor Initiator、业务处理线程Handler)和内核进程(包括通知线程Proactor、注册及I/O线程Asynchronous Operator Processor)。其中:应用进程的Procator Initiator负责创建Procator和Handler,并将Procator和Handler都通过Asynchronous Operation Processor注册到内核。

2301_79989083的博客 773

并发框架SEDA

应用程序开发中经常采用多线程模型(如下图),工作原理一般是对于每一个request,dispatcher会为其创建并分配一个线程,该线程负责这个请求的处理。这种模式优点是处理逻辑清晰,容易开发,执行粒度也是整个完整的处理流程。        但是也存在缺点,随着处理请求不断增加,导致并发执行的线程数量太多,过多的线程数量导致系统在线程调度和资源争用上的开销过大,引起系统性能急剧下降,经常是难

jstart 2337
上一篇: 【高性能服务器】单进程服务器
下一篇: 【力扣】最长连续序列
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值