在 32 位系统上使用 libevent 提供大文件(> 2GB) [英] Serving large files (>2GB) with libevent on 32-bit system

查看:51
本文介绍了在 32 位系统上使用 libevent 提供大文件(> 2GB)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

序言:基于 libevent v2 (evhttp)、Linux、ARM、glibc2.3.4 用 C 语言编写的轻量级 http 服务器

Preamble: lightweight http server written in C based on libevent v2 (evhttp), Linux, ARM, glibc2.3.4

我正在尝试在 32 位系统上使用 evbuffer_add_file() 提供大文件(超过 2GB).libevent 是使用 -D_FILE_OFFSET_BITS=64 标志编译的.这是简化的代码:

I'm trying to serve big files (over 2GB) using evbuffer_add_file() on 32 bit system. The libevent was compiled with -D_FILE_OFFSET_BITS=64 flag. Here is the simplified code:

int fd = -1;
if ((fd = open(path, O_RDONLY)) < 0) {
    // error handling
}

struct stat st;
if (fstat(fd, &st) < 0) {
    // error handling
}

struct evbuffer *buffer = evbuffer_new();
evbuffer_set_flags(buffer, EVBUFFER_FLAG_DRAINS_TO_FD); // force using system's sendfile
evbuffer_add_file(buffer, fd, 0, st.st_size);
evhttp_send_reply(req, 200, NULL, buffer);
evbuffer_free(buffer);

st.st_size 具有正确的值,在本例中为 4913809524,但响应头 Content-Length 的值为 618842228.即使我将 Content-Length 头设置为适当的值,文件传输也会在 618842228 处停止 ...

st.st_size has correct value, in this case 4913809524, but response header Content-Length has value of 618842228. Even if i set Content-Length header to appropriate value the file transfer stops at 618842228 ...

我是否想念或做错了什么?有可能吗?

Do i miss or do something wrong? Is it possible at all?

提前致谢

推荐答案

正如我在评论中所说,问题显然不在 libevent 中,而是在系统的 sendfile 实现中.因此,通过一些解决方法,我找到了解决此问题的方法evhttp_send_reply_(start|chunk|end) 函数族:

As i said in comment obviously the problem is not in libevent, but in system's sendfile implementation. So with a little workaround i found the way to solve this problem using evhttp_send_reply_(start|chunk|end) functions family:

    struct chunk_req_state {
        struct evhttp_request *req;
        int fd;
        long chunksize;
        off_t filesize;
        off_t offset;
    };

    static void
    chunked_trickle_cb(evutil_socket_t fd, short events, void *arg)
    {
        struct evbuffer *evb = evbuffer_new();
        struct chunk_req_state *state = arg;
        struct timeval when = { 0, 0 };
        ev_ssize_t read;

        if (lseek(state->fd, state->offset, SEEK_SET) == -1) {
                    evbuffer_free(evb);
                    close(state->fd);
                    free(state);
                    return;
        }

        read = evbuffer_read(evb, state->fd, (ev_ssize_t) state->chunksize);
        if (read == -1) {
            evbuffer_free(evb);
            evhttp_send_reply_end(state->req);
            close(state->fd);
            free(state);
            return;
        }

        evhttp_send_reply_chunk(state->req, evb);
        evbuffer_free(evb);

        state->offset += read;

        if (state->offset < state->filesize) {
              // there's more data to send
            event_base_once(ebase, -1, EV_TIMEOUT, chunked_trickle_cb, state, &when);
        } else {
                // reached the end
            evhttp_send_reply_end(state->req);
            close(state->fd);
            free(state);
        }
    }

    int fd = -1;
    if ((fd = open(path, O_RDONLY)) < 0) {
        // error handling
    }

    struct stat st;
    if (fstat(fd, &st) < 0) {
        // error handling
    }

    struct timeval when = { 0, 0 };
    struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state));
    memset(state, 0, sizeof(struct chunk_req_state));
    state->req = req;
    state->fd = fd;
    state->chunksize = 10*1024*1024;
    state->filesize = st.st_size;
    state->offset = 0;
    // set Content-Length to prevent chunked transfer
    char *length = NULL;
    spprintf(&length, 0, "%lld", st.st_size);
    evhttp_add_header(evhttp_request_get_output_headers(request->req), "Content-Length", length);
    free(length);
    evhttp_send_reply_start(request->req, 200, NULL);
    event_base_once(ebase, -1, EV_TIMEOUT, chunked_trickle_cb, state, &when);

这篇关于在 32 位系统上使用 libevent 提供大文件(> 2GB)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