一、服务器

1.服务器分类

  • 单循环服务器:只能处理一个客户端任务的服务器
  • 并发服务器:可同时处理多个客户端任务的服务器

二、TCP并发服务器的构建

1.如何构建?

        

(1)多进程(每一次创建都非常耗时耗空间,但是安全)

#include "head.h"
int init_tcp(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket fail");return 1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind fail");return 1;}ret = listen(sockfd, 100);if (ret < 0){perror("lisen fail");return 1;}return sockfd;
}
void do_wait(int signo)
{wait(NULL);
}
int main(int argc, char const *argv[])
{int sockfd = init_tcp("192.168.1.138", 50000);if (sockfd < 0){return 1;}signal(SIGCHLD, do_wait);char buf[1024] = {0};struct sockaddr_in cliaddr;int clilen = sizeof(cliaddr);while (1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);if (connfd < 0){perror("connect fail");return 1;}pid_t pid = fork();if (pid > 0){}else if (0 == pid){while (1){memset(buf, 0, sizeof(buf));ssize_t size = recv(connfd, buf, sizeof(buf), 0);if (size < 0){perror("recv fail");break;}else if (0 == size){printf("client connet offline");break;}printf("[%s] [%d]  : %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buf);strcat(buf, "------ok");size = send(connfd, buf, sizeof(buf), 0);if (size < 0){perror("fail send");break;}}close(connfd);exit(1);}else{perror("fork fail");return 1;}}close(sockfd);return 0;
}

(2)多线程(并发程度高、不太安全)

#include "head.h"
int init_tcp(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket fail");return 1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind fail");return 1;}ret = listen(sockfd, 100);if (ret < 0){perror("lisen fail");return 1;}return sockfd;
}
typedef struct
{int connfd;struct sockaddr_in cliaddr;
} XIN;void do_thurance(void *arg)
{XIN xi = *(XIN *)arg;char buf[1024] = {0};while (1){memset(buf, 0, sizeof(buf));ssize_t size = recv(xi.connfd, buf, sizeof(buf), 0);if (size < 0){perror("recv fail");break;}else if (0 == size){printf("client connet offline");break;}printf("[%s] [%d]  : %s\n", inet_ntoa(xi.cliaddr.sin_addr), ntohs(xi.cliaddr.sin_port), buf);strcat(buf, "------ok");size = send(xi.connfd, buf, sizeof(buf), 0);if (size < 0){perror("fail send");break;}}close(xi.connfd);pthread_exit(NULL);
}int main(int argc, char const *argv[])
{int sockfd = init_tcp("192.168.1.138", 50000);if (sockfd < 0){return 1;}char buf[1024] = {0};pthread_t tid;struct sockaddr_in cliaddr;int clilen = sizeof(cliaddr);while (1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);if (connfd < 0){perror("connect fail");return 1;}printf("client getline\n");XIN xi;xi.connfd = connfd;xi.cliaddr = cliaddr;pthread_create(&tid, NULL, do_thurance, &xi);pthread_detach(tid);                    //设置分离属性,线程结束,操作系统自动会回收;}close(sockfd);return 0;
}

(3)线程池

  • 主要解决:程序运行过程中,线程被反复创建和销毁带来的耗时问题;

(4)IO多路复用

        理解:不创建进程和线程的情况下,对多个文件描述符监测复用一个进程;

二、IO多路复用

1.阻塞IO方式:

(1)多个IO之间是同步关系;

(2)多个IO之间相互影响;

2.IO多路复用

(1)步骤

        1)创建文件描述符集合(数组、链表、树形结构.......);

        2)添加关注的文件描述符带集合中;

        3)通过函数接口,把集合传递给内核,并开始检测IO事件(输入输出、读写事件);

        4)当内核检测到事件时,通过相关函数返回,做具体的相关操作;

(2)select

        1)创建文件描述符集合表:fd_set

        2)清楚集合表

         void FD_CLR(int fd, fd_set *set);//把fd清掉
int  FD_ISSET(int fd, fd_set *set);//查看fd在这个表中有没有
void FD_SET(int fd, fd_set *set);//把fd放进集合表中
void FD_ZERO(fd_set *set);//把集合表整体清空

        3)把文件描述符加入到集合表中

        4)select:

     int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

        功能:通知内核检测的集合表并开始检测

        参数:

                nfds:关注的最大描述符+1

                readfds:关注的读事件的我文件描述符的地址

                writefds:关注的写事件的我文件描述符的地址

                exceptfds:其他事件

                timeout:超时事件的地址;设置一个时间结点,如果都没有事件来,就直接返回;NULL:不设置超时时间

        返回值:

                成功:返回到达事件的个数

                失败:-1

                超时时间到达没有事件时:0

