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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言使用先序遍历创建二叉树

#include<stdio.h> #include<stdlib.h>typedef struct node {int data;struct node * left;struct node * right; } Node;Node * createNode(int val); Node * createTree(); void freeTree(Node * node);void preOrder(Node * node);// 先序创建二叉树 int main()…

猫头虎博主全栈前沿AI技术领域矩阵社群

猫头虎博主全栈前沿AI技术领域矩阵社群 &#x1f44b;大家好&#xff0c;我是猫头虎&#xff01;今天我要向大家介绍一个非常重要的社群矩阵——专为全栈前沿AI技术领域的朋友们打造的各种技术交流和资源互助的社群。这些社群不仅能帮助大家快速提升技术水平&#xff0c;还能拓…

【数据结构与算法】堆排序算法原理与实现:基于堆实现的高效排序算法

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注 ​ 目录 一、引言 堆排序的简介 堆排序的特点 二、堆的概念 三、堆排序算法的原理 四、堆…

MySQL 9.0 悄悄上线,支持面向AI的向量数据库

MySQL狂热粉丝群已经发现MySQL官网上MySQL9.0这两天悄然上线&#xff0c;已经可以下载体验了&#xff0c;目前被定义为创新版本&#xff08;Innovation&#xff09;。 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 支持主流的操作系统&#xff0c;安装后可以直…

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

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

TCP 的安全可靠

TCP的安全可靠 重传机制往返时间测量快速重传 流量控制拥塞控制 重传机制 T C P确认从另一端收到的数据以提供可靠的运输层&#xff0c;但数据和确认都有可能会丢失。 T C P通过在发送时设置一个定时器来解决这种问题。如果当定时器溢出时还没有收到确认&#xff0c;它就重传该…

7.2.SQL注入-基于函数报错extractvalue(),floor()

注入基于函数报错extractvalue(),floor()-字符型 基于extractvalue() 爆出数据库版本payload语句&#xff1a; kobe and extractvalue(0,concat(0x7e,version()))#爆出数据库版本 基于floor() floor()函数就是取整数 爆出数据版本信息 kobe and (select 2 from (select …

深度解密Spark性能优化之道

课程介绍 课程通过实战案例解析和性能调优技巧的讲解&#xff0c;帮助学员提升大数据处理系统的性能和效率。课程内容涵盖了Spark性能调优的各个方面&#xff0c;包括内存管理、并行度设置、数据倾斜处理、Shuffle调优、资源配置等关键技术和策略。学员将通过实际案例的演示和…

【Altium】如何处理PCB上所有焊盘被误盖油

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决焊盘被误盖油的操作 2、 问题场景 所有焊盘都可以设置为盖油或不盖油&#xff0c;由于焊盘需要用来焊接元器件&#xff0c;所以都不会设置盖油。由于误操作或者创建封装时设置错误&#xff0c;造成一定数量的焊盘…

web基础及http协议

一、WEB&#xff1a;就是我们所说的页面&#xff0c;点开的每个页面都是web。&#xff08;全球广域网、万维网&#xff09; 分布式图形信息系统&#xff1a;同一个服务&#xff0c;但是部署在不同的机器上且提供的服务和内容全部一致&#xff0c;集群就是建立在分布式的基础上。…

爬虫逆向实战(42)-某巢登陆(AES、MD5、RSA、滑块验证码)

一、数据接口分析 主页地址&#xff1a;某巢 1、抓包 通过抓包可以发现在登录时&#xff0c;网站首先请求captcha/querySlideImage/来获取滑块验证码的图片&#xff0c;然后请求captcha/checkCode/接口来验证滑块验证码。滑块验证码校验成功后&#xff0c;请求noshiro/getPu…

nlp--最大匹配分词(计算召回率)

最大匹配算法是一种常见的中文分词算法&#xff0c;其核心思想是从左向右取词&#xff0c;以词典中最长的词为优先匹配。这里我将为你展示一个简单的最大匹配分词算法的实现&#xff0c;并结合输入任意句子、显示分词结果以及计算分词召回率。 代码 : # happy coding…

Ubuntu24.04之安装KVM(二百五十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

2-18 基于matlab的关于联合对角化盲源分离算法的二阶盲识别(SOBI)算法

基于matlab的关于联合对角化盲源分离算法的二阶盲识别&#xff08;SOBI&#xff09;算法。通过联合对角化逼近解混矩阵。构建的四组信号&#xff0c;并通过认为设置添加噪声比例&#xff0c;掩盖信号信息。通过SOBI算法实现了解混。程序已调通&#xff0c;可直接运行。 2-18联合…

JavaScript中location对象的主要属性和方法

属性 href&#xff1a;获取或设置整个URL。protocol&#xff1a;获取URL的协议部分&#xff0c;如"http:"或"https:"。host&#xff1a;获取URL的主机名&#xff08;包括端口号&#xff0c;如果有的话&#xff09;。hostname&#xff1a;获取URL的主机名&…

netlink通信——读取路由表获取通信网卡IP

读取路由表获取通信网卡IP是什么意思呢&#xff1f;且听我一一道来… 下面是我虚拟机两个网卡的IP&#xff0c;很明显两个网卡是不同网段的&#xff0c;我的物理机网卡网段是192.168.1.0/24&#xff0c;与我物理机和外网通信的网卡是ens160&#xff0c;即192.168.31.0/24网段&a…

2018年全国大学生数学建模竞赛A题高压油管的压力控制(含word论文和源代码资源)

文章目录 一、部分题目二、部分论文三、部分源代码问题1&#xff08;1&#xff09;绘制弹性模量与压力函数图&#xff08;2&#xff09;求最优单次开阀时间 问题二&#xff08;1&#xff09;极径与极角关系&#xff08;2&#xff09;求最优凸轮角速度 四、完整word版论文和源代…

多语言模型(Multilingual Models)用于推理(Inference)

在深入探讨多语言模型&#xff08;Multilingual Models&#xff09;用于推理&#xff08;Inference&#xff09;的详细内容时&#xff0c;我们需要首先理解多语言模型的基本概念、它们如何工作、为什么它们在现代自然语言处理&#xff08;NLP&#xff09;中变得如此重要&#x…

物理建模的一个重要概念:因果/非因果建模

物理系统的建模仿真&#xff0c;根据建模思想可划分为&#xff1a; 因果建模&#xff08;Causal Modeling&#xff09;非因果建模&#xff08;Acausal Modeling&#xff09; 二者的核心思想是通过信号流还是方程来定义模型的行为。 像我们熟知的Simulink就是基于因果建模的思…

【C++知识点总结全系列 (05)】:IO 类的详细总结和分析

1、基类 istream 和 ostream (1)istream A.What 输入流的抽象类&#xff0c;是所有输入流类的基类 B.Why&#xff08;输入流的作用&#xff09; 用于从数据源&#xff08;如文件、标准输入设备等&#xff09;读取数据 (2)ostream A.What 输出流的抽象类&#xff0c;是所有输…