基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpRequest模块 解析http请求协议

本文主要是介绍基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpRequest模块 解析http请求协议,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、HTTP响应报文格式

HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0xf3c9743300024ee4
Cache-Control: private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Fri, 26 Feb 2021 08:44:35 GMT
Expires: Fri, 26 Feb 2021 08:44:35 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=13; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=33514_33257_33273_31660_33570_26350; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1614329075128412289017566699583927635684
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked

二、根据解析出的原始数据,对客户端的请求做出处理 processHttpRequest 

// 处理http请求协议
bool processHttpRequest(struct HttpRequest* req,struct HttpResponse* response);
// 处理基于get的http请求
bool processHttpRequest(struct HttpRequest* req,struct HttpResponse* response) {if(strcasecmp(req->method,"get") != 0) {return -1;}decodeMsg(req->url,req->url); // 解码字符串// 处理客户端请求的静态资源(目录或者文件)char* file = NULL;if(strcmp(req->url,"/") == 0) {file = "./";}else {file = req->url + 1;}// 获取文件属性struct stat st;int ret = stat(file,&st);if(ret == -1) {// 文件不存在 -- 回复404// sendHeadMsg(cfd,404,"Not Found",getFileType(".html"),-1);// sendFile("404.html",cfd);strcpy(response->fileName,"404.html");response->statusCode = NotFound;strcpy(response->statusMsg,"Not Found");// 响应头httpResponseAddHeader(response,"Content-Type",getFileType(".html"));response->sendDataFunc = sendFile;return 0;}strcpy(response->fileName,file);response->statusCode = OK;strcpy(response->statusMsg,"OK!");// 判断文件类型if(S_ISDIR(st.st_mode)) {// 把这个目录中的内容发送给客户端// sendHeadMsg(cfd,200,"OK",getFileType(".html"),-1);// sendDir(file,cfd);// 响应头httpResponseAddHeader(response,"Content-Type",getFileType(".html"));response->sendDataFunc = sendDir;}else {// 把文件的内容发送给客户端// sendHeadMsg(cfd,200,"OK",getFileType(file),st.st_size);// sendFile(file,cfd);// 响应头char tmp[12] = {0};sprintf(tmp,"%ld",st.st_size);httpResponseAddHeader(response,"content-type",getFileType(file));httpResponseAddHeader(response,"content-length",tmp);response->sendDataFunc = sendFile;}return 0;
}

1.解码字符串  

  • 解决浏览器无法访问带特殊字符的文件得到问题
// 将字符转换为整型数
int hexToDec(char c){if (c >= '0' && c <= '9')return c - '0';if (c >= 'a' && c <= 'f')return c - 'a' + 10;if (c >= 'A' && c <= 'F')return c - 'A' + 10;return 0;
}// 解码字符串
// to 存储解码之后的数据, 传出参数, from被解码的数据, 传入参数
void decodeMsg(char* to,char* from) {for(;*from!='\0';++to,++from) {// isxdigit -> 判断字符是不是16进制格式, 取值在 0-f// Linux%E5%86%85%E6%A0%B8.jpgif(from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])){// 将16进制的数 -> 十进制 将这个数值赋值给了字符 int -> char// B2 == 178// 将3个字符, 变成了一个字符, 这个字符就是原始数据// *to = (hexToDec(from[1]) * 16) + hexToDec(from[2]);*to = (hexToDec(from[1]) << 4) + hexToDec(from[2]);// 跳过 from[1] 和 from[2] ,因此在当前循环中已经处理过了from += 2;}else{// 字符拷贝,赋值*to = *from;}}*to = '\0';
}

2.判断文件扩展名,返回对应的 Content-Type(Mime-Type)

const char* getFileType(const char* name);
const char* getFileType(const char* name) {// a.jpg a.mp4 a.html// 自右向左查找 '.' 字符,如不存在返回NULLconst char* dot = strrchr(name,'.');if(dot == NULL) return "text/plain; charset=utf-8";//纯文本if(strcmp(dot,".html") == 0 || strcmp(dot,".htm") == 0) return "text/html; charset=utf-8";if(strcmp(dot,".jpg")==0 || strcmp(dot,".jpeg")==0) return "image/jpeg";if(strcmp(dot,".gif")==0)return "image/gif";if(strcmp(dot,".png")==0)return "image/png";if(strcmp(dot,".css")==0) return "text/css";if(strcmp(dot,".au")==0)return "audio/basic";if(strcmp(dot,".wav")==0)return "audio/wav";if(strcmp(dot,".avi")==0)return "video/x-msvideo";if(strcmp(dot,".mov")==0 || strcmp(dot,".qt")==0)return "video/quicktime";if(strcmp(dot,".mpeg")==0 || strcmp(dot,".mpe")==0)return "video/mpeg";if(strcmp(dot,".vrml")==0 || strcmp(dot,".wrl")==0)return "model/vrml";if(strcmp(dot,".midi")==0 || strcmp(dot,".mid")==0)return "audio/midi";if(strcmp(dot,".mp3")==0)return "audio/mpeg";if(strcmp(dot,".ogg") == 0) return "application/ogg";if(strcmp(dot,".pac") == 0)return "application/x-ns-proxy-autoconfig";if(strcmp(dot,".pdf") == 0)return "application/pdf";return "text/plain; charset=utf-8";//纯文本
}

3.发送文件  sendFile

// 发送文件
void sendFile(const char* fileName,struct Buffer* sendBuf,int cfd);
void sendFile(const char* fileName,struct Buffer* sendBuf,int cfd) {// 打开文件int fd = open(fileName,O_RDONLY);assert(fd > 0); 
#if 1while (1) {char buf[1024];int len = read(fd,buf,sizeof(buf));if(len > 0) {// send(cfd,buf,len,0);bufferAppendData(sendBuf,buf,len);}else if(len == 0) {break;}else{close(fd);perror("read");}}
#else// 把文件内容发送给客户端off_t offset = 0;int size = lseek(fd,0,SEEK_END);// 文件指针移动到了尾部lseek(fd,0,SEEK_SET);// 移动到文件头部while (offset < size){int ret = sendfile(cfd,fd,&offset,size - offset);printf("ret value: %d\n",ret);if (ret == -1 && errno == EAGAIN) {printf("没数据...\n");}}
#endifclose(fd);
}

4.发送目录

// 发送目录
void sendDir(const char* dirName,struct Buffer* sendBuf,int cfd); 
void sendDir(const char* dirName,struct Buffer* sendBuf,int cfd) {char buf[4096] = {0};sprintf(buf,"<html><head><title>%s</title></head><body><table>",dirName);struct dirent** nameList;int num = scandir(dirName,&nameList,NULL,alphasort);for(int i=0;i<num;i++) {// 取出文件名 nameList 指向的是一个指针数组 struct dirent* tmp[]char* name = nameList[i]->d_name;struct stat st;char subPath[1024] = {0};sprintf(subPath,"%s/%s",dirName,name);stat(subPath,&st);if(S_ISDIR(st.st_mode)) {// 从当前目录跳到子目录里边,/sprintf(buf+strlen(buf),"<tr><td><a href=\"%s/\">%s</a></td><td>%ld</td></tr>",name,name,st.st_size);}else{sprintf(buf+strlen(buf),"<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>",name,name,st.st_size);}// send(cfd,buf,strlen(buf),0);bufferAppendString(sendBuf,buf);memset(buf,0,sizeof(buf));free(nameList[i]); } sprintf(buf,"</table></body></html>");// send(cfd,buf,strlen(buf),0);bufferAppendString(sendBuf,buf);free(nameList);
}

三、解析http请求协议 parseHttpRequest  

可以看这篇关于httpResponsePrepareMsg函数具体是如何组织响应数据的:HttpResponse的定义和初始化 以及组织 HttpResponse 响应消息icon-default.png?t=N7T8https://blog.csdn.net/weixin_41987016/article/details/135464760

// 解析http请求协议
bool parseHttpRequest(struct HttpRequest* req,struct Buffer* readBuf,struct HttpResponse* response,struct Buffer* sendBuf,int socket);
// 解析http请求协议
bool parseHttpRequest(struct HttpRequest* req,struct Buffer* readBuf,struct HttpResponse* response,struct Buffer* sendBuf,int socket) {bool flag = true;while(req->curState!=ParseReqDone) {switch(req->curState) {case ParseReqLine:// 解析请求行flag = parseHttpRequestLine(req,readBuf);break;case ParseReqHeaders:// 解析请求头flag = parseHttpRequestHeader(req,readBuf);break;case ParseReqBody:break;default:break;}if(!flag) {return flag;}// 判断是否解析完毕了,如果完毕了,需要准备回复的数据if(req->curState==ParseReqDone) {// 1.根据解析出的原始数据,对客户端的请求做出处理processHttpRequest(req,response);// 2.组织响应数据并发送给客户端httpResponsePrepareMsg(response,sendBuf,socket);}}req->curState = ParseReqLine;// 状态还原,保证还能继续处理第二条及以后的请求return flag;
}

这篇关于基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpRequest模块 解析http请求协议的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/586257

相关文章

VScode连接远程Linux服务器环境配置图文教程

《VScode连接远程Linux服务器环境配置图文教程》:本文主要介绍如何安装和配置VSCode,包括安装步骤、环境配置(如汉化包、远程SSH连接)、语言包安装(如C/C++插件)等,文中给出了详... 目录一、安装vscode二、环境配置1.中文汉化包2.安装remote-ssh,用于远程连接2.1安装2

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

Linux中shell解析脚本的通配符、元字符、转义符说明

《Linux中shell解析脚本的通配符、元字符、转义符说明》:本文主要介绍shell通配符、元字符、转义符以及shell解析脚本的过程,通配符用于路径扩展,元字符用于多命令分割,转义符用于将特殊... 目录一、linux shell通配符(wildcard)二、shell元字符(特殊字符 Meta)三、s

Window Server创建2台服务器的故障转移群集的图文教程

《WindowServer创建2台服务器的故障转移群集的图文教程》本文主要介绍了在WindowsServer系统上创建一个包含两台成员服务器的故障转移群集,文中通过图文示例介绍的非常详细,对大家的... 目录一、 准备条件二、在ServerB安装故障转移群集三、在ServerC安装故障转移群集,操作与Ser

详解Java如何向http/https接口发出请求

《详解Java如何向http/https接口发出请求》这篇文章主要为大家详细介绍了Java如何实现向http/https接口发出请求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用Java发送web请求所用到的包都在java.net下,在具体使用时可以用如下代码,你可以把它封装成一

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

Java后端接口中提取请求头中的Cookie和Token的方法

《Java后端接口中提取请求头中的Cookie和Token的方法》在现代Web开发中,HTTP请求头(Header)是客户端与服务器之间传递信息的重要方式之一,本文将详细介绍如何在Java后端(以Sp... 目录引言1. 背景1.1 什么是 HTTP 请求头?1.2 为什么需要提取请求头?2. 使用 Spr

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

Python项目打包部署到服务器的实现

《Python项目打包部署到服务器的实现》本文主要介绍了PyCharm和Ubuntu服务器部署Python项目,包括打包、上传、安装和设置自启动服务的步骤,具有一定的参考价值,感兴趣的可以了解一下... 目录一、准备工作二、项目打包三、部署到服务器四、设置服务自启动一、准备工作开发环境:本文以PyChar