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

2024-01-09 06:49:59

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

文章来源:https://blog.csdn.net/weixin_41987016/article/details/135469611
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。