2013-12-09 5 views
1

Преамбула: облегченный сервер HTTP написана на C основана на Libevent v2 (evhttp), Linux, ARM, glibc2.3.4Обслуживание больших файлов (> 2 Гб) с Libevent на 32-битной системе

Я пытаюсь служить большие файлы (более 2 ГБ) с использованием evbuffer_add_file() в 32-битной системе. Libevent был скомпилирован с флагом -D_FILE_OFFSET_BITS = 64. Вот упрощенный код:

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 ...

Пропустить или сделать что-то не так? Это вообще возможно?

Заранее спасибо

+0

Так что, когда '(((ev_off_t) st.st_size) = st.st_size) 'у вас проблемы? – chux

+0

нет, в моем случае (((ev_off_t) st.st_size) == st.st_size). Я думаю, что проблема заключается в реализации sendfile системы, а не в libevent ... Решена эта проблема, реализовав потоковое использование, используя evhttp_send_reply_start()/evhttp_send_reply_chunk()/evhttp_send_reply_end() – dvinogradov

ответ

0

Как я сказал в комментарии, очевидно, что проблема не в Libevent, но в SendFile реализации системы. Так с небольшим обходным путем я нашел способ решить эту проблему с помощью evhttp_send_reply_ (начала | Кусок | конца) функция семья:

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); 
+0

Повторно открыл это, потому что это решение действительно не решает проблему. Запрос файла с такими клиентами, как VLC (потоковое видео), вызывает переполнение сервера сервера ... – dvinogradov