基于多反应堆的高并发服务器【C/C++/Reactor】(中)在TcpConnection 中接收并解析Http请求消息

news2024/11/17 0:06:36

一、在TcpConnection 中多添加和http协议相关的request和response

struct TcpConnection {
    struct EventLoop* evLoop;
    struct Channel* channel;
    struct Buffer* readBuf;
    struct Buffer* writeBuf;
    char name[32];
    // http协议
    struct HttpRequest* request;
    struct HttpResponse* response;
};

二、给客户端回复数据(方法一)

1.在Buffer.h文件中添加bufferSendData函数:

// 发送数据
int bufferSendData(struct Buffer* buf,int socket);
// 发送数据
int bufferSendData(struct Buffer* buf,int socket) {
    // 判断有无数据
    int readableSize = bufferReadableSize(buf);// 这些未读的数据就是待发送的数据
    if(readableSize > 0) {
        int count = send(socket,buf->data + buf->readPos,readableSize,MSG_NOSIGNAL);
        if(count > 0) {
            buf->readPos += count;
            usleep(1);
        }
        return count;
    }    
    return 0;
}

 2.在TcpConnection.c文件中添加processWrite函数:

int processWrite(void* arg) {
    struct TcpConnection* conn = (struct TcpConnection*)arg;
    // 发送数据
    int count = bufferSendData(conn->writeBuf,conn->channel->fd);
    if(count > 0) {
        // 判断数据是否被全部发送出去了
        if(bufferReadableSize(conn->writeBuf) == 0){
            // 1.不再检测写事件 -- 修改channel中保存的事件
            writeEventEnable(conn->channel,false);
            // 2.修改dispatcher检测的集合 -- 添加任务节点
            eventLoopAddTask(conn->evLoop,conn->channel,MODIFY);    
            // 3.删除这个节点
            eventLoopAddTask(conn->evLoop,conn->channel,DELETE);
        }
    }
    return 0;
}

3.修改tcpConnectionInit函数中调用的channelInit函数的写回调函数为processWrite函数

​// 初始化
struct TcpConnection* tcpConnectionInit(int fd,struct EventLoop* evLoop) {
    struct TcpConnection* conn = (struct TcpConnection*)malloc(sizeof(struct TcpConnection));
    conn->evLoop = evLoop;
    struct Channel* channel = channelInit(fd,ReadEvent,processRead,processWrite,tcpConnectionDestroy,conn);
    conn->channel = channel;
    conn->readBuf = bufferInit(10240); // 10k
    conn->writeBuf = bufferInit(10240); // 10k
    sprintf(conn->name,"TcpConnection-%d",fd);

    // http协议
    conn->request = httpRequestInit();
    conn->response = httpResponseInit();

    // 把channel添加到事件循环对应的任务队列里边
    eventLoopAddTask(evLoop,conn->channel,ADD);
    return conn;
}

三、给客户端回复数据(方法二)

  • 在TcpConnection.h中添加 
// #define MSG_SEND_AUTO
  • TcpConnection.c
// 接收客户端数据
int processRead(void* arg) {
    struct TcpConnection* conn = (struct TcpConnection*)arg;
    // 接收数据
    int count = bufferSocketRead(conn->readBuf,conn->channel->fd);
    if(count > 0) {
        // 接收到了Http请求,解析Http请求
        int socket = conn->channel->fd;
#ifdef MSG_SEND_AUTO  // 给客户端回复数据的方式一
        writeEventEnable(conn->channel,true);
        eventLoopAddTask(conn->evLoop,conn->channel,MODIFY);
#endif
        bool flag = parseHttpRequest(conn->request,conn->readBuf,conn->response,conn->writeBuf,socket);
        if(!flag) {
            // 解析失败,回复一个简单的html
            char* errMsg = "Http/1.1 400 Bad Request\r\n\r\n";
            bufferAppendString(conn->writeBuf,errMsg);
        }
    }
    else{
#ifdef MSG_SEND_AUTO
        // 断开连接
        eventLoopAddTask(conn->evLoop,conn->channel,DELETE);
#endif
    }
#ifndef MSG_SEND_AUTO
    // 断开连接
    eventLoopAddTask(conn->evLoop,conn->channel,DELETE);
#endif
    return 0;
}