位图在内核中,保持最小未被使用原则

#include "head.h"
int init_tcp(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket fail");return 1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind fail");return 1;}ret = listen(sockfd, 100);if (ret < 0){perror("lisen fail");return 1;}return sockfd;
}
int main(int argc, char const *argv[])
{int sockfd = init_tcp("192.168.1.138", 50002);if (sockfd < 0){return 1;}struct sockaddr_in cliaddr;int clilen = sizeof(cliaddr);int maxs;fd_set rdfds;fd_set tmprdfds;FD_ZERO(&rdfds);FD_SET(sockfd, &rdfds);int i = 0;maxs = sockfd;char buf[1024]={0};while (1){tmprdfds = rdfds;int cnt = select(maxs + 1, &tmprdfds, NULL, NULL, NULL);if (cnt < 0){perror("fail select");return 1;}if (FD_ISSET(sockfd, &tmprdfds)){int connfd = accept(sockfd,(struct sockaddr*)&cliaddr, &clilen);if (connfd < 0){perror("fail accept");return 1;}FD_SET(connfd, &rdfds);maxs = maxs > connfd ? maxs : connfd;}// for(i=sockfd;i<maxs+1;++i)// {//     printf("%d\n",i);// }// sleep(3);for (i = sockfd + 1; i < maxs + 1; ++i){if (FD_ISSET(i, &tmprdfds)){memset(buf, 0, sizeof(buf));ssize_t size = recv(i, buf, sizeof(buf), 0);if (size < 0){perror("recv fail");continue;}if(0==size){printf("client offlink\n");return 1;}printf("[%s] [%d]  : %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buf);strcat(buf, "------ok");size = send(i, buf, sizeof(buf), 0);if (size < 0){perror("fail send");continue;}}  }}close(sockfd);return 0;
}
        缺陷:
  • 限制了最多只能检测1024个文件描述符(底层使用数组的机制储存);
  • 在应用层每次需要遍历才可找到到达的事件的文件描述符,效率不高,还耗时;
  • 集合表存在于应用层,内核存在应用层和内核层的数据表的反复拷贝,耗时;
  • select只能工作在水平触发模式(低速模式),不能工作在边沿触发模式(高速模式);

        边沿触发:数据从无变有,从低电平到高电平,触发一次,称读数据的上升沿触发;

数据一次收不完,但是下一次继续读,

        水平触发:数据从无到有,先触发一次读,没读完,再触发读,一直到读完了,才不触发;优势在反复把数据读完;缺点:耗时,低俗模式

     

(3)poll

        1)解决的问题:检测的文件描述符个数不受1024限制;底层对于集合表的方式改变,变成了链表,时间复杂度O(n),其他问题未被改善,仍然需要反复拷贝、遍历、只可工作在水平触发模式;

(4)epoll

        1)解决的问题:检测的文件描述符是树形结构;时间复杂度是O(log(n)【红黑树】,也不受1024限制;将检测的文件描述符集合创建在内核,解决了内核和用户层的数据拷贝;直接返回到达事件的文件描述符集合,不需要遍历寻找;epoll可以工作在水平触摸式,也可工作在边沿触发模式

        2)步骤

                a)创建文件描述符集合表;

              int epoll_create(int size);

                功能:创建文件描述符集合表到内核

                参数

                        size:最多监测的文件描述符的个数

                返回值:

                        成功返回非负的文件描述符,代表了内核的集合;

                        失败返回-1

                b)添加关注的文件描述符到集合;

                int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

                功能:对文件描述符进行操作;

                参数:

                        epfd:文件描述符集合表的文件描述符

                        op:        
EPOLL_CTL_ADD        新增事件
EPOLL_CTL_MOD        修改事件 
EPOLL_CTL_DEL        删除事件
fd:要操作的文件描述符 
events:事件相关结构体

                                         EPOLLIN        读事件
EPOLLOUT    写事件  
EPOLLET        边沿触发    
LT            水平触发

typedef union epoll_data {
void        *ptr;
int          fd;
uint32_t     u32;
uint64_t     u64;
} epoll_data_t;

        struct epoll_event {
uint32_t     events;      /* Epoll events */
epoll_data_t data;        /* User data variable */
};

                返回值:

                        成功返回0;

                        失败返回-1;

                c)epoll通知内核开始检测;

               
      int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);

                功能:监听事件表中的事件,并将产生的事件存放到结构体数组中
    参数:
