【Linux高性能服务器编程】——高性能服务器框架

news2024/9/28 5:29:36

 16b9d0dfc990426e968798e2f5a7628b.png

hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之高性能服务器框架介绍,在这篇文章中,你将会学习到高效的创建自己的高性能服务器,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!!

希望这篇文章能对你有所帮助9fe07955741149f3aabeb4f503cab15a.png,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!1a2b6b564fe64bee9090c1ca15a449e3.png注:这章对于高性能服务器的架构非常重要哟!!!

03d6d5d7168e4ccb946ff0532d6eb8b9.gif         

目录

 一.服务器模型

    1.1 C/S模型

C/S模型的组成

C/S模型的通信过程

1.2 P2P模型

Linux 中的服务器 P2P 模型

二. 两种高效的服务器事件处理

2.1 Reactor模式

Linux 中的 Reactor 模式

2.2. Proactor模式

Linux 中的 Proactor 模式

 一.服务器模型

    1.1 C/S模型

C/S模型,即客户端/服务器模型(Client/Server Model),是一种网络计算模型,它将任务和工作负载分配到客户端和服务器两个不同的计算环境中。在这种模型中,客户端负责发送请求,而服务器负责处理请求并返回响应。

如图: 

C/S模型的组成

  1. 客户端(Client)

    • 客户端通常是用户直接交互的应用程序,例如网页浏览器、电子邮件客户端或移动应用。
    • 它向服务器发送请求,并接收服务器返回的数据。
    • 客户端可以执行一些计算任务,但主要依赖于服务器来处理复杂或数据密集型的任务。
  2. 服务器(Server)

    • 服务器是一个提供数据存储和服务的系统,它响应客户端的请求。
    • 服务器通常拥有强大的计算能力和存储空间,能够处理多个客户端的请求。
    • 服务器可以运行数据库管理系统,如 MySQL 或 PostgreSQL,以及各种服务器软件,如 HTTP 服务器 Apache 或 Nginx。

C/S模型的通信过程

  1. 请求:客户端建立一个到服务器的连接,并发送一个请求。
  2. 处理:服务器接收到请求后,对其进行处理。
  3. 响应:服务器将处理结果作为响应发送回客户端。
  4. 关闭连接:客户端接收响应后,通常关闭与服务器的连接

我们可以使用多线程来进行实现,一个连接的业务处理分配一个线程:

 

核心代码如下:

线程处理函数:

// 定义线程函数
void *handle_client(void *socket_desc) {
    int sock = *(int*)socket_desc;
    char *message;
    int len;

    // 接收客户端数据
    while((len = read(sock, message, 1024)) > 0) {
        printf("收到数据:%s\n", message);
        // 发送响应
        write(sock, "Hello, Client!", 14);
        memset(message, 0, 1024);
    }

    // 关闭套接字
    close(sock);
    return 0;
}

 主函数:

​
int main() {
    int sock, newsock, clilen;
    struct sockaddr_in serv_addr, cli_addr;
    int *new_sock;
    pthread_t thread_id;

    // 创建套接字
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        printf("Could not create socket");
    }

    // 填充服务器地址结构
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(8080);

    // 绑定套接字到地址
    if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("bind failed");
        return -1;
    }

    // 监听套接字
    listen(sock, 3);
    printf("Listening...\n");
    clilen = sizeof(cli_addr);

    // 接受客户端连接
    while((newsock = accept(sock, (struct sockaddr *)&cli_addr, (socklen_t*)&clilen)) > 0) {
        // 创建新线程
        new_sock = malloc(1);
        *new_sock = newsock;
        if (pthread_create(&thread_id, NULL, handle_client, (void*)new_sock) < 0) {
            perror("could not create thread");
            return -1;
        }
        pthread_detach(thread_id);
    }

    // 关闭套接字
    if (newsock < 0) {
        perror("accept failed");
        return -1;
    }

    return 0;
}

​

 

1.2 P2P模型

在 Linux 环境中,P2P(点对点)模型是一种直接连接两个或多个计算机的网络通信方式,其中没有中心服务器参与数据传输。在 P2P 模型中,每个节点既是客户端也是服务器,可以相互发送和接收数据。

