workflow系列教程(6)实现静态资源服务器

往期教程

如果觉得写的可以,请给一个点赞+关注支持一下
观看之前请先看,往期的博客教程,否则这篇博客没办法看懂

处理请求

我们不占用任何线程读取文件,而是产生一个异步的读文件任务,在读取完成之后回复请求。

再次说明一下,我们需要把完整回复数据读取到内存,才开始回复消息。所以不适合用来传输太大的文件。

cpp 复制代码
struct SeriesContext{
    WFHttpTask *serverTask;	//所属workflow任务
    int fd;					//文件fd
    char *buf;				//文件数据缓冲区
    size_t filesize;		//文件大小
};

void process(WFHttpTask *serverTask){
    // 1 创建文件IO任务
    size_t filesize = 614;
    int fd = open("postform.html",O_RDONLY);
    char *buf = new char[filesize];
    auto IOTask = WFTaskFactory::create_pread_task(fd,buf,filesize,0,IOCallback);
    // 2 把文件IO任务加入到序列中
    series_of(serverTask)->push_back(IOTask);
    // 3 创建传递给IOTask的context
    SeriesContext *context = new SeriesContext;
    context->serverTask =  serverTask;
    context->fd = fd;
    context->buf = buf;
    context->filesize = filesize;
    series_of(serverTask)->set_context(context);
    // 4 设置序列的回调函数,释放所有资源
    series_of(serverTask)->set_callback([](const SeriesWork *series){
        fprintf(stderr,"series callback\n");
        SeriesContext * context = static_cast<SeriesContext *>(series->get_context());
        delete[] context->buf;
        close(context->fd);
        delete context;
    });
}

与HTTP反向代理产生一个新的http client任务不同,这里我们通过factory产生了一个pread任务。

WFTaskFactory.h里,我们可以看到相关的接口。

cpp 复制代码
struct FileIOArgs
{
    int fd;
    void *buf;
    size_t count;
    off_t offset;
};

...
using WFFileIOTask = WFFileTask<struct FileIOArgs>;
using fio_callback_t = std::function<void (WFFileIOTask *)>;
...

class WFTaskFactory
{
public:
    ...
    static WFFileIOTask *create_pread_task(int fd, void *buf, size_t count, off_t offset,
                                           fio_callback_t callback);

    static WFFileIOTask *create_pwrite_task(int fd, void *buf, size_t count, off_t offset,
                                            fio_callback_t callback);
    ...

    /* Interface with file path name */
	static WFFileIOTask *create_pread_task(const std::string& pathname, void *buf, size_t count, off_t offset,
                                           fio_callback_t callback);

    static WFFileIOTask *create_pwrite_task(const std::string& pathname, void *buf, size_t count, off_t offset,
                                            fio_callback_t callback);  
};

无论是pread还是pwrite,返回的都是WFFileIOTask。这与不区分sort或psort,不区分client或server task是一个道理。

除这两个接口还有preadv和pwritev,返回WFFileVIOTask,以及fsync,fdsync,返回WFFileSyncTask。可以在头文件里查看。

示例用了task的user_data域保存服务的全局数据。但对于大服务,推荐使用series context。

处理读文件结果及响应

将文件资源返回给客户端

cpp 复制代码
void IOCallback(WFFileIOTask *IOTask){
    SeriesContext *context = static_cast<SeriesContext *>(series_of(IOTask)->get_context());
    auto resp2client = context->serverTask->get_resp();
    resp2client->add_header_pair("Content-Type","text/html");
    resp2client->append_output_body(context->buf,context->filesize);
}

完整代码

postform.html

html 复制代码
<!DOCTYPE html>
<html>
    <head>
        <title>test</title>
    </head>
    <body> 
        <form  method="post" enctype="application/x-www-form-urlencoded">
            <div>
              <label for="username">username:</label>
              <input type="text" name="username">
            </div>
            <div>
              <label for="password">password:</label>
              <input type="text" name="password">
            </div>
            <div class="button">
                <button type="submit">Send your message</button>
            </div>
        </form>
    </body>
</html>
cpp 复制代码
#include "linuxheader.h"
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
#include <workflow/HttpUtil.h>
static WFFacilities::WaitGroup waitGroup(1);
void sigHandler(int num){
    waitGroup.done();
    fprintf(stderr,"wait group is done\n");
}
struct SeriesContext{
    WFHttpTask *serverTask;
    int fd;
    char *buf;
    size_t filesize;
};
void IOCallback(WFFileIOTask *IOTask){
    SeriesContext *context = static_cast<SeriesContext *>(series_of(IOTask)->get_context());
    auto resp2client = context->serverTask->get_resp();
    resp2client->add_header_pair("Content-Type","text/html");
    resp2client->append_output_body(context->buf,context->filesize);
}
void process(WFHttpTask *serverTask){
    // 1 创建文件IO任务
    size_t filesize = 614;
    int fd = open("postform.html",O_RDONLY);
    char *buf = new char[filesize];
    auto IOTask = WFTaskFactory::create_pread_task(fd,buf,filesize,0,IOCallback);
    // 2 把文件IO任务加入到序列中
    series_of(serverTask)->push_back(IOTask);
    // 3 创建传递给IOTask的context
    SeriesContext *context = new SeriesContext;
    context->serverTask =  serverTask;
    context->fd = fd;
    context->buf = buf;
    context->filesize = filesize;
    series_of(serverTask)->set_context(context);
    // 4 设置序列的回调函数,释放所有资源
    series_of(serverTask)->set_callback([](const SeriesWork *series){
        fprintf(stderr,"series callback\n");
        SeriesContext * context = static_cast<SeriesContext *>(series->get_context());
        delete[] context->buf;
        close(context->fd);
        delete context;
    });
}
int main(){
    signal(SIGINT,sigHandler);
    WFHttpServer server(process);
    if(server.start(1234) == 0){
        waitGroup.wait();
        server.stop();
    }
    else{
        perror("server start failed\n");
        return -1;
    }
    return 0;   
}
相关推荐
保持低旋律节奏26 分钟前
C++——list链表
c++·链表·list
qq_183802872 小时前
Linux内核idr数据结构使用
linux·运维·服务器
2401_841495642 小时前
【数据结构】基于Floyd算法的最短路径求解
java·数据结构·c++·python·算法··floyd
码农-小林2 小时前
使用leaflet库加载服务器离线地图瓦片(这边以本地nginx服务器为例)
运维·服务器·nginx
噜啦噜啦嘞好2 小时前
Linux:库制作与原理
linux·运维·服务器
androidstarjack2 小时前
知乎服务器崩溃!
运维·服务器
纵有疾風起3 小时前
C++模版:模板初阶及STL简介
开发语言·c++·经验分享·开源
QT 小鲜肉3 小时前
【个人成长笔记】Qt Creator快捷键终极指南:从入门到精通
开发语言·c++·笔记·qt·学习·学习方法
勇闯逆流河3 小时前
【C++】用红黑树封装map与set
java·开发语言·数据结构·c++
实心儿儿3 小时前
C++——内存管理
c++