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

news2024/12/22 23:05:30

🔥博客主页: 我要成为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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1885225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在CenteOs7上安装mysql8.0(Super详细版)

在CenteOs7上安装mysql8.0 为什么用Mysql8.0&#xff1f;如何下载下载地址需要提前准备下载步骤 服务器上安装如何上传到服务器&#xff1f;通过wget下载到服务器并解压 开始安装非必须安装如果全部安装执行顺序 安装完后&#xff0c;启动mysql使用“systemctl”检测mysqld服务…

前端基础:JavaScript(篇一)

目录 JavaScript概述 JavaScript历史&#xff1a; 须知&#xff1a; 基本语法 变量 代码 运行 数据类型 1、数值型(number)&#xff1a; 代码 运行 2、布尔型(boolean)&#xff1a; 代码 运行 3、字符串型&#xff1a; 代码 运行 4、 undefined类型 代码…

Boss直聘,无良厂商,乱封号

耽误招工作&#xff0c;瞎吉儿封号 哥们单身 需要女生多的公司 问一下都不行&#xff0c;什么尿性 直接就给你封了 装什么呢 辣鸡boss 倒闭吧赶紧 我是狗子&#xff0c;希望你倒闭&#xff01;

GPON-GPON帧链路层知识学习

前言&#xff1a; 引用&#xff1a; gpon学习_gpon帧结构-CSDN博客 了解 GPON 技术 - Cisco GPON、XG(S)-PON基础_网络_门牙会稍息-开放原子开发者工作坊 gpon学习_gpon帧结构-CSDN博客 广域网宽带接入技术七GPON技术_gtc帧-CSDN博客 https://www.cnblogs.com/aliyunyun/…

顶顶通语音信箱手机助手拦截方案

在电话自动外呼系统&#xff0c;常见的问题是被叫号码开通了语音信箱&#xff0c;或者运营商自动给开通了小秘书服务&#xff0c;一旦电话打不通&#xff0c;就会先播放一个类似这样的提示音&#xff0c;你拨打的电话已经开启了来电小秘书&#xff0c;请在滴声后留言。还有一个…

什么是智能仓储

智能仓储是指利用先进的信息技术、物联网技术以及自动化设备&#xff0c;实现仓储管理过程的智能化&#xff0c;通过集成多种现代技术&#xff0c;使得仓库管理更加高效、精准和透明。以下是智能仓储的一些关键特点和技术&#xff1a; --------------------------------------…

夸克网盘拉新暑期大涨价!官方授权渠道流程揭秘

夸克网盘拉新暑期活动来袭&#xff0c;价格大涨&#xff01;从7月1日开始持续两个月&#xff0c;在这两个月里夸克网盘拉新的移动端用户&#xff0c;一个从原来的5元涨到了10元。这对做夸克网盘拉新的朋友来说&#xff0c;真的是福利的。趁着暑期时间多&#xff0c;如果有想做夸…

迅为2K1000核心板商业级/工业级/全国产

硬件配置 国产龙芯处理器&#xff0c;双核64位系统&#xff0c;板载2G DDR3内存&#xff0c;流畅运行Busybox、Buildroot、Loognix、QT5.12 系统! 接口全板载4路USB HOST、2路千兆以太网、2路UART、2路CAN总线、Mini PCIE、SATA固态盘接口、4G接口、GPS接口WIF1、蓝牙、Mini HD…

中原汉族与北方游牧民族舞蹈文化在这段剧中表现得淋漓尽致,且看!

中原汉族与北方游牧民族舞蹈文化在这段剧中表现得淋漓尽致&#xff0c;且看&#xff01; 《神探狄仁杰》之使团喋血记是一部深入人心的历史侦探剧&#xff0c;不仅以其曲折离奇的案情和狄仁杰的睿智形象吸引观众&#xff0c;更以其对唐代文化的精准再现而备受赞誉。#李秘书讲写…

大数据、人工智能、云计算、物联网、区块链序言【大数据导论】

各位大佬好 &#xff0c;这里是阿川的博客&#xff0c;祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 本篇序言前 必看 【大数据导论】—大数据序言 这是…

