Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to receive a file using sendfile?

Tags:

c

linux

sockets

send a file with sendfile is easy:

stat(fd,&filestat);
sendfile(sockfd,fd,0,filestat.len)

but how to receive a file using sendfile? since I don't know the length of the file, should I send the file length first?

 sendfile(fd, sockfd,0, ??)

There seems to be two ways of doing this:

  1. send the filestat.len first

    //send end
    write(sockfd,filestat.len);
    sendfile(sockfd,fd,&offset,filestat.len);
    //receive end
    read(sockfd,&len);
    sendfile(fd,sockfd,&offset,len)
    
  2. use a loop in the receive end:

    //receive end
    while(sendfile(fd,sockfd,&offset,BUF_LEN) > 0) {
        offset += BUF_LEN;
    }
    

Which one is better? Should I handle the buffer length specifically? Is there any problem in the first way when the file is quite large?

(I really like the mac os version of sendfile, it will send till the end of file if count is 0)

like image 714
Lynton Avatar asked Nov 27 '13 06:11

Lynton


People also ask

Why use sendfile?

sendfile() copies data between one file descriptor and another. Because this copying is done within the kernel, sendfile() is more efficient than the combination of read(2) and write(2), which would require transferring data to and from user space.

What is Sendfile?

SendFiles is an encrypted file service that allows you to securely share sensitive documents and large files online. Using SendFiles you can transfer files up to 2GB in size and the service offers more than one solution for sending and receiving files.


2 Answers

This is a great question!

The other posters are correct: you could call read() or recv() (what is the difference?) repeatedly until either of those returns 0, which indicates end of file (EOF).

However! You should consider first passing the size of the file, as a good practice. This would allow your client to anticipate exactly how much data is coming through the socket, figure out if (for example) there is enough disk space, etc. It allows you to have some sort of sanity-checking before committing to downloading whatever the server tries to send.

(This has its own perils. What if the server sends the wrong size?)

You might also consider sending the file in chunks. This way, if there is an interruption, you have a greater granularity when figuring out how much you've transferred. (The kernel does this for you anyway. But food for thought.)

Sending a single integer (a file size) over the network isn't too difficult, but there are a few tricks to be aware of if you are very worried about portability.

Good luck!

like image 100
poundifdef Avatar answered Oct 20 '22 21:10

poundifdef


According to manual to sendfile():

RETURN VALUE
       If  the  transfer was successful, the number of bytes written to out_fd
       is returned.  On error, -1 is returned, and errno is set appropriately.

So just use it in a way you use read() for sockets: repeat reading as much as you need until the whole amount of data you need will be read. Sure, paying attention to the cases of -1 and 0 results.

like image 44
Michael Avatar answered Oct 20 '22 22:10

Michael