LINUX高性能服务器框架

1.服务器模型:

1). C/S模型

 :


  此模型就是一个服务器能给多个客户端提供服务,但所以资源都被服务端所占有,客户端想要获取只能通过请求连接服务端去获取。由于客户端的请求访问是异步的,所以需要一个手段进行此类事件的监听,比如SELECT或者是EPOLL,可以只监听服务器套接字,将客户端的客户需求交给子进程去处理,也可以进行同时监听,将连接对面的套接字加入,进行一起监听。

2).P2P模式

 此模型放弃了以服务器为中心的观点,改为每个电脑的地位相同,既可以作为客户端,也可以作为服务端。

 

2.服务器大致框架:

不同差别服务器,其差别一般都于逻辑处理,但大体框架是一样的,如下图:

3.io模型:

I/O模型一般有如下几种:

阻塞I/O:即当客户端发起连接以后,服务端成功连接,但是服务/客户端因为某些原因无法立刻执行,从而被操作系统挂起,直到有消息或者要处理时,才会从等待队列中唤醒。可能被阻塞的I/O有accept(),connect(),send(),recv()等等。

非阻塞I/O:此类不会一直等待,比如可以设置一段事件进行等待,事件发生就立即进行返回。

同步/异步 I/O

4:两种高效的I/O模型:

1.): reactor模型(同步模型):

如上图:就绪事件准备好之后插入就绪队列,然后唤醒请求队列进行不同的工作状态进行工作。

 

以同步I/O模拟proactor模式

注意如此,用同步I/O来模拟异步I/O时,发生读事件,将数据进行封装然后储存在队列中,然后注册写事件就绪,而写事件则是队列取出进行回馈返回。

5.高效的并发模式(主要运用线程池):
1).半同步半异步:

半同步半异步指的就是同步用来处理用户逻辑,异步用来处理I/O事件:

但是上述模型有缺点:

8-11改良后的半同步/半异步模式,每个工作线程都可以通过epoll_wait()。来监听多个SOCKET进行服务。

2).领导/追逐者模式:读者能力有限,请自行 查阅资料

6.优先状态机,用来处理单元之间的逻辑问题:比如先处理什么,再处理什么,接着处理什么。

想象一个如下有限状态机:

下面是利用上述原理进行的分析HTTP请求的状态机。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>#define BUFFER_SIZE 4096
enum CHECK_STATE { CHECK_STATE_REQUESTLINE = 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT };
enum LINE_STATUS { LINE_OK = 0, LINE_BAD, LINE_OPEN };
enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, FORBIDDEN_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION };
static const char* szret[] = { "I get a correct result\n", "Something wrong\n" };LINE_STATUS parse_line( char* buffer, int& checked_index, int& read_index )
{char temp;for ( ; checked_index < read_index; ++checked_index ){temp = buffer[ checked_index ];if ( temp == '\r' ){if ( ( checked_index + 1 ) == read_index )//对面传来一行数据 这“一行”数据 可能是属于HTTP头部的一部分 还没到"\n",这里判断是read_index是消息行末尾的后一个字节,checked_index已经读到对面发过来末尾了{return LINE_OPEN;}else if ( buffer[ checked_index + 1 ] == '\n' ){buffer[ checked_index++ ] = '\0';buffer[ checked_index++ ] = '\0';return LINE_OK;}return LINE_BAD;}else if( temp == '\n' ){if( ( checked_index > 1 ) &&  buffer[ checked_index - 1 ] == '\r' ){buffer[ checked_index-1 ] = '\0';buffer[ checked_index++ ] = '\0';return LINE_OK;}return LINE_BAD;}}return LINE_OPEN;
}HTTP_CODE parse_requestline( char* szTemp, CHECK_STATE& checkstate )
{char* szURL = strpbrk( szTemp, " \t" );if ( ! szURL ){return BAD_REQUEST;}*szURL++ = '\0';char* szMethod = szTemp;if ( strcasecmp( szMethod, "GET" ) == 0 ){printf( "The request method is GET\n" );}else{return BAD_REQUEST;}szURL += strspn( szURL, " \t" );char* szVersion = strpbrk( szURL, " \t" );if ( ! szVersion ){return BAD_REQUEST;}*szVersion++ = '\0';szVersion += strspn( szVersion, " \t" );if ( strcasecmp( szVersion, "HTTP/1.1" ) != 0 ){return BAD_REQUEST;}if ( strncasecmp( szURL, "http://", 7 ) == 0 ){szURL += 7;szURL = strchr( szURL, '/' );}if ( ! szURL || szURL[ 0 ] != '/' ){return BAD_REQUEST;}//URLDecode( szURL );printf( "The request URL is: %s\n", szURL );checkstate = CHECK_STATE_HEADER;return NO_REQUEST;
}HTTP_CODE parse_headers( char* szTemp )
{if ( szTemp[ 0 ] == '\0' ){return GET_REQUEST;}else if ( strncasecmp( szTemp, "Host:", 5 ) == 0 ){szTemp += 5;szTemp += strspn( szTemp, " \t" );printf( "the request host is: %s\n", szTemp );}else{printf( "I can not handle this header\n" );}return NO_REQUEST;
}HTTP_CODE parse_content( char* buffer, int& checked_index, CHECK_STATE& checkstate, int& read_index, int& start_line )
{LINE_STATUS linestatus = LINE_OK;HTTP_CODE retcode = NO_REQUEST;while( ( linestatus = parse_line( buffer, checked_index, read_index ) ) == LINE_OK ){char* szTemp = buffer + start_line;start_line = checked_index;//连续两次++ switch ( checkstate ){case CHECK_STATE_REQUESTLINE:{retcode = parse_requestline( szTemp, checkstate );if ( retcode == BAD_REQUEST ){return BAD_REQUEST;}break;}case CHECK_STATE_HEADER:{retcode = parse_headers( szTemp );if ( retcode == BAD_REQUEST ){return BAD_REQUEST;}else if ( retcode == GET_REQUEST ){return GET_REQUEST;}break;}default:{return INTERNAL_ERROR;}}}if( linestatus == LINE_OPEN ){return NO_REQUEST;}else{return BAD_REQUEST;}
}int main( int argc, char* argv[] )
{if( argc <= 2 ){printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );return 1;}const char* ip = argv[1];int port = atoi( argv[2] );struct sockaddr_in address;bzero( &address, sizeof( address ) );address.sin_family = AF_INET;inet_pton( AF_INET, ip, &address.sin_addr );address.sin_port = htons( port );int listenfd = socket( PF_INET, SOCK_STREAM, 0 );assert( listenfd >= 0 );int ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );assert( ret != -1 );ret = listen( listenfd, 5 );assert( ret != -1 );struct sockaddr_in client_address;socklen_t client_addrlength = sizeof( client_address );int fd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );if( fd < 0 ){printf( "errno is: %d\n", errno );}else{char buffer[ BUFFER_SIZE ];memset( buffer, '\0', BUFFER_SIZE );int data_read = 0;int read_index = 0;int checked_index = 0;int start_line = 0;CHECK_STATE checkstate = CHECK_STATE_REQUESTLINE;while( 1 ){data_read = recv( fd, buffer + read_index, BUFFER_SIZE - read_index, 0 );if ( data_read == -1 ){printf( "reading failed\n" );break;}else if ( data_read == 0 ){printf( "remote client has closed the connection\n" );break;}read_index += data_read;HTTP_CODE result = parse_content( buffer, checked_index, checkstate, read_index, start_line );if( result == NO_REQUEST ){continue;}else if( result == GET_REQUEST ){send( fd, szret[0], strlen( szret[0] ), 0 );break;}else{send( fd, szret[1], strlen( szret[1] ), 0 );break;}}close( fd );}close( listenfd );return 0;
}

只需注意一点的是通过while循环不断读取请求,根据读取数据进行请求行和消息头的寻找和分析。

下面是简单的,通过epoll_wait()调用的有限状态机(四种情况)和recator模型,将监听到的事件分为读写事件的简单服务端,代码如下:

//recator
#include <sys/socket.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <iostream>
#include <netinet/in.h>
#include <assert.h>const int MAX_EVENT=1000;
const int MAX_BUFSIZE=1024;
const int clinetnum=99999;
int epollfd=0;
struct clinet_data;typedef int(*REPLAY_LETTER)(int,clinet_data*);
void epollset(int,int);
void clintset(clinet_data*,int);struct clinet_data{int fd;char bufread[MAX_BUFSIZE];char bufwrite[MAX_BUFSIZE];int readend;int writeend;REPLAY_LETTER readevent;REPLAY_LETTER writevent;REPLAY_LETTER closeevent;clinet_data():fd(0),readend(0),writeend(0){};
};clinet_data* dataset=new clinet_data[clinetnum];void closeevent(int sockfd){close(sockfd);std::cout<<"close sockfd :"<<sockfd<<std::endl;
}
void epoll_event_change(int sockfd,int mode){switch (mode){case 1:{epoll_ctl(epollfd,EPOLL_CTL_DEL,sockfd,NULL);break;}case 2:{epoll_event ee;ee.data.fd=sockfd;ee.events=EPOLLOUT;epoll_ctl(sockfd,EPOLL_CTL_MOD,sockfd,&ee);break;}case 3:{epoll_event ee;ee.data.fd=sockfd;ee.events=EPOLLIN;epoll_ctl(sockfd,EPOLL_CTL_MOD,sockfd,&ee);  break;}case 4:{struct sockaddr_in addr;socklen_t size=sizeof(addr);int connect=accept(sockfd,(struct sockaddr*)&addr,&size);epollset(epollfd,connect);clintset(dataset,connect);break;}default:break;}
}
int readevent(int sockfd,clinet_data* dataset){//size<MAX_BUFFSIZE,事件改为写 然后发送回去int size=recv(sockfd,dataset[sockfd].bufread,MAX_BUFSIZE-1,0);dataset[sockfd].readend=size;dataset[sockfd].writeend=0;if(size==0){//对面关闭memset(dataset[sockfd].bufread,'\0',MAX_BUFSIZE);std::cout<<"nodata,over"<<std::endl;closeevent(sockfd);dataset[sockfd].fd=0;epoll_event_change(sockfd,1);return 0;}else{std::cout<<"recv data  :"<<dataset[sockfd].bufread<<std::endl;memcpy(dataset[sockfd].bufwrite,dataset[sockfd].bufread,size);memset(dataset[sockfd].bufread,'\0',MAX_BUFSIZE);epoll_event_change(sockfd,2);}return size;
}int writeevent(int sockfd,clinet_data* dataset){dataset[sockfd].writeend=dataset[sockfd].readend;dataset[sockfd].readend=0;int count=send(sockfd,dataset[sockfd].bufwrite,dataset[sockfd].writeend,0);epoll_event_change(sockfd,3);return count;
}void  clintset(clinet_data* con,int fd){con[fd].readevent=readevent;con[fd].writevent=writeevent;con[fd].fd=fd;
}int setsocknoblock(int fd){
int old=fcntl(fd,F_GETFL);
int newfd=old|O_NONBLOCK;
fcntl(old,F_SETFL,newfd);
return old;
}void epollset(int epollfd,int fd){epoll_event even;even.data.fd=fd;even.events=EPOLLET|EPOLLIN;epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&even);setsocknoblock(fd);
}int main(int argc,char* argv[]){if(argc<2){std::cout<<"not enough"<<std::endl;exit(1);}int ret=0;struct sockaddr_in address;epoll_event events[MAX_EVENT]; memset(&address,0,sizeof(address));address.sin_family=AF_INET;address.sin_addr.s_addr=inet_addr("127.0.0.1");address.sin_port=htons(atoi(argv[1]));int severfd=socket(PF_INET,SOCK_STREAM,0);assert(severfd>=0);epollfd=epoll_create(5);epollset(epollfd,severfd);ret=bind(severfd,(struct sockaddr*)&address,sizeof(address));assert(ret>=0);ret=listen(severfd,5);assert(ret>=0);clinet_data* dataset=new clinet_data[clinetnum];while(1){int numm=epoll_wait(epollfd,events,MAX_EVENT-1,5*1000);if(numm==0){std::cout<<"timeout"<<std::endl;}else if(numm<0){std::cout<<"epoll_wait error"<<std::endl;exit(1);}else{for(int i=0;i<numm;i++){int fd=events[i].data.fd;if(events[i].events&EPOLLIN){if(fd==severfd){epoll_event_change(fd,4);}else{readevent(fd,dataset);}}else if(events[i].events&EPOLLOUT){writeevent(fd,dataset);}}}
}
delete [] dataset;
close(severfd);
return 0;
}

结合客户端代码(输入Q后,客户端断开自行测试:(作者已经测试过了,没有问题)。

#include<iostream>
#include<sys/socket.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
const int a=1024;
using namespace std;
int main(int argc,char* argv[]){char message[a];
struct sockaddr_in times;
if(argc!=3){cout<<"error()"<<endl;exit(1);
}
int cilsock;
int str_len;
cilsock=socket(PF_INET,SOCK_STREAM,0);
if(cilsock==-1){cout<<"socket()error"<<endl;
}
memset(&times,0,sizeof(times));
times.sin_family=AF_INET;
times.sin_addr.s_addr=inet_addr(argv[1]);
times.sin_port=htons(atoi(argv[2]));
if(connect(cilsock,(struct sockaddr *)&times,sizeof(times))==-1){
cout<<"connext()error()"<<endl;
}else{
puts("connect........");
}while(1){fputs("input message",stdout);fgets(message,a,stdin);if(!strcmp(message,"q/n")||!strcmp(message,"Q/n"))break;write(cilsock,message,strlen(message));str_len=read(cilsock,message,a-1);message[str_len]=0;cout<<message<<endl;
}
close(cilsock);
return 0;}

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

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

相关文章

基于微信小程序的课堂考勤系统的设计与实现(论文+源码)_kaic

基于微信小程序的课堂考勤系统的设计与实现 摘 要 在高校教育普及的今天&#xff0c;学生人数日益增多&#xff0c;为保证课堂质量&#xff0c;教师多要在课前进行考勤。因此本设计提出基于微信小程序的课堂考勤系统&#xff0c;增加了定位功能&#xff0c;避免了“假打卡”…

前端开发知识(一)-html

1.前端开发需掌握的内容&#xff1a; 2.前端开发的三剑客&#xff1a;html、css、javascript Vue可以简化JavaScpript流程。 Element&#xff08;饿了么开发的&#xff09; &#xff1a;前端组件库。 Ngix&#xff1a;前端服务器。 3.前端开发工具&#xff1a;vscode 1)按…

基于Java+SpringBoot+Vue的学生心理咨询评估系统

前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 哈喽兄弟们&#xff0c;好久不见哦&#xff5…

乐尚代驾六订单执行一

加载当前订单 需求 无论是司机端&#xff0c;还是乘客端&#xff0c;遇到页面切换&#xff0c;重新登录小程序等&#xff0c;只要回到首页面&#xff0c;查看当前是否有正在执行订单&#xff0c;如果有跳转到当前订单执行页面 之前这个接口已经开发&#xff0c;为了测试&…

Linux嵌入式学习——数据结构——线性表的链式结构

线性表的链式存储 解决顺序存储的缺点&#xff0c;插入和删除&#xff0c;动态存储问题。 特点&#xff1a; 线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素&#xff0c;存储单元可以是连续的&#xff0c;也可以不连续。可以被存储在任意内存未被占…

四、单线程多路IO复用+多线程业务工作池

文章目录 一、前言1 编译方法 二、单线程多路IO复用多线程业务工作池结构三、重写Client_Context类四、编写Server类 一、前言 我们以及讲完单线程多路IO复用 以及任务调度与执行的C线程池&#xff0c;接下来我们就给他结合起来。 由于项目变大&#xff0c;尝试解耦项目&#…

47.简易电压表的设计与验证(2)

&#xff08;1&#xff09;Verilog 代码&#xff1a; module adc_collect(input clk ,input reset_n ,input [7:0] adc_data ,output clk_adc );wire clk_adc_a ;…

提交高通量测序处理数据到 GEO --- 操作流程

❝ 写在前面 由于最近在提交课题数据到 NCBI 数据库&#xff0c;整理了相关笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. 提交高通量测序数据到 GEO --- 说明书 2. 提交高通量测序原…

基于机器学习的二手房价格分析与预测设计与实现

概述 随着西安房地产市场的不断发展和变化&#xff0c;对二手房价格的准确预测变得至关重要。本研究旨在利用机器学习技术对西安市二手房价格进行深入分析与预测&#xff0c;通过对原始数据进行数据预处理和特征提取&#xff0c;以构建有效的预测模型。通过数据分析和可视化&a…

LabVIEW学习-LabVIEW处理带分隔符的字符串从而获取数据

带分隔符的字符串很好处理&#xff0c;只需要使用"分隔符字符串至一维字符串数组"函数或者"一维字符串数组至分隔符字符串"函数就可以很轻松地处理带分隔符地字符串。 这两个函数所在的位置为&#xff1a; 函数选板->字符串->附加字符串函数->分…

第13周 简历职位功能开发与Zookeeper实战

第13周 简历职位功能开发与Zookeeper实战 本章概述1. Mysql8窗口函数over使用1.1 演示表结构与数据1.2 案例1:获取男女总分数1.3 案例2****************************************************************************************本章概述 1. Mysql8窗口函数over使用 参考案例…

从零开始学习机器学习,掌握AI未来的关键!

从零开始学习机器学习 1. 介绍1.1 人工智能&#xff08;AI&#xff09;概述1.2 机器学习在人工智能中的应用1.3 机器学习基础概念 2. 监督学习2.1 什么是监督学习2.2 回归分析2.3 分类问题2.4 模型评估和选择 3. 无监督学习3.1 什么是无监督学习3.2 聚类算法3.3 降维技术 4. 深…

Elasticsearch 7.x入门学习-Java API操作

1 创建项目 在idea开发工具中创建Maven项目 修改 pom 文件&#xff0c;增加 Maven 依赖关系 <dependencies><dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.8.0</versi…

C语言 | Leetcode C语言题解之第268题丢失的数字

题目&#xff1a; 题解&#xff1a; /* 求和运算 */ /* 对[0,n]求和, 减去数组每个元素, 得出丢失的元素 */ int missingNumber(int* nums, int numsSize){int i;int sum numsSize;for (i 0; i < numsSize; i) {sum i - nums[i];}return…

每天五分钟计算机视觉:目标检测模型从RCNN到Fast R-CNN的进化

本文重点 前面的课程中,我们学习了RCNN算法,但是RCNN算法有些慢,然后又有了基于RCNN的Fast-RCNN,Fast R-CNN是一种深度学习模型,主要用于目标检测任务,尤其在图像中物体的识别和定位方面表现出色。它是R-CNN系列算法的一个重要改进版本,旨在解决R-CNN中计算量大、速度慢…

python-阶乘和(赛氪OJ)

题目描述 求Sn​1!2!3!4!5!⋯n!的值&#xff0c;其中 &#x1d45b;n 是一个数字。输入格式&#xff1a; 输入一个整数 n。输出格式&#xff1a; 输出对应的 Sn​。 样例输入输出样例输入 5样例输出 153数据范围 对于 100% 的数据&#xff0c;保证1≤n≤20。来源/分类&#xff…

接口自动化测试框架实战-4-日志方法封装

上一小节我们讲解了文件读写方法的封装方法&#xff0c;本小节我们完成一下框架日志的封装方法。 首先我们讲解一下日志封装和日志记录有哪些用处&#xff1f; 更加方便的设置日志的格式和输出方式全局方法可以各个类/函数中都能统一调用分类记录接口用例执行日志&#xff0c…

【CPS出版】2024年智能计算与数据分析国际学术会议(ICDA 2024,9月6日-8)

为探讨数据科学和计算智能领域的关键问题&#xff0c;促进相关交流&#xff0c;2024年智能计算与数据分析国际学术会议&#xff08;ICDA 2024)将于2024年9月6日-8日在中国青岛召开。 本届会议拟邀请数据分析和计算智能领域的顶级专家、学者和产业界优秀人才&#xff0c;围绕当前…

【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !

目录 C语言中指针的大小1. 指针大小的基本概念1.1 32位系统1.2 64位系统 2. 指针大小示例2.1 32位系统输出2.2 64位系统输出 3. 指针大小与数据类型无关示例输出示例 4. 跨平台的指针大小示例输出示例 5. 关键点总结5.1 指针大小与平台关系5.2 跨平台编程注意事项 6. 指针大小示…

【QAC】Dashboard服务端如何配置

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决Dashboard服务端如何配置的问题。 2、 问题场景 客户想使用Dashboard&#xff0c;Dashboard服务端如何配置。 3、软硬件环境 1、软件版本&#xff1a;HelixQAC23.04 2、机器环境&#xff1a;Windows 64bit 3…