C++实现基于http协议的epoll非阻塞模型的web服务器框架(支持访问服务器目录下文件的解析)
创始人
2025-01-20 15:07:47
0

使用方法:

编译

例子:./httpserver 9999 ../ htmltest/

可执行文件  +端口 +要访问的目录下的

例子:http://192.168.88.130:9999/luffy.html

前提概要

http协议 :应用层协议,用于网络通信,封装要传输的数据,通过http协议组织的数据最终会是一个数据块多行数据,换行需要 \r\n

通信流程:

客户端:通过使用http传输数据发送给服务器    通过http协议组织数据→得到一个字符串→发送给服务器    接受数据→根据http协议解析→得到原始数据→处理  服务器端:    接受数据→通过http协议解析→得到原始数据→处理    回复数据→通过http协议组织数据→得到一个字符串→发送给客户端 

http协议分成两部分:

http请求:

客户端发送给服务器的一种数据格式 

http响应:

服务器端回复客户端的一种格式 

http请求

客户端给服务器发送的一种数据格式,可以分为四部分

1.请求行     指定提交数据的方式    有两种提交的方式:      **get**:简单       **post**:复杂  2. 请求头     多个键值对    客户端给服务器发送的身份的描述符  3. 空行   4.请求的数据向服务器提交的数据 
这是网页用GET 发过来的请求

第一行:请求行用的GET

第一部分:GET :提交的数据的方式

第二部分:中间的橙色的字符

/ :访问的服务器资源目录,/ →代表资源根目录  ? :后面的内容:客户端向服务器端提交的数据    key=value 

第三部分 :HTTP/1.1 →http协议的版本

第二-第八行: 请求头

若干个键值对,每一个键值对占一行,使用\r\n换行

第九行是:空行

用post请求

第一行:请求行

post:提交数据的方式

/:作为客户端访问了服务器的什么目录,资源的根目录

http 、1.1http协议的版本

第二行-12请求头

第13行:空行

第14 行:客户端向服务器提交的数据

GET与POST的区别

功能上:

get

作为客户端向服务器申请访问静态资源(网页,图片,文件) 

post :

向服务器提交动态数据    用户登录信息    上传下载文件 

从操作的数据量来说:

get:

比较少,使用get向服务器提交的数据在请求行的第二部分  在请求第二部分的时候需要显示到浏览器的地址栏中  浏览器的地址栏的缓存很小,谷歌默认7k左右,数据量小 

post :

可以操作大数据    文件上传(大文件)  post 提交数据放到了请求协议的第四部分 

安全性:

get :

提交的数据会显示到浏览器的地址栏中,容易泄露 

post :

不会泄露,提交数据不再浏览器的地址栏中 

http响应

服务器给客户端回复数据

http响应的组成部分→4个部分

状态行

响应头(包头)

n个键值对  里面的信息是服务器发送给客户端 

空行

响应的数据,根据客户端请求给客户端回复的数据

第一行 :状态行

HTTP 、1.1 http协议版本

200:状态码

ok :对应状态码的描述

第二-九行 :响应头

content-type :服务器给客户端的数据快的格式==http协议的第四块的数据格式

text、plain→纯文本  charset =iso-8859-1→数据的字符编码    iso-8859-1→不支持中文    utf 支持中文 

content-length :服务器给客户端的数据快的长度==http协议的第四块的数据块的长度,总字节数;不知道写-1;

http状态码:

3.web服务器实现

 客户端:浏览器

通过浏览器访问服务器: -访问方式:

服务器的IP地址:端口 应用层协议使用:http,数据需要在浏览器端使用该协议进行包装响应消息的处理也是浏览器完成的 => 程序猿不需要管-客户端通过ur1访问服务器资源

-客户端访问的路径:http://192.168.1.100:8989/或者http://192.168.1.100:8989

**[访问服务器提供的资源目录的根目录](http://192.168.1.100:8989/或者http://192.168.1.100:8989访问服务器提供的资源目录的根目录)**  并不是服务器的 / 目录 

#### 服务器端:

提供服务器,让客户端访问

支持多客户端访问

-使用I0多路转接=>epo11

客户端发送给的请求消息是基于http的 -需要能够解析http请求 服务器回复客户端数据,使用http协议封装回复的数据=>http响应

服务器端需要提供一个资源目录,目录中的文件可以供客户端访问

客户端访问的文件没有在资源目录中,就不能访问了

假设服务器端提供的目录:/home/robin/luffy 

 代码展示

main()函数

/*************************************************************************     > File Name: main.cpp     > Author:Wux1aoyu     >       > Created Time: Fri 17 May 2024 05:02:16 AM PDT  ************************************************************************/  #include"sever.h" using namespace std; // 原则上 main 函数只是逻辑函数调用,具体的内容不会写在这里面 //代码量少 int main(int argc,char *argv[]){     //启动服务器->epoll     if(argc<3){         cout<<"./a.out port path\n"<

头文件

 #ifndef SERVER_H #define SERVER_H  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  using namespace std;  #ifdef __cplusplus extern "C" { #endif  // 初始化监听的文件描述符 int initlistenFd(unsigned short port);  // 启动 epoll 模型 int epollrun(unsigned short port);  // 建立新连接 int acceptConn(int lfd, int epfd);  // 接收 HTTP 请求 int recvHttprequest(int cfd, int epfd);  // 解析请求行 int parserequestline(const char *requline, int cfd);  // 发送头信息 int sendHeadmsg(int cfd, int status, const char *descr, const char *type, int length);  //发送目录 int senddir(int cfd,char*dirname);  // 发送文件 int sendFile(int cfd, const char *file);  // 断开连接 int disconnect(int cfd, int epfd);   #ifdef __cplusplus } #endif  #endif // SERVER_H 

 服务器端: sever.cpp

#include"sever.h" //初始化监听套接字 int initlistenFd(unsigned short port) {     //1.创建监听的套接字     int lfd=socket(AF_INET,SOCK_STREAM,0);     if(lfd==-1){         perror("socket");         return -1;     }      //2. 端口复用     //如果服务器主动断开链接,那么将会进入TIME_WAIT 状态,等待2msl,这个时间太长了,所以就设置端口复用,继续使用端口复用,使客户端用这个端口链接,但是上一个仍处于TIME_WAIT      int opt=1;     int ret = setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));     if(ret==-1){         perror("ret");     }      //3.绑定     //设置文件描述符的地址ip端口     struct sockaddr_in addr;     addr.sin_family=AF_INET;//IPV4     addr.sin_port=htons(port);     addr.sin_addr.s_addr=INADDR_ANY; //0地址          ret=bind(lfd,(sockaddr*)&addr,sizeof(addr));     if(ret==-1){         perror("bind");         return -1;     }      //4.设置监听     ret=listen(lfd,128);      if(ret==-1){         perror("listen");         return -1;     }          //5.返回可用的监听的套接字     return lfd;  }  //启动epoll模型 int epollrun(unsigned short port){     //初始化epoll模型     int epfd=epoll_create(1000);//创建epoll树     if(epfd==-1){         perror("create");         return -1;     }      //初始化epoll树,将监听lfd添加上树     int lfd=initlistenFd(port);      struct epoll_event ev;//事件结构体     ev.events=EPOLLIN;//检查读事件     ev.data.fd=lfd;//将lfd添加属性中     //添加上树     int ret=epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);     if(ret==-1){         perror("epoll_ctl-add");         return -1;     }      //检测,循环检测,边沿ET模式,epoll非阻塞     struct epoll_event evs[1024];     int size=sizeof(evs)/sizeof(int);     int flag =0;     while (1)     {         if(flag==1){             break;         }         int num=epoll_wait(epfd,evs,size,0);//非阻塞进行         //遍历发生可读事件的变化的数组         for (int  i = 0; i < num; i++)         {             int curfd=evs[i].data.fd;//临时变量找到变化的文件描述符             if(curfd==lfd)//如果使监听套接字发生变化,一定是客户端请求链接             {                 //建立链接               int ret= acceptConn(curfd,epfd);               if(ret==-1){                 //建立链接失败直接终止程序                 flag=1;                 break;               }              }             else{                 //通信//接受http请求                 recvHttprequest(curfd,epfd);             }                      }              }            return 0; }  //和客户端建立新连接,并且将通信文件描述符设置成非阻塞属性 int acceptConn(int lfd,int epfd){      //建立链接     int cfd=accept(lfd,NULL,NULL);     if(cfd==-1){         perror("accept");         return -1;     }      //设置通信文案描述属性为非阻塞     int flag=fcntl(cfd,F_GETFL);     flag|=O_NONBLOCK;     fcntl(cfd,F_SETFL,flag);      //通信套接字添加到epoll模型上     struct epoll_event ev;     ev.data.fd=cfd;     ev.events=EPOLLIN | EPOLLET;//事件为边沿属性,检查读缓冲区;     int ret =epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);     if(ret==-1){         perror("epoll_ctl");         return -1;     }  }  //和客户端断开新链接 int disconnect(int cfd,int epfd){      //将节点从epoll模型删除     int ret =epoll_ctl(epfd,EPOLL_CTL_DEL,cfd,NULL);//删除操作最后一个制空     if(ret==-1){         perror("epoll_ctl_del");         return -1;     }     //关闭通信套接字     close(cfd);     return 0; } //接受客户端http的请求消息 int recvHttprequest(int cfd,int epfd){     //因为是边沿非阻塞模型,所以要一次性循环读      char tmp[1024];//每次读1k数据     char buf[4096];//每次把读的数据存到这个缓冲区里面     //循环读数据     int len,total=0;//total 是当前的buf的数据     //客户端申请的都是静态资源,请求的资源内容,在请求行的第二部分     //只需将请求完整的保存下来就可以     //不需要解析请求头的数据,因此接受到之后不储存也是没问题的      while((len=recv(cfd,tmp,sizeof(tmp),0))>0){         if(len+total这里只需两个键值对     //content-type /content-length   https://tool.oschina.net/commons去这里查      sprintf(buf + strlen(buf), "Content-Type: %s\r\n", type);     sprintf(buf + strlen(buf), "Content-Length: %d\r\n\r\n", length);      // 空行     //拼接完成之后发送     send(cfd,buf,strlen(buf),0);//非阻塞       return 0;  }  int sendFile(int cfd,const char *file){      //读文件,发送给客户端     //在发送内容之前应该有状态+消息包头,+空行+文件内容     //这四部分数据组织好之后再发送数据吗?     //不是 为什么,因为传输层默认人是tcp的     //面向连接的流式传输协议-》只有最后全部发送完就可以      int fd=open(file,O_RDONLY);//只读     while (1)     {         char buf[1024];         int len=read(fd,buf,sizeof(buf));         if(len>0){             //发送读出的数据             send(cfd,buf,len,0);         }         else if(len==0){             //文件读完了             break;         }         else{             perror("read");             return -1;         }              }       return 0; }  

相关内容

热门资讯

妙计辅助!创思维激k有挂吗(辅... 妙计辅助!创思维激k有挂吗(辅助挂)切实存在有辅助脚本(果真有挂)创思维激k有挂吗辅助器是一种具有地...
记者获悉!aapoker破解侠... 记者获悉!aapoker破解侠是真的吗,来玩app破解版,练习教程(有挂讲解)1、很好的工具软件,可...
学习辅助!新道游辅助软件下载(... 学习辅助!新道游辅助软件下载(辅助挂)竟然真的有辅助方法(有挂透视)1、打开软件启动之后找到中间准星...
黑科技代打!wepoker模拟... 黑科技代打!wepoker模拟器哪个,wepoker可以免费玩吗,绝活教程(有挂细节)wepoker...
办法辅助!wepoker修改工... 办法辅助!wepoker修改工具(辅助挂)都是真的有辅助工具(有挂教程)1、在wepoker修改工具...
明白辅助挂!佛手在线有挂吗,w... 明白辅助挂!佛手在线有挂吗,wepoker透视功能下载,积累教程(有挂猫腻)1、完成wepoker透...
要领辅助!老友辅助工具下载(辅... 要领辅助!老友辅助工具下载(辅助挂)果然真的有辅助技巧(真实有挂)1、完成老友辅助工具下载辅助器v3...
此事迅速冲上热搜!sohoo ... 此事迅速冲上热搜!sohoo poker辅助,来玩app破解版,指南书教程(有挂攻略)1、金币登录送...
举措辅助!牛总管辅助神器(辅助... 举措辅助!牛总管辅助神器(辅助挂)确实确实有辅助软件(有挂透明挂)进入游戏-大厅左侧-新手福利-激活...
值得注意的是!wepoker透... 值得注意的是!wepoker透视脚本免费,sohoo开挂辅助,诀窍教程(有挂教程)wepoker透视...