0

I'm writing a simple TCP server for file transfer. The server uses an epoll objects to monitor incoming connections and data and I would like to use the sendfile() method in order to allow faster file transfer.

Trying to use sendfile() on an fd returned by epoll_wait() results in errno 29.

I'm able to read from this fd using traditional read() method, which means the fd is not corrupted (Using fstat() on the fd returns 0 size though).

I'm attaching the code snippet of the sendfile() alone, if necessary I can share the entire code:

epoll_wait:

while (true) {
    int activeFds = epoll_wait(epollfd, events, MAXEVENTS, -1);

    if (-1 == activeFds) {
        printf("Epoll wait failed %s\n", strerror(errno));
        res = false;
        goto Exit;
    }

    res = iterateActiveFds(activeFds, events);
    if (false == res) {
        goto Exit;
    }
}

Find fd:

bool iterateActiveFds(int activeFds, struct epoll_event events[]) {
    bool res = true;
    int i;

    for (i = 0; i < activeFds; ++i) {
        if (events[i].data.ptr == &sockFd) {
            res = acceptNewConnection(i);
        } else {
            res = handelIncomingData(i);
        }
    }

    return res;
}

Handle connection:

bool handelIncomingData(int i) {
    bool res = true;

    /* Handle data send from client */
    int* connData = (int*) events[i].data.ptr;
    if ((events[i].events & EPOLLHUP) || (events[i].events & EPOLLERR)) {
        /* Error occured */
        printf("Epoll error occurred\n");
        close(*connData);
        free(connData);
    } else if (EPOLLIN == events[i].events) {
        /* Delete the read event */
        res = modifyEpoll(epollfd, EPOLL_CTL_DEL, *connData, 0, 0);
        if (false == res) {
            goto Exit;
        }

        /* Handle write in a new thread */
        //TODO: integrate threadpool
        pthread_t thrd;
        if (0 != pthread_create(&thrd, NULL, threadMethod, connData)) {
            res = false;
        }
        pthread_join(thrd, NULL);
    }

    Exit: return res;
}

sendfile():

void* threadMethod(void* connFd) {
int* connData = (int*) connFd;
int fd = *connData;

/* Transfer using sendfile */
struct stat stat_buf;
off_t offset = 0;

fstat(fd, &stat_buf);
int res = sendfile(file, fd, &offset, 10);
printf("res = %d\n", res);
printf("errno = %d\n", errno);
…
Return NULL;
}

sendfile() returns -1 and errno is set to 29.

quinz
  • 1,282
  • 4
  • 21
  • 33
  • 5
    How? Why? From [man epoll_wait](http://man7.org/linux/man-pages/man2/epoll_wait.2.html) `When successful, epoll_wait() returns the number of file descriptors ready for the requested I/O`. The epoll_wait returns the count of file descriptors, not a file descriptor. Why would you call any file descriptor operation on a count of file descriptors? `if necessary` it's necessary to share at least the epoll_wait call and how you open the fd. – KamilCuk Jul 05 '19 at 09:38
  • I'm sorry, I explained this part wrong. yes, I'm aware that epoll_wait returns the number of active fds. I'm then scanning the data structure containing the fds to handle the appropriate one. I'm editing the question with relevant code, as you requested. – Barak Sason Rofman Jul 05 '19 at 09:43
  • 1
    Errno 29 is `ESPIPE` (Using `strerror()` or `perror()` to get a human-readable error message and not just a cryptic number is something you should be aiming for). From the `sendfile()` manpage: *offset is not NULL but the input file is not seek(2)-able.* – Shawn Jul 05 '19 at 09:45
  • Based on variable names, you might have your arguments reversed... – Shawn Jul 05 '19 at 09:47
  • I've edited and added the important bits of the code, hope now it's clearer. Thank you all for your help. – Barak Sason Rofman Jul 05 '19 at 09:49
  • @Shawn The first argument is the destination file and the second is source file, no? – Barak Sason Rofman Jul 05 '19 at 10:01
  • Yes. Are you trying to use `sendfile()` to **read** from a socket? Also from the manpage: *The in_fd argument must correspond to a file which supports mmap(2)-like operations (i.e., it cannot be a socket).* If `fd` is a socket, and you'd checked that `fstat()` call for errors, you'd probably see an issue there too... – Shawn Jul 05 '19 at 10:04
  • @Shawn Yes, I'm trying to read from the fd and yes fstat() is giving incorrect data too… What am I missing? I want to write the data send by the client to a file opened in the server. – Barak Sason Rofman Jul 05 '19 at 10:21
  • `read()` from the socket and `write()` to the file. – Shawn Jul 05 '19 at 10:34
  • @Shawn it's impossible to do that with sendfile()? – Barak Sason Rofman Jul 05 '19 at 10:36
  • Like the documentation says, you can't use `sendfile()` to read from a socket. – Shawn Jul 05 '19 at 10:37
  • @Shawn Is there a viable way to copy the data the client sends and write it to a file using sendfile() or read-write is the only option? – Barak Sason Rofman Jul 05 '19 at 10:58
  • @BarakSasonRofman **no**. sendfile requires that the source is mmappable. You cannot memory map the socket as the data is on the sending machine! – Antti Haapala -- Слава Україні Jul 05 '19 at 11:01
  • OK. Thank you all for your help. – Barak Sason Rofman Jul 05 '19 at 11:17
  • maybe a pair of splice(2) (one from socket to pipe and one from pipe to file) could be benchmarked against read()/write()? – A.B Jul 07 '19 at 01:05
  • @A.B Thank you for replying. Could you maybe write a simple example? I'm not sure how such a thing would be done. Much appreciated. – Barak Sason Rofman Jul 08 '19 at 13:44

0 Answers0