Linux 中的服务器 P2P 模型

  1. 节点间通信

    • P2P 网络中的每个节点都直接与其他节点通信,没有中央服务器来处理连接和数据传输。
    • 节点之间通过套接字(sockets)进行通信,可以建立全双工连接。
  2. 网络拓扑

    • P2P 网络可以是星型、网状或混合型,取决于节点的连接方式和网络结构。
    • 节点可以通过各种机制发现其他节点,如 DHT(分布式哈希表)算法。

如图:

代码和C/S相似,大家可以去网上自行寻找资料,这里就不再重复了哦!

二. 两种高效的服务器事件处理

2.1 Reactor模式

Reactor 模式是一种事件驱动的网络编程模式,用于处理高并发网络服务。在 Reactor 模式中,一个或多个线程负责监听网络事件,当事件发生时,例如新的连接请求、数据到达等,Reactor 模式会触发相应的处理函数来处理这些事件。

Linux 中的 Reactor 模式

  1. 事件循环

    • Reactor 模式通常包含一个事件循环,该循环不断地轮询所有事件,等待事件发生并处理它们。
  2. 事件处理器

    • 事件处理器是处理特定事件的函数,它们通常与事件类型相关联。
  3. 事件分派器

    • 事件分派器负责将事件分发给相应的事件处理器。
  4. 事件源

    • 事件源是产生事件的实体,例如网络套接字、文件描述符等。

流程图如下:

  

使用的是同步I/O模型。

1) 主线程往epol l 内核事件表中注册socket上的读就绪事件。

2) 主线程调用epol l _ wait等待 socket 上有数据可读。 

3)当socket 上有数据可读时, epoll _ wait通知主线程。主线程则将socket 可读事件放入请求队列。  

4)睡眠在请求队列上的某个工作线程被唤醒,它从 socket 读取数据,并处理客户请求,然后往epol l内核事件表中注册该socket 上的写就绪事件。

5) 主线程调用epoll _ wait等待 socket 可写。

6)当socket 可写时, epoll wait通知主线程。主线程将socket 可写事件放入请求队列。 

7)睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。
 

 

2.2. Proactor模式

Proactor 模式是一种事件驱动的网络编程模式,与 Reactor 模式类似,但它使用异步 I/O 操作来处理网络事件。在 Proactor 模式中,一个或多个线程负责监听网络事件,当事件发生时,例如新的连接请求、数据到达等,Proactor 模式会触发相应的处理函数来处理这些事件。

Linux 中的 Proactor 模式

  1. 事件循环

    • Proactor 模式通常包含一个事件循环,该循环不断地轮询所有事件,等待事件发生并处理它们。
  2. 事件处理器

    • 事件处理器是处理特定事件的函数,它们通常与事件类型相关联。
  3. 事件分派器

    • 事件分派器负责将事件分发给相应的事件处理器。
  4. 事件源

    • 事件源是产生事件的实体,例如网络套接字、文件描述符等。
  5. 异步 I/O 操作

    • Proactor 模式使用异步 I/O 操作来处理网络事件,这样可以减少线程间的上下文切换,提高系统的性能。

 具体流程图如下:

1)主线程调用aio _ read 函数向内核注册socket 上的读完成事件, 并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序(这里以信号为例,详情请参考sigevent的man手册)。

2)主线程继续处理其他逻辑。 

3)当socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,以通知应用程序数据已经可用。  

4)应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完客户请求之后,调用aio _ write函数向内核注册 socket上的写完成事件, 并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序(仍然以信号为例)。

5)主线程继续处理其他逻辑。  

6)当用户缓冲区的数据被写入socket之后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕。  

7)应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭 socket。

 这里给出一个代码例子:

事件处理函数:

// 事件处理函数
void *handle_connection(void *socket_desc) {
    int sock = *(int*)socket_desc;
    char *message;
    int len;

    // 接收客户端数据
    while((len = read(sock, message, 1024)) > 0) {
        printf("收到数据:%s\n", message);
        // 发送响应
        write(sock, "Hello, Client!", 14);
        memset(message, 0, 1024);
    }

    // 关闭套接字
    close(sock);
    return 0;
}

