Send and Receive a file in socket programming in Linux with C/C++ (GCC/G++)
c, c++, linux, network-programming, sockets
Solution
The most portable solution is just to read the file in chunks, and then write the data out to the socket, in a loop (and likewise, the other way around when receiving the file). You allocate a buffer, `read` into that buffer, and `write` from that buffer into your socket (you could also use `send` and `recv`, which are socket-specific ways of writing and reading data). The outline would look something like this:
while (1) {
// Read data into buffer. We may not have enough to fill up buffer, so we
// store how many bytes were actually read in bytes_read.
int bytes_read = read(input_file, buffer, sizeof(buffer));
if (bytes_read == 0) // We're done reading from the file
break;
if (bytes_read < 0) {
// handle errors
}
// You need a loop for the write, because not all of the data may be written
// in one call; write will return how many bytes were written. p keeps
// track of where in the buffer we are, while we decrement bytes_read
// to keep track of how many bytes are left to write.
void *p = buffer;
while (bytes_read > 0) {
int bytes_written = write(output_socket, p, bytes_read);
if (bytes_written <= 0) {
// handle errors
}
bytes_read -= bytes_written;
p += bytes_written;
}
}
Make sure to read the documentation for `read` and `write` carefully, especially when handling errors. Some of the error codes mean that you should just try again, for instance just looping again with a `continue` statement, while others mean something is broken and you need to stop.
For sending the file to a socket, there is a system call, `sendfile` that does just what you want. It tells the kernel to send a file from one file descriptor to another, and then the kernel can take care of the rest. There is a caveat that the source file descriptor must support `mmap` (as in, be an actual file, not a socket), and the destination must be a socket (so you can't use it to copy files, or send data directly from one socket to another); it is designed to support the usage you describe, of sending a file to a socket. It doesn't help with receiving the file, however; you would need to do the loop yourself for that. I cannot tell you why there is a `sendfile` call but no analogous `recvfile`.
Beware that `sendfile` is Linux specific; it is not portable to other systems. Other systems frequently have their own version of `sendfile`, but the exact interface may vary (FreeBSD, Mac OS X, Solaris).
In Linux 2.6.17, the `splice` system call was introduced, and as of 2.6.23 is used internally to implement `sendfile`. `splice` is a more general purpose API than `sendfile`. For a good description of `splice` and `tee`, see the rather good explanation from Linus himself. He points out how using `splice` is basically just like the loop above, using `read` and `write`, except that the buffer is in the kernel, so the data doesn't have to transferred between the kernel and user space, or may not even ever pass through the CPU (known as "zero-copy I/O").
Problem
I would like to implement a client-server architecture running on Linux using sockets and C/C++ language that is capable of sending and receiving files. Is there any library that makes this task easy? Could anyone please provide an example?