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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

系列三十五、获取Excel中的总记录数

一、获取Excel中的总记录数 1.1、概述 使用EasyExcel开发进行文件上传时&#xff0c;通常会碰到一个问题&#xff0c;那就是Excel中的记录数太多&#xff0c;使用传统的方案进行文件上传&#xff0c;很容易就超时了&#xff0c;这时可以通过对用户上传的Excel中的数量进行限制…

闰年问题-第11届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第25讲。 闰年问题&#xf…

swaggerUI不好用,试试这个openapiUI?

1.背景 由于长期使用 swaggerUI 工具&#xff0c;它的轻量风格个人觉得还是不错的&#xff0c;但是它的整体使用体验确实不好&#xff0c;用过的可能都有体会&#xff0c;这里就不一一列举了&#xff08;由于语言表达能力有限&#xff0c;手动&#x1f436;保命&#xff0c;毕…

1.7数算PPT选择汇总,PTA选择汇总,计算后缀表达式,中缀转后缀、前缀、快速排序

PTA选择汇总 在第一个位置后插入&#xff0c;注意是在后面插入&#xff0c;而不是前面&#xff1b;要移动49&#xff0c;为50-I&#xff0c;第25个的话&#xff0c;移25个 如果是插在前面&#xff0c;就移动50&#xff0c;N-I1&#xff0c;注意是插在前面还是后面 删第一个&a…

JS-基础语法(一)

JavaScript简单介绍 变量 常量 数据类型 类型转换 案例 1.JavaScript简单介绍 JavaScript 是什么&#xff1f; 是一种运行在客户端&#xff08;浏览器&#xff09;的编程语言&#xff0c;可以实现人机交互效果。 JS的作用 JavaScript的组成 JSECMAScript( 基础语法 )…

变换器电感饱和以及电流变大电感变小原因分析

电感电流变大电感值变小 在一个DC-DC电源转换器中&#xff0c;电感器的电流与其电感量是有关系的。当电感器的电流增大时&#xff0c;其电感量通常会变小。 电感器的电感量&#xff08;L&#xff09;是指在单位电流变化率下&#xff0c;电感器两端的电压变化的比例。根据电感…

【JAVA GUI+MYSQL]社团信息管理系统

本社团信息管理系统主要实现登录注册、管理员信息管理、社团用户信息管理、用户申请信息管理功能模块。 目录 &#xff11;&#xff0e;系统主要功能介绍 &#xff12;&#xff0e; 数据库概念模型设计 3.具体功能模块的实现 3.1模型类 3.1.1Student.java 3.1.2User .j…

高通平台开发系列讲解(USB篇)Ubuntu 下如何使用模块

文章目录 一、查看VID、PID二、adb添加2.1、在udev下添加模块的VID2.2、重启adb服务三、虚拟串口添加(AT、Diag)沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要图解高通平台上位机使用方法 一、查看VID、PID 在ubuntu下使用模块进行AT指令发送,Diag等串…

【QML COOK】- 003-处理鼠标事件

1. 编辑main.qml import QtQuickWindow {width: 800height: 800visible: truetitle: qsTr("Hello World")Image {id: backgroudanchors.fill: parentsource: "qrc:/Resources/Images/arrow.png"}MouseArea {anchors.fill: parentonClicked: backgroud.rot…

docker run 命令详解

一、前言 Docker容器是一个开源的应用容器引擎&#xff0c;让开发者可以以统一的方式打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何安装了Docker引擎的服务器上&#xff08;包括流行的Linux机器、Windows机器&#xff09;&#xff0c;也可以实现虚拟…

【hcie-cloud】【20】容器详解【容器介绍,容器工作机制、容器常用命令说明】【上】

文章目录 前言容器是什么虚拟化技术的四个特点容器也是一种虚拟化技术容器是怎么实现虚拟化的&#xff1f;容器对比虚拟机有哪些优势&#xff1f;容器对比虚拟机有哪些不足&#xff1f;容器不仅是一种虚拟化技术&#xff0c;更重要的是一种应用打包机制容器提供的是PaaS服务常见…

SSH远程访问出现Permission denied(password)解决方法

首先&#xff0c;这个不是密码输错了的问题&#xff1b; 1、在主机先ping一下服务器 ping XXX.XXX.XX.XXX (服务器ip地址) 如果pin成功了&#xff0c;说明可以进行连接 查看服务器的ip ifconfig2、主机连接服务器 &#xff08;服务器的ip&#xff09; ssh testXXX.XXX.XX.…

Java中SpringBoot组件集成接入【MQTT中间件】

Java中SpringBoot组件集成接入【MQTT中间件】 1.MQTT介绍2.搭建MQTT服务器1.Windows2.Ubuntu3.Docker4.其他方式3.mqtt可视化客户端MQTTX及快速使用教程4.SpringBoot接入MQTT1、maven依赖2、MQTT配置3、MQTT组件具体代码1.定义通道名字2.消息发布器3.MQTT配置、生产者、消费者4…

线扫相机品牌汇总(国外+国内)

线扫相机品牌汇总(国外+国内) 行者 ​ 热爱生活 22 人赞同了该文章 线扫相机也叫做线阵相机,和面阵相机一样,都是重要的工业相机。 线扫相机正如其名字那样,拍照时像扫描一样,相机和被拍照物体有相对匀速运动。 Perhaps the most common example of line scan imagin…

如何顺滑使用华为云编译构建平台?

这两年平台构建服务需求越来越大&#xff0c;却一直苦于找不到一些指南&#xff0c; 这里特意写了一篇&#xff0c; 对在学习代码阶段和新手程序员朋友也蛮友好&#xff0c; 配置真的也不难&#xff0c; 也特别适合想尝试从0到1做个APP的朋友了。 以华为云的CodeArts Build为例…

Mac/Linux虚拟机CrossOver2024新版下载使用教程

CrossOver不像Parallels或VMware的模拟器&#xff0c;而是实实在在Mac OS X系统上运行的一个软件&#xff0c;该软件可以让用户在mac是上直接运行windows软件&#xff0c;本文为大家带来的是CrossOver Mac版安装教程&#xff01; CrossOver Mac-安装包下载如下&#xff1a;http…

ubuntu系统(9):ubuntu 20.02安装pydot

目录 警告信息 1、确保安装了Python和pip 2、安装Graphviz软件包 3、pip安装pydot 验证 在gem5中&#xff0c;pydot库用于生成图形化输出&#xff0c;特别是生成.dot文件和相关的图像文件&#xff0c;如PDF、PNG等。它与gem5结合使用的一个常见用途是生成系统结构图、内存…

MFC结合GDI+

MFC结合GDI 创建一个空的MFC界面&#xff0c;在确定按钮函数里进行画图&#xff1a; 1、包含头文件与库 在stdafx.h中加入以下三行代码&#xff1a; #include "gdiplus.h" using namespace Gdiplus; #pragma comment(lib, "gdiplus.lib")2、安装GDI 在…

深度学习 Day25——J4 ResNet与DenseNet结合探索(DPN)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 文章目录 前言1 我的环境2 pytorch实现DPN算法2.1 前期准备2.1.1 引入库2.1.2 设置GP…

【C++进阶04】STL中map、set、multimap、multiset的介绍及使用

一、关联式容器 vector/list/deque… 这些容器统称为序列式容器 因为其底层为线性序列的数据结构 里面存储的是元素本身 map/set… 这些容器统称为关联式容器 关联式容器也是用来存储数据的 与序列式容器不同的是 其里面存储的是<key, value>结构的键值对 在数据检索时…