循环逻辑:

 while(1) {
        activity = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if ((activity < 0) && (errno != EINTR)) {
            printf("epoll_wait error");
            return -1;
        }

        for (i = 0; i < activity; i++) {
            if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) {
                // 处理错误情况
                close(events[i].data.fd);
                continue;
            }

            if (events[i].data.fd == sock) {
                // 有新的客户端连接
                newsock = accept(sock, (struct sockaddr *)&cli_addr, (socklen_t*)&clilen);
                printf("新的客户端连接:%s\n", inet_ntoa(cli_addr.sin_addr));
                client_sockets[i] = newsock;
                event.events = EPOLLIN;
                event.data.fd = newsock;
                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, newsock, &event) == -1) {
                    perror("epoll_ctl failed");
                    return -1;
                }
            } else {
                // 处理客户端数据
                new_sock = malloc(1);
                *new_sock = events[i].data.fd;
                if (pthread_create(&thread_id, NULL, handle_connection, (void*)new_sock) < 0) {
                    perror("could not create thread");
                    return -1;
                }
                pthread_detach(thread_id);
            }
        }
    }

    // 关闭 epoll 实例
    close(epollfd);

    return 0;
}

在这个例子中,服务器端使用 epoll 函数来实现 Proactor 模式,创建一个简单的服务器。服务器使用 pthread 库来创建多线程来处理多个客户端的连接,每个连接都由一个单独的线程处理。服务器收到客户端的请求后,发送一个响应,并关闭与客户端的连接。(不是完整代码哦!)

   好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家还有不懂得,可以评论区或者私信我都可以哦4d7d9707063b4d9c90ac2bca034b5705.png!! 感谢大家的阅读,我还会持续创造网络编程相关内容的,记得点点小爱心和关注哟!2cd0d6ee4ef84605933ed7c04d71cfef.jpeg     

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

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

相关文章

LeetCode:组合求和III之回溯法

题目 题目链接 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a;只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。题目图解 ** ** cpp代码 class …

大模型改变了NLP的游戏规则了吗

NLP已经死了吗&#xff1f; 自从 ChatGPT 横空出世以来&#xff0c;自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09; 研究领域就出现了一种消极的声音&#xff0c;认为大模型技术导致 NLP “死了”。在某乎上就有一条热门问答&#xff0c;大…

03-为啥大模型LLM还没能完全替代你?

1 不具备记忆能力的 它是零状态的&#xff0c;我们平常在使用一些大模型产品&#xff0c;尤其在使用他们的API的时候&#xff0c;我们会发现那你和它对话&#xff0c;尤其是多轮对话的时候&#xff0c;经过一些轮次后&#xff0c;这些记忆就消失了&#xff0c;因为它也记不住那…

Python 开发实现登陆和注册模块

Python 开发实现登陆和注册模块 一、案例介绍 本例设计一个用户登录和注册模块&#xff0c;使用Tkinter框架构建界面&#xff0c;主要用到画布、文本框、按钮等组件。涉及知识点&#xff1a;Python Tkinter界面编程、pickle数据存储。本例实现了基本的用户登录和注册互动界面…

纹理合成在AI去衣技术中的关键作用

随着人工智能技术的飞速发展&#xff0c;图像处理和计算机视觉领域取得了显著的进步。其中&#xff0c;AI去衣技术作为图像处理的一个分支&#xff0c;近年来引起了广泛关注。在AI去衣技术中&#xff0c;纹理合成发挥着至关重要的作用&#xff0c;它不仅能够保证图像的真实性&a…

YOLO算法改进Backbone系列之MogaNet:

卷积神经网络&#xff08;ConvNets&#xff09;一直是计算机视觉的首选方法。受灵长类视觉系统的启发&#xff0c;卷积层可以对具有区域密集连接和平移等方差约束的观测图像的邻域相关性进行编码。通过交错分层&#xff0c;ConvNets获得了被动增加的感受野&#xff0c;并善于识…

掼蛋比赛中的违规及处罚

一、越序违规及处罚 1、越序抓牌&#xff1a;抢先抓其他选手应抓的牌。 &#xff08;1&#xff09;越序抓牌但并没有看到的&#xff0c;一经发现须马上退回。 &#xff08;2&#xff09;越序抓牌已经看到的但是没有插入手牌中的&#xff0c;除马上退回外&#xff0c;可由裁判员…

OpenHarmony实战开发-文件上传下载性能提升指导。