1.修改HttpRequest.c文件中的sendFile函数和sendDir函数

void sendFile(const char* fileName,struct Buffer* sendBuf,int cfd) {
    // 打开文件
    int fd = open(fileName,O_RDONLY);
    if(fd < 0) {
        perror("open");
        return;
    }
    // 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);
#ifndef MSG_SEND_AUTO // 给客户端回复数据(方法二)
            bufferSendData(sendBuf,cfd);
#endif
        }
        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);
}

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);
#ifndef MSG_SEND_AUTO // 给客户端回复数据(方法二)
        bufferSendData(sendBuf,cfd);
#endif
        memset(buf,0,sizeof(buf));
        free(nameList[i]); 
    } 
    sprintf(buf,"</table></body></html>");
    // send(cfd,buf,strlen(buf),0);
    bufferAppendString(sendBuf,buf);
#ifndef MSG_SEND_AUTO // 给客户端回复数据(方法二)
    bufferSendData(sendBuf,cfd);
#endif
    free(nameList);
}
 

2.修改HttpResponse.c文件的httpResponsePrepareMsg函数

// 组织http响应数据
void httpResponsePrepareMsg(struct HttpResponse* response,struct Buffer* sendBuf,int socket) {
    // 状态行
    char tmp[1024] = {0};
    sprintf(tmp,"HTTP/1.1 %d %s\r\n",response->statusCode,response->statusMsg);
    bufferAppendString(sendBuf,tmp);
    
    // 响应头
    for(int i=0;i<response->headerNum;++i) {
        // memset(tmp,0,sizeof(tmp));  ?????????
        sprintf(tmp,"%s: %s\r\n",response->headers[i].key,response->headers[i].value);
        bufferAppendString(sendBuf,tmp);
    }

    // 空行
    bufferAppendString(sendBuf,"\r\n");
    
#ifndef MSG_SEND_AUTO // 给客户端回复数据(方法二)
    bufferSendData(sendBuf,socket);
#endif
    // 回复的数据
    response->sendDataFunc(response->fileName,sendBuf,socket);
}

 四、释放资源 tcpConnectionDestroy

// 释放资源
int tcpConnectionDestroy(void* arg);
// 释放资源
int tcpConnectionDestroy(void* arg) {
    struct TcpConnection* conn = (struct TcpConnection*)arg;
    if(conn!=NULL) {
        if (conn->readBuf && bufferReadableSize(conn->readBuf) == 0 &&
            conn->writeBuf && bufferReadableSize(conn->writeBuf) == 0) {
            destroyChannel(conn->evLoop,conn->channel);
            bufferDestroy(conn->readBuf);
            bufferDestroy(conn->writeBuf);
            httpRequestDestroy(conn->request);
            httpResponseDestroy(conn->response);
            free(conn);
        }
    }
    return 0;
}

五、日志功能

#pragma once
#include <stdarg.h>
#include <stdio.h>

#define  DEBUG   1  

