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;   
}
相关推荐
会周易的程序员27 分钟前
使用 pybind11 封装 C++ 日志库 microLog 为 Python 模块
java·c++·python·物联网·架构·日志·aiot
WZF-Sang34 分钟前
网络基础——2
服务器·网络·c++·学习·网络编程·php
wang_shu_mo_ran2 小时前
网络编程(一):网络编程初识
运维·服务器·网络
-西门吹雪2 小时前
现代C++ 并发编程-学习指南
java·jvm·c++
ton_tom3 小时前
设备驱动程序编程-Linux2.6.10-kdb安装-32位
linux·运维·服务器
坐望云起3 小时前
FFmpeg.wasm 纯浏览器端视频压缩与格式转换实战:零服务器、零上传、100% 隐私
服务器·ffmpeg·wasm
aaPIXa6224 小时前
C++ TensorRT Edge-LLM 边缘推理框架:从原理到实战
开发语言·c++·edge
cft56200_ln4 小时前
gPTP Master 报文目的 MAC 地址
运维·服务器·网络
nianniannnn6 小时前
c++复习自存--标准模板库STL
开发语言·c++·windows
努力努力再努力wz6 小时前
【高性能网络库与HTTP Server系列】:基于主从 Reactor 模型实现高性能 C++ 网络库与 HTTP Server
开发语言·网络·数据结构·数据库·c++·网络协议·http