概述 在开发应用时&#xff0c;要实现高效的客户端跟服务器之间数据交换&#xff0c;文件传输的性能是至关重要的。一个数据交换性能较低的应用会导致其在加载过程中耗费较长时间&#xff0c;在很多的场景造成页面卡顿&#xff0c;极大的影响了用户体验。相反&#xff0c;一个…

【进程地址空间】地址空间理解存在原因 | 深入理解页表写时拷贝虚拟地址

目录 地址空间深入理解 划分区域 理解地址空间 地址空间存在的意义 意义1 意义2 意义3 理解页表和写时拷贝 页表 写时拷贝 OS识别错误 理解虚拟地址 fork解释 上篇我们简单的学习了进程地址空间/页表/物理地址/虚拟地址/写时拷贝等概念。本篇深入理解下。 地址空…

分数求和(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;double a 0, b 1, result1 2, sum 0;int i 0;//循环运算&#xff1b;for (i 1; i <…

人工智能大模型培训老师叶梓 探索知识库问答中的查询图生成:处理多跳复杂问题的新方法

在人工智能领域&#xff0c;基于知识库的问答&#xff08;KBQA&#xff09;技术正变得越来越重要。它使得机器能够理解自然语言问题&#xff0c;并从结构化的知识库中检索答案。然而&#xff0c;面对多跳复杂问题&#xff0c;传统的KBQA方法往往力不从心。近期&#xff0c;研究…

Threejs绘制传送带

接下来会做一个MES场景下的数字孪生&#xff0c;所以开始做车间相关的模型&#xff0c;不过还是尽量少用建模&#xff0c;纯代码实现&#xff0c;因为一方面可以动态使用&#xff0c;可以调节长度和宽度等&#xff0c; 下面这节就做一个简单的传送带&#xff0c;这是所有车间都…

C++心决之类和对象详解(中篇)(封装入门二阶)

目录 1.类的6个默认成员函数 2. 构造函数 2.1 概念 2.2 特性 3.析构函数 3.1 概念 3.2 特性 4. 拷贝构造函数 4.1 概念 4.2 特征 5.赋值运算符重载 5.1 运算符重载 5.2 赋值运算符重载 5.3 前置和后置重载 7.const成员 8.取地址及const取地址操作符重载 1.类的…

Win 进入桌面黑屏,只有鼠标

大家好&#xff0c;我叫秋意零。 今天&#xff0c;遇到一个同事电脑进入桌面黑屏&#xff0c;只有鼠标。经过询问沟通&#xff0c;说是 Windows 突然进行了自动更新&#xff0c;更新之后桌面就黑了屏。经过查询是一个桌面进程没启动才会导致桌面黑屏。首先分两种情况&#xff0…

【linux】软件工具安装 + vim 和 gcc 使用(上)

目录 1. linux 安装软件途径 2. rzsz 命令 3. vim 和 gcc 使用 a. vim的基本概念 b. 命令模式下的指令 c. 底行模式下的指令 1. linux 安装软件途径 源代码安装rpm安装 -- linux安装包yum安装&#xff08;最好&#xff0c;可以解决安装源&#xff0c;安装版本&#xff0…

ArrayList与顺序表(1)

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x…

使用matplotlib的quiver绘制二维箭头图

使用ax.quiver绘制二维箭头图 1. matplotlib的quiver函数的调用方式 quiver函数是axes类的成员函数&#xff0c;其基本调用方式为&#xff1a; quiver([X, Y], U, V, [C], **kwargs) [X,Y]是箭头的位置&#xff0c;U,V是箭头的方向&#xff0c;C是箭头颜色。 具体而言&#x…

多项式轨迹规划

公众号“轻松玩转机器人”&#xff0c;欢迎关注。 1、简介 常用的多项式规划一般泛指3次、5次和7次等多项式规划&#xff0c;4次多项式规划用到的比较少&#xff0c;暂不介绍。 为什么奇数次多项式比较常用呢&#xff1f;因为其有偶数个系数&#xff01; 偶数个系数有什么用…

泛型的初步认识(1)

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x…

Unity中的UI系统之UGUI

目录 概述UGUI基础——六大基础组件六大基础组件概述Canvas画布组件CanvasScaler画布缩放控制器组件必备知识恒定像素模式缩放模式恒定物理模式3D模式 Graphic Raycaster图形射线投射器EventSystem和Standalone Input ModuleRectTransform UGUI基础——三大基础控件Image图像控…