#if DEBUG
/*
*  如果不加 do ... while(0) 在进行条件判断的时候(只有一句话), 省略了{}, 就会出现语法错误
*  if 
*     xxxxx
*  else
*     xxxxx
*  宏被替换之后, 在 else 前面会出现一个 ;  --> 语法错误
*/
#define LOG(type, fmt, args...)  \
  do{\
    printf("%s: %s@%s, line: %d\n***LogInfo[", type, __FILE__, __FUNCTION__, __LINE__);\
    printf(fmt, ##args);\
    printf("]\n\n");\
  }while(0)
#define Debug(fmt, args...) LOG("DEBUG", fmt, ##args)
#define Error(fmt, args...) do{LOG("ERROR", fmt, ##args);exit(0);}while(0)
#else
#define LOG(fmt, args...)  
#define Debug(fmt, args...)
#define Error(fmt, args...)
#endif


 

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

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

相关文章

leecode1143 | 最长公共子序列

给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff08;也可以不…

第7章 PKI 和密码应用

7.1 非对称密码 第6章的“现代密码学”一节介绍了私钥&#xff08;对称&#xff09;和公钥&#xff08;非对称&#xff09;密码的基本原则。 你曾学过&#xff0c;对称密钥密码系统要求通信双方使用同一个共享秘密密钥&#xff0c;因而形成了安全分发密钥的问题。 你还曾学过…

如何使用科大讯飞星火大模型AI批量生成文章

如何使用科大讯飞的星火大模型AI工具批量生成文章呢&#xff1f; 我们可以使用科大讯飞AI的星火大模型API接口&#xff0c;它支持批量处理和生成文章的AI功能。 但是星火大模型API接口无法直接使用&#xff0c;一般需要技术人员开发对应程序对接才行。为了让不懂技术的普通用…

【微信小程序开发】深入学习小程序开发之功能扩展和优化

前言 随着移动互联网的快速发展&#xff0c;微信小程序作为一种轻量级应用&#xff0c;已经逐渐成为许多企业和个人进行业务推广和服务提供的重要平台本文将详细介绍 微信小程序开发的功能扩展和优化&#xff0c;帮助开发者更好地提升小程序的用户体验和性能。 一、功能扩展 …

数据库系统概念 第七版 中文答案 第3章 SQL介绍

3.1 将以下查询使用SQL语言编写&#xff0c;使用大学数据库模式。 &#xff08;我们建议您实际在数据库上运行这些查询&#xff0c;使用我们在书籍网站db-book.com上提供的示例数据。有关设置数据库和加载示例数据的说明&#xff0c;请参阅上述网站。&#xff09; a. 查找计算机…

蓝桥杯练习题(六)

&#x1f4d1;前言 本文主要是【算法】——蓝桥杯练习题&#xff08;六&#xff09;的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 …

软件分发点(DP)的合理规划

软件分发点&#xff08;Distribution Point, DP&#xff09;是用于托管文件以分发到计算机和移动设的服务器&#xff0c;Jamf Pro可以通过分发点分发以下类型的文件&#xff1a; 软件包 脚本 内部应用程序 内部书籍 Jamf Pro支持两种类型的分发点&#xff0c;您可以使用这些类型…

我用 Laf 开发了一个非常好用的密码管理工具

【KeePass 密码管理】是一款简单、安全简洁的账号密码管理工具&#xff0c;服务端使用 Laf 云开发&#xff0c;支持指纹验证、FaceID&#xff0c;N 重安全保障&#xff0c;可以随时随地记录我的账号和密码。 写这个小程序之前&#xff0c;在国内市场找了很多密码存储类的 App …

汽配企业MES管理系统的特点与实践

随着汽车工业的飞速发展&#xff0c;汽车零部件制造企业面临着日益复杂的生产环境和多样化的市场需求。为了应对这些挑战&#xff0c;许多汽配企业开始引入MES管理系统解决方案&#xff0c;以提高生产效率、优化资源配置、提升产品质量。本文将重点探讨汽配企业MES管理系统的特…

阿尔泰推出19“8槽4U上架式CPCI机箱 支持客户定制化机箱需求

阿尔泰科技发展有限公司是北京阿尔泰科技的子公司&#xff0c;公司于2010年正式成立&#xff0c;集全国技术支持与服务&#xff0c;销售&#xff0c;结构设计&#xff0c;项目支持等一批专业从事工控行业的工程师屹立在天府之国。公司涵盖数据采集&#xff0c;无线传输&#xf…

搭建sprinboot服务环境

搭建sprinboot服务环境 安装jdk安装nginx安装Redis安装MySQL一 下载MySQL二 安装MySQL三 启动mysql服务获取初始化密码四 登陆MySQL五 修改密码六 设置远程访问七 相关问题错误&#xff1a;1819错误&#xff1a;1251 或 2059错误&#xff1a;10060忽略表名大小写 记录搭建sprin…

[计算机提升] 创建FTP共享

4.7 创建FTP共享 4.7.1 FTP介绍 在Windows系统中&#xff0c;FTP共享是一种用于在网络上进行文件传输的标准协议。它可以让用户通过FTP客户端程序访问并下载或上传文件&#xff0c;实现文件共享。 FTP共享的用途非常广泛&#xff0c;例如可以让多个用户共享文件、进行文件备份…

solr 远程命令执行漏洞复现 (CVE-2019-17558)

solr 远程命令执行漏洞复现 (CVE-2019-17558) ‍ 名称: solr 远程命令执行 (CVE-2019-17558) 描述: Apache Velocity是一个基于Java的模板引擎&#xff0c;它提供了一个模板语言去引用由Java代码定义的对象。Velocity是Apache基金会旗下的一个开源软件项目&#xff0c;旨在确…

【抓包教程】BurpSuite联动雷电模拟器——安卓高版本抓包移动应用教程

前言 近期找到了最适合自己的高版本安卓版本移动应用抓HTTP协议数据包教程&#xff0c;解决了安卓低版本的问题&#xff0c;同时用最简单的办法抓到https的数据包&#xff0c;特此进行文字记录和视频记录。 前期准备 抓包工具&#xff1a;BurpSuite安卓模拟器&#xff1a;雷…

docker 利用特权模式逃逸并拿下主机

docker 利用特权模式逃逸并拿下主机 在溯源反制过程中&#xff0c;会经常遇到一些有趣的玩法&#xff0c;这里给大家分享一种docker在特权模式下逃逸&#xff0c;并拿下主机权限的玩法。 前言 在一次溯源反制过程中&#xff0c;发现了一个主机&#xff0c;经过资产收集之后&…

图形化编程:下一代的创新教育工具

在科技日新月异的今天&#xff0c;编程已经成为了一项必备的技能。然而&#xff0c;传统的编程语言对于许多人来说仍然是一项挑战&#xff0c;尤其是对于年轻的学习者。为了解决这个问题&#xff0c;图形化编程应运而生&#xff0c;它以其直观、易理解和易操作的特点&#xff0…

DevOps搭建(十六)-Jenkins+K8s部署详细步骤

​ 1、整体部署架构图 2、编写脚本 vi pipeline.yml apiVersion: apps/v1 kind: Deployment metadata:namespace: testname: pipelinelabels:app: pipeline spec:replicas: 2selector:matchLabels:app: pipelinetemplate:metadata:labels:app: pipelinespec:containers:- nam…

Logstash应用介绍

1.Logstash介绍 1.1 前世今生 Logstash 项目诞生于 2009 年 8 月 2 日。其作者是世界著名的运维工程师乔丹西塞(JordanSissel)&#xff0c;乔丹西塞当时是著名虚拟主机托管商 DreamHost 的员工。 Logstash 动手很早&#xff0c;对比一下&#xff0c;scribed 诞生于 2008 年&am…

程序员自由创业周记#24:逃离北上广

程序员自由创业周记#24&#xff1a;逃离北上广 有没有这种城市 房价&#xff1a;市区房价均价1W以内工资&#xff1a;每月工资能买一平米及以上的房子交通&#xff1a;路宽不堵车&#xff0c;高铁、高速发达&#xff0c;坐飞机方便快递&#xff1a;方便&#xff0c;包邮&#…

gazebo怎样快速导入其他机器人及其配置

只要拿过来100块钱&#xff0c;我就告诉你我花了1天才偶然找到的内容哈哈&#xff0c;请留言