自动编码器简单理解及简单使用描述

1. 什么是自动编码器&#xff1f; 自动编码器分为编码器和解码器&#xff0c;其中解码器只在训练阶段用到。具体过程就是&#xff1a; 首先&#xff0c;输入训练样本&#xff0c;编码器对输入样本进行编码&#xff0c;对其进行降维&#xff0c;直到到达某个瓶颈层&#xff1b…

财务RPA工具——探索用于财务自动化的常见RPA工具

近年来数字化浪潮正席卷各大行业&#xff0c;数字化转型成为企业通向成功的一条必经之路。而财务管理作为企业经营过程中的有力支撑&#xff0c;是企业数字化转型的一个关键切入点。如今越来越多的数字技术在现代财务工作中得以应用&#xff0c;以提升财务工作效率&#xff0c;…

JavaScript——对象的创建

目录 任务描述 相关知识 对象的定义 对象字面量 通过关键字new创建对象 通过工厂方法创建对象 使用构造函数创建对象 使用原型(prototype)创建对象 编程要求 任务描述 本关任务&#xff1a;创建你的第一个 JavaScript 对象。 相关知识 JavaScript 是一种基于对象&a…

JVM线上监控环境搭建Grafana+Prometheus+Micrometer

架构图 一: SpringBoot自带监控Actuator SpringBoot自带监控功能Actuator&#xff0c;可以帮助实现对程序内部运行情况监控&#xff0c;比如监控内存状况、CPU、Bean加载情况、配置属性、日志信息、线程情况等。 使用步骤&#xff1a; 1. 导入依赖坐标 <dependency><…

H5漂流瓶交友源码_社交漂流瓶H5源码

简介&#xff1a; 一种流行的娱乐性社交新潮流&#xff0c;年轻人玩得比较多。和盲盒有点类似 社交漂流瓶搭建教程 环境&#xff1a;Nginx 1.20.1-MySQL 5.6.50-PHP-7.3 上传源码至网站根目录&#xff0c;创建并导入数据库 数据库信息修改&#xff1a;/config/database.ph…

.net core 的缓存方案

这里主要讲两个缓存的使用&#xff0c;MemoryCache和Redis 先讲讲常见的缓存 1、.net framework web中自带有Cache缓存&#xff0c;这种缓存属于粘性缓存&#xff0c;是缓存到项目中的&#xff0c;项目从服务器迁移的时候缓存的内容也能够随着服务器一起迁移 2、MemoryCache缓存…

聊聊低代码,它到底厉害在哪?

今天想和大家聊聊低代码这个热门话题。 低代码是什么&#xff1f; 简单来说&#xff0c;它是一种通过可视化界面和少量代码就能快速构建应用程序的技术。以往&#xff0c;开发一款应用可能需要数月甚至更长时间&#xff0c;而低代码的出现大大缩短了这个周期。 低代码的发展历…

Spring Boot配置文件properties/yml/yaml

一、Spring Boot配置文件简介 &#xff08;1&#xff09;名字必须为application,否则无法识别。后缀有三种文件类型&#xff1a; properties/yml/yaml&#xff0c;但是yml和yaml使用方法相同 &#xff08;2&#xff09; Spring Boot 项⽬默认的配置文件为 properties &#xff…

Salus: Efficient Security Support for CXL-Expanded GPU Memory——论文泛读

HPCA 2024 Paper CXL论文阅读笔记整理 问题 GPU已成为许多数据密集型应用程序不可或缺的加速器&#xff0c;如科学工作负载、深度学习模型和图分析&#xff0c;这些应用程序对越来越大的内存有着共同的需求。通常&#xff0c;采用统一存储器[12]、[18]、[34]、[67]和多GPU[7]…

2024阿里云大模型自定义插件(如何调用自定义接口)

1&#xff0c;自定义插件入口 2&#xff0c;插件定义&#xff1a;描述插件的参数 2.1&#xff0c;注意事项&#xff1a; 2.1.1&#xff0c;只支持json格式的参数&#xff1b;只支持application/JSON&#xff1b;如下图&#xff1a; 2.1.2&#xff0c;需要把接口描述进行修改&a…