在32位系统上使用libevent提供大文件(> 2GB)

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

我正在尝试使用32位系统上的evbuffer_add_file()来提供大文件(超过2GB)。 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 …

我想念或做错事吗? 有可能吗?

提前致谢

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

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