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

这篇具有很好参考价值的文章主要介绍了基于多反应堆的高并发服务器【C/C++/Reactor】(中)在TcpConnection 中接收并解析Http请求消息。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、在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

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

// 释放资源
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


 文章来源地址https://www.toymoban.com/news/detail-818744.html

到了这里,关于基于多反应堆的高并发服务器【C/C++/Reactor】(中)在TcpConnection 中接收并解析Http请求消息的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包