Faites un man 2 sendfile
. Il vous suffit d'ouvrir le fichier source sur le client et le fichier de destination sur le serveur, puis d'appeler sendfile et le noyau hachera et déplacera les données.
La solution la plus portable consiste simplement à lire le fichier en morceaux, puis à écrire les données sur le socket, dans une boucle (et de même, dans l'autre sens lors de la réception du fichier). Vous allouez un buffer, read
dans ce tampon, et write
de ce tampon dans votre socket (vous pouvez également utiliser send
et recv
, qui sont des manières spécifiques aux sockets d'écrire et de lire des données). Le contour ressemblerait à ceci :
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;
}
}
Assurez-vous de lire la documentation pour read
et write
avec soin, en particulier lors de la manipulation des erreurs. Certains des codes d'erreur signifient que vous devez simplement réessayer, par exemple simplement en boucle avec un continue
déclaration, tandis que d'autres signifient que quelque chose est cassé et que vous devez arrêter.
Pour envoyer le fichier à une socket, il y a un appel système, sendfile
ça fait exactement ce que tu veux. Il indique au noyau d'envoyer un fichier d'un descripteur de fichier à un autre, puis le noyau peut s'occuper du reste. Il y a une mise en garde que le descripteur de fichier source doit prendre en charge mmap
(comme dans, être un fichier réel, pas un socket), et la destination doit être un socket (vous ne pouvez donc pas l'utiliser pour copier des fichiers ou envoyer des données directement d'un socket à un autre) ; il est conçu pour prendre en charge l'utilisation que vous décrivez, consistant à envoyer un fichier à un socket. Cependant, cela n'aide pas à recevoir le fichier; vous auriez besoin de faire la boucle vous-même pour cela. Je ne peux pas vous dire pourquoi il y a un sendfile
appel mais pas de recvfile
analogue .
Attention, sendfile
est spécifique à Linux ; il n'est pas portable sur d'autres systèmes. D'autres systèmes ont souvent leur propre version de sendfile
, mais l'interface exacte peut varier (FreeBSD, Mac OS X, Solaris).
Sous Linux 2.6.17, le splice
l'appel système a été introduit, et à partir de 2.6.23 est utilisé en interne pour implémenter sendfile
. splice
est une API plus générale que sendfile
. Pour une bonne description de splice
et tee
, voir la plutôt bonne explication de Linus lui-même. Il souligne comment l'utilisation de splice
est fondamentalement comme la boucle ci-dessus, en utilisant read
et write
, sauf que le tampon est dans le noyau, de sorte que les données n'ont pas à être transférées entre le noyau et l'espace utilisateur, ou peuvent même ne jamais passer par le CPU (appelé "E/S sans copie").