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

news2024/11/16 8:48:08

一、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.jpg
        if(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
    // 自右向左查找 '.' 字符,如不存在返回NULL
    const 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 1
    while (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");
        }
    }
#endif
    close(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.coloradmin.cn/o/1376784.html

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

相关文章

Day27 回溯算法理论 77组合 216组合总和

回溯算法理论 回溯是一种效率并不高的穷举算法&#xff0c;因为用暴力算法都解决不了一些问题&#xff0c;所以才会考虑这个方法&#xff0c;它可以解决一系列问题&#xff1a; 组合问题&#xff1a;N个数里面按一定规则找出k个数的集合切割问题&#xff1a;一个字符串按一定规…

太平洋产险海南分公司:春季爱车保养,就看这几点!

一年之计在于春&#xff0c;春天不仅是万物复苏的好时节&#xff0c;也是一年中非常适合汽车养护的季节。 刚刚过去的春节&#xff0c;汽车的使用频率大大增加&#xff0c;很多车主都准备对爱车进行一次全面保养。加上立春过后&#xff0c;天气渐暖&#xff0c;许多车主也计划开…

提高iOS App开发效率的方法

引言 随着智能手机的普及&#xff0c;iOS App开发成为越来越受欢迎的技术领域之一。许多人选择开发iOS应用程序来满足市场需求&#xff0c;但是iOS App开发需要掌握一些关键技术和工具&#xff0c;以提高开发效率和质量。本文将介绍一些关键点&#xff0c;可以帮助你进行高效的…

基于SpringBoot的康复中心管理系统 JAVA简易版

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 普通用户模块2.2 护工模块2.3 管理员模块 三、系统展示四、核心代码4.1 查询康复护理4.2 新增康复训练4.3 查询房间4.4 查询来访4.5 新增用药 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的康复中…

Uncaught ReferenceError: videojs is not defined

项目场景&#xff1a; 项目背景&#xff1a; 开发 vue 项目时&#xff0c;调试时浏览器前端控制台 出现红色 报错信息&#xff1a; Uncaught ReferenceError: videojs is not defined 问题描述 遇到的问题&#xff1a; 开发 vue 项目时&#xff0c; 浏览器控制台出现如下所…

Vue-15、Vue条件渲染

1、v-show 在Vue中&#xff0c;v-show是一个指令&#xff0c;用于根据表达式的值来控制元素的显示与隐藏。当指令的值为true时&#xff0c;元素显示&#xff1b;当指令的值为false时&#xff0c;元素隐藏。 v-show的用法如下&#xff1a; <!DOCTYPE html> <html lan…

【REST2SQL】06 GO 跨包接口重构代码

【REST2SQL】01RDB关系型数据库REST初设计 【REST2SQL】02 GO连接Oracle数据库 【REST2SQL】03 GO读取JSON文件 【REST2SQL】04 REST2SQL第一版Oracle版实现 【REST2SQL】05 GO 操作 达梦 数据库 对所有关系数据的操作都只有CRUD&#xff0c;采用Go 的接口interface{}重构代码…

黑群晖6.x 7.x ABB Active Backup for Business 套件激活方法

注意事项&#xff1a; 要先下载安装好Active Backup for Business套件再操作。SN码在【控制面板】 - 【信息中心】 -【产品序列号】。建议复制到记事本内修改内容。群晖的https是默认的5001端口&#xff0c;如果你的https端口号换过请自行修改&#xff1a;5001 为当前的端口号…

应用在热能表领域中的数字温度传感芯片

热能表&#xff0c;是适用于测量在热交换环路中&#xff0c;被称作载热液体的液体所吸收或转换热能的仪器&#xff0c;它由流量传感器、温度传感器和热能积算仪三部分组成。热量表&#xff08;热表&#xff09;又称热能表、热能积算仪&#xff0c;既能测量供热系统的供热量又能…

Zabbix6.0全套落地方案-基于RHEL9系列源码编译安装-Linux+Nginx+Mysql+Redis生产级模板Agent2客户端一键部署

实践说明&#xff1a;基于RHEL9系列(CentOS9,AlmaLinux9,RockyLinux9等)&#xff0c;但适用场景不限于此&#xff0c;客户端一键部署安装包基于RHEL8和RHEL9。 文档形成时期&#xff1a;2023年 因系统或软件版本不同&#xff0c;构建部署可能略有差异&#xff0c;但本文未做细分…

便携式灯具的UL测试标准UL153介绍

UL153标准&#xff1a;UL153标准主要是描述有关使用电源线及插头作为连接工具,使用120伏电压,15或20安培的电源,并符合美国国家电器规范的便携灯.此标准也适用于那些不用插头,而用一些兼容的接线端作为连接工具的便携灯&#xff0c;同时对于使用非120伏电压&#xff0c;15or20安…

Google I/O大会:Android 13

3个体验升级的方向 以智能手机为场景核心、 扩大智能终端的应用边界以及实现多设备间更好地协同。具体到系统体验层&#xff0c;安卓13将支持图标颜色随主题更换、为不同应用设定使用的语言、新的媒体中心界面等等&#xff0c;同时谷歌也推出了自家的钱包应用&#xff08;Goog…

C++程序员必备的面试技巧

“程序员必备的面试技巧&#xff0c;就像是编写一段完美的代码一样重要。在面试战场上&#xff0c;我们需要像忍者一样灵活&#xff0c;像侦探一样聪明&#xff0c;还要像无敌铁金刚一样坚定。只有掌握了这些技巧&#xff0c;我们才能在面试的舞台上闪耀光芒&#xff0c;成为那…

雪花代码-html版

雪花代码 动画效果 代码 <!DOCTYPE html><html><head><style>body {background-color: #000000;}.snowflake {position: absolute;font-size: 10px;color: #FFFFFF;text-shadow: 1px 1px 1px #000000;user-select: none;}</style></head>…

java基于ssm框架的少儿编程在线培训系统论文

目 录 目 录 I 1 课题背景及研究内容 4 1.1 课题背景 4 1.2 开发目的和意义 4 2 相关技术和应用 4 2.1 VUE技术 4 2.2 Mysql数据库简介 5 2.3 SSM框架简介 6 2.4 Eclipse简介 6 2.5 B/S系统架构 6 3 系统分析 8 3.1可行性分析 8 3.1.1技术可行性 8 3.1.2经济可行性 8 3.1.3操作…

【会议征稿通知】第二届数字化经济与管理科学国际学术会议(CDEMS 2024)

第二届数字化经济与管理科学国际学术会议&#xff08;CDEMS 2024&#xff09; 2024 2nd International Conference on Digital Economy and Management Science&#xff08;CDEMS 2024&#xff09; 2024年第二届数字经济与管理科学国际会议(CDEMS 2024) 定于2023年4月26-28日…

怎么做微信秒杀链接_开启用户的购物新体验

微信秒杀&#xff1a;开启你的购物新体验 在繁忙的生活节奏中&#xff0c;你是否厌倦了长时间排队等待购物&#xff0c;或者在电商平台上漫长而复杂的购物流程&#xff1f;今天&#xff0c;我要向你介绍一种全新的购物方式——微信秒杀。这不仅是一种全新的购物体验&#xff0…

基于SpringBoot的婚恋交友网站 JAVA简易版

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 会员管理模块2.3 新闻管理模块2.4 相亲大会管理模块2.5 留言管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 会员信息表3.2.2 新闻表3.2.3 相亲大会表3.2.4 留言表 四、系统展示五、核心代码5.…

git: Updates were rejected because the tip of your current branch is behind

一、报错含义 由于本地分支的tip落后远程分支&#xff0c;push操作被拒绝。 二、产生原因 我再本地拉去了新的分支并未同步到远程仓库&#xff0c;在新分支进行开发&#xff0c;由于前几天同步也创建了该分支并同步到了远程仓库&#xff0c;导致我本次push失败 三、解决方…

使用proteus进行主从JK触发器仿真失败原因的分析

在进行JK触发器的原理分析的时候&#xff0c;我首先在proteus根据主从JK触发器的原理进行了实验根据原理图&#xff0c;如下图&#xff1a; 我进行仿真&#xff0c;在仿真的过程中&#xff0c;我向电路图中添加了外部的置0/1端口&#xff0c;由此在proteus中得到下面的电路图 …