基于多反应堆的高并发服务器【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;
}

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

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

相关文章

Open3D (C++) 计算条件数

目录 一、算法原理1、条件数2、参考文献二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 1、条件数 条件数法是目前应用最为广泛的一种病态诊断方法。条件数的定义为:

嵌入式培训机构四个月实训课程笔记(完整版)-Linux网络编程第二天-TCP编程练习(物联技术666)

点赞+关注,功德无量。更多配套资料,欢迎私信。 百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.baidu.com/s/1F6BR6uTANKKcNnoaCUAZYA?p…

计算机毕业设计 基于SpringBoot的物资综合管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

安卓Android Studio读写MifareOne M1 IC卡源码

本示例使用的发卡器&#xff1a; https://item.taobao.com/item.htm?id615391857885&spma1z10.5-c-s.w4002-21818769070.11.66af789eLeok2R <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout …

定时启动 × 集简云数据采集,定时采集小红书话题笔记同步到数据表,对话题数据快速分析与监控

1 场景描述 在自媒体时代&#xff0c;众多企业将小红书视为关键的品牌构建平台。通过分析与跟进用户喜爱的话题&#xff0c;企业能够洞察市场趋势和用户需求&#xff0c;从而精准制定营销策略。然而&#xff0c;快速且精确地采集大量数据并非易事。 每个企业的自媒体团队都身兼…

mysql原理--redo日志2

1.redo日志文件 1.1.redo日志刷盘时机 我们前边说 mtr 运行过程中产生的一组 redo 日志在 mtr 结束时会被复制到 log buffer 中&#xff0c;可是这些日志总在内存里呆着也不是个办法&#xff0c;在一些情况下它们会被刷新到磁盘里&#xff0c;比如&#xff1a; (1). log buffer…

【办公技巧】Word中如何对齐选择题中的ABCD选项?

使用word文件制作试卷&#xff0c;如何将ABCD选项全部设置对齐&#xff1f;除了一直按空格或者Tab键以外&#xff0c;还有其他方法吗&#xff1f;今天分享如何将ABCD选项对齐。 首先&#xff0c;我们打开【替换和查找】&#xff0c;在查找内容输入空格&#xff0c;然后点击全部…

jsPlumb、mxGraph和Antv x6实现流程图选型

解决方案 结合我们项目以及主流解决方案&#xff0c;提供以下几种方案&#xff1a; 序号技术栈性质是否开源说明1jsPlumb国外框架社区版、商业版中台项目现有方案2mxGraph国外框架开源比较有名的开源绘图网站draw.io &#xff08;和processOn类似&#xff09;&#xff0c;使用…

【Python】编程练习的解密与实战(三)

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《Python | 编程解码》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 &#x1fa90;1. 初识Python &a…

工程机械比例阀电流采集方案——IPEhub2与IPEmotion APP

自从国家实施一带一路和新基建计划以来&#xff0c;工程机械的需求量呈现出快速增长的趋势。而关于工程机械&#xff0c;其比例阀的控制问题不容忽视。比例阀是一种新型的液压控制装置——在普通压力阀、流量阀和方向阀上&#xff0c;用比例电磁铁替代原有的控制部分&#xff0…

【漏洞复现】先锋WEB燃气收费系统文件上传漏洞 1day

漏洞描述 /AjaxService/Upload.aspx 存在任意文件上传漏洞 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作…

MySQL中datetime和timestamp的区别

datetime和timestamp的区别 相同点: 存储格式相同 datetime和timestamp两者的时间格式都是YYYY-MM-DD HH:MM:SS 不同点: 存储范围不同. datetime的范围是1000-01-01到9999-12-31. 而timestamp是从1970-01-01到2038-01-19, 即后者的时间范围很小. 与时区关系. datetime是存储…