epfd:事件表文件描述符
events:存放结果事件结构体数组空间首地址 
maxevents:最多存放事件个数
timeout:超时时间
-1:阻塞等待直到有事件发生 
返回值:
成功返回产生事件个数
失败返回-1 

                  d)epoll返回检测到的事件结果;

3.在数据量比较小的时候,select的比epoll的性能差不多,甚至更好,更小的时候,优势体现不明显

对于IO:如果处理的任务有耗时任务,此时应该考虑增加进线程,把耗时的任务给进线程去做



并发服务器的性能对比

线程池+epoll 

对于IO:如果处理的任务有耗时任务,此时应该考虑增加进线程,把耗时的任务给进线程去做

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VR全景制作的流程?VR全景制作可以用在哪些领域?

VR全景制作的流程&#xff1f;VR全景制作可以用在哪些领域&#xff1f;VR全景制作&#xff1a;流程、应用与未来虚拟现实&#xff08;VR&#xff09;全景制作正迅速改变我们的感官体验&#xff0c;使我们能够身临其境地探索虚拟世界&#xff0c;享受沉浸式的奇妙感受。那么&…

用LangChain重构客服系统:腾讯云向量数据库+GPT-4o实战

人们眼中的天才之所以卓越非凡&#xff0c;并非天资超人一等而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。———— 马尔科姆格拉德威尔 目录 一、传统客服系统痛点与重构价值 1.1 传统方案瓶颈分析 1.2 新方案技术突破点 二、系统架构设计&…

主要分布在腹侧海马体(vHPC)CA1区域(vCA1)的混合调谐细胞(mixed-tuning cells)对NLP中的深层语义分析的积极影响和启示

腹侧海马体CA1区&#xff08;vCA1&#xff09;的混合调谐细胞&#xff08;mixed-tuning cells&#xff09;通过整合情感、社会关系、空间概念等多模态信息&#xff0c;形成动态的情景化语义表征&#xff0c;为自然语言处理&#xff08;NLP&#xff09;的深层语义分析提供了重要…

ESP32的ADF详解:6. Audio Processing的API

一、Downmix 1. 核心功能 将基础音频流和新加入音频流混合为单一输出流&#xff0c;支持动态增益控制和状态转换。输出声道数与基础音频一致&#xff0c;新加入音频自动转换声道匹配。2. 关键特性声道处理 输出声道数 基础音频声道数新加入音频自动转换声道&#xff08;如立体…

Qt(基本组件和基本窗口类)

一、基本组件1. Designer设计师为什么要上来先将这个东西呢&#xff0c;这个是QT外置的设计界面的工具&#xff0c;没啥用&#xff0c;所以了解一下。我们用的多的是QT内置的界面设计&#xff0c;只需要我们双击我们创建的项目的.ui文件就可以进入这个界面&#xff0c;你对界面…

docker与k8s的容器数据卷

Docker容器数据卷 特性 docker镜像由多个只读层叠加而成&#xff0c;启动容器时&#xff0c;Docker会加载只读镜像层并在镜像栈顶部添加一个读写层。如果运行中的容器修改了现有的一个已经存在的文件&#xff0c;那么该文件将会从读写层下面的只读层复制到读写层&#xff0c;该…

自然语言处理技术应用领域深度解析:从理论到实践的全面探索

1. 引言:自然语言处理的技术革命与应用前景 自然语言处理(Natural Language Processing,NLP)作为人工智能领域的核心分支,正在以前所未有的速度改变着我们的数字化生活。从最初的规则基础系统到如今基于深度学习的大语言模型,NLP技术经历了从理论探索到实际应用的深刻变…

OpenGLRender开发记录(二): 阴影(shadowMap,PCF,PCSS)

目录已实现功能阴影shadowMapPCFPCSS实现shadowMapPCFPCSS阴影GitHub主页&#xff1a;https://github.com/sdpyy1 OpenGLRender:https://github.com/sdpyy1/CppLearn/tree/main/OpenGL 已实现功能 除了上次实现IBL之外&#xff0c;项目目前新增了imGUI的渲染&#xff0c;更方便…

Linux:日志乱码

1、Linux日志乱码可能是XShell客户端编码没设置为UTF-8引起的&#xff0c;按照以下步骤&#xff0c;设置终端格式&#xff1a;中文版&#xff1a;打开Xshell会话属性&#xff08;文件→属性→终端→编码&#xff09;&#xff0c;选择与服务器一致的编码格式&#xff08;如UTF-8…

Rouge:面向摘要自动评估的召回导向型指标——原理、演进与应用全景

“以n-gram重叠量化文本生成质量&#xff0c;为摘要评估提供可计算标尺” Rouge&#xff08;Recall-Oriented Understudy for Gisting Evaluation&#xff09; 是由 南加州大学信息科学研究所&#xff08;ISI&#xff09;的Chin-Yew Lin 于2004年提出的自动文本摘要评估指标&am…

[STM32][HAL]stm32wbxx 超声波测距模块实现(HY-SRF05)

前言 在电子技术应用中,距离测量是一个常见且重要的需求。超声波模块因其测量精度较高、成本较低、易于使用等优点,被广泛应用于机器人避障、液位检测、智能停车系统等领域。该文主要讲解以stm32wb芯片为主控,用HAL库来对HY-SRF05超声波模块进行代码编写,实现基本的驱动和测…

MySQL 性能调优实战指南:从诊断到优化全解析

引言在日常的数据库运维工作中&#xff0c;我们经常需要对 MySQL 数据库进行诊断和性能分析。本文将介绍一套全面的 MySQL 诊断脚本&#xff0c;适用于 MySQL 8.0&#xff08;兼容 8.0.15 及以上版本&#xff09;&#xff0c;涵盖事务锁分析、性能瓶颈定位、配置检查、连接状态…

8. 状态模式

目录一、应用背景二、状态模式2.1 解决的问题2.2 角色2.3 实现步骤三、通用设计类图四、实现4.1 设计类图4.2 状态转换图4.3 代码实现一、应用背景 某对象发生变化时&#xff0c;其所能做的操作也随之变化。应用程序的可维护性和重用性差代码的逻辑较复杂 二、状态模式 2.1 …

php语法--foreach和in_array的使用

文章目录foreach基础语法&#xff1a;案例1&#xff1a;引用传递模式&#xff1a;嵌套数组处理&#xff1a;避免在循环中计算数组长度&#xff1a;使用引用减少内存拷贝&#xff1a;打印数组in_array基础使用严格使用foreach 基础语法&#xff1a; foreach ($iterable as $va…

ES6模块详解:核心语法与最佳实践

以下是 EMAScript 6&#xff08;ES6&#xff09;模块规范的核心要点及细节解析&#xff1a; &#x1f4e6; 一、核心语法导出&#xff08;export&#xff09; 命名导出&#xff1a;支持导出多个具名成员。export const a 1; export function b() { /* ... */ } // 或集中导出 …

Python day25

浙大疏锦行 Python day25. 内容&#xff1a; 异常处理&#xff0c;在日常的编码工作过程中&#xff0c;为了避免由于各种bug导致的异常情况&#xff0c;我们需要引入异常处理机制&#xff0c;它的工作场景是当程序运行出现意外时&#xff0c;可以根据编码规则处理响应的错误。…

mac llama_index agent算术式子计算示例

本文通过简单数学计算&#xff0c;示例llama_index使用agent解决复杂任务过程。 假设mac本地llama_index环境已安装&#xff0c;过程参考 mac测试ollama llamaindex-CSDN博客 测试mac笔记本内存8G&#xff0c;所以使用较小LLM完成示例。 ollama pull qwen3:1.7b qwen3:1.7b能…

uni-app小程序云效持续集成

创建项目 必须是 cli 命令行创建的 uni-app 小程序项目参考uni-app官方构建命令&#xff1a; npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project生成小程序代码上传密钥 管理-开发设置-小程序代码上传生成的文件放在根目录即可 安装持续集成插件 pnpm install uni-mi…

uniapp+高德地图实现打卡签到、打卡日历

一、注册高德地图。应用管理创建应用&#xff0c;分别添加Andriod平台、Web服务、Web端、微信小程序四种类型的key。二、考勤规则打卡地点选择位置代码&#xff1a;<script setup lang"ts"> import { onMounted, onUnmounted, reactive, ref, watchEffect } fr…

CentOS 7.9 + GCC9 离线安装 IWYU(Include What You Use)

本教程适用于 离线环境下在 CentOS 7.9 系统中使用 GCC 9 离线安装 IWYU 的完整步骤&#xff0c;涵盖 Clang 11.1.0 编译、IWYU 构建以及头文件自动优化流程。&#x1f4e5; 一、准备安装包请提前下载以下源码包&#xff08;可通过在线机器提前下载&#xff0c;再传输到离线环境…