Führen Sie einenman 2 sendfile
durch . Sie müssen nur die Quelldatei auf dem Client und die Zieldatei auf dem Server öffnen, dann sendfile aufrufen und der Kernel wird die Daten zerhacken und verschieben.
Die portabelste Lösung besteht darin, die Datei nur in Blöcken zu lesen und die Daten dann in einer Schleife in den Socket zu schreiben (und umgekehrt beim Empfang der Datei). Sie weisen einen Puffer zu, read
in diesen Puffer und write
aus diesem Puffer in Ihren Socket (Sie könnten auch send
verwenden und recv
, die Socket-spezifische Methoden zum Schreiben und Lesen von Daten sind). Die Gliederung würde in etwa so aussehen:
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;
}
}
Lesen Sie unbedingt die Dokumentation für read
und write
sorgfältig, insbesondere im Umgang mit Fehlern. Einige der Fehlercodes bedeuten, dass Sie es einfach noch einmal versuchen sollten, zum Beispiel einfach eine Wiederholung mit einem continue
ausführen Aussage, während andere meinen, dass etwas kaputt ist und Sie aufhören müssen.
Um die Datei an einen Socket zu senden, gibt es einen Systemaufruf sendfile
das macht genau das was du willst. Es weist den Kernel an, eine Datei von einem Dateideskriptor zu einem anderen zu senden, und dann kann sich der Kernel um den Rest kümmern. Es gibt eine Einschränkung, dass der Deskriptor der Quelldatei mmap
unterstützen muss (wie in, eine tatsächliche Datei sein, kein Socket), und das Ziel muss ein Socket sein (Sie können es also nicht verwenden, um Dateien zu kopieren oder Daten direkt von einem Socket zu einem anderen zu senden); Es wurde entwickelt, um die von Ihnen beschriebene Verwendung zu unterstützen, eine Datei an einen Socket zu senden. Es hilft jedoch nicht beim Empfangen der Datei; Dafür müsstest du die Schleife selbst machen. Ich kann Ihnen nicht sagen, warum es einen sendfile
gibt anrufen, aber kein analoges recvfile
.
Achtung sendfile
ist Linux-spezifisch; es ist nicht auf andere Systeme übertragbar. Andere Systeme haben häufig ihre eigene Version von sendfile
, aber die genaue Schnittstelle kann variieren (FreeBSD, Mac OS X, Solaris).
In Linux 2.6.17 ist der splice
Systemaufruf wurde eingeführt und wird ab 2.6.23 intern verwendet, um sendfile
zu implementieren . splice
ist eine allgemeinere API als sendfile
. Für eine gute Beschreibung von splice
und tee
, siehe die ziemlich gute Erklärung von Linus selbst. Er weist darauf hin, wie man splice
verwendet ist im Grunde genau wie die Schleife oben, mit read
und write
, außer dass sich der Puffer im Kernel befindet, sodass die Daten nicht zwischen dem Kernel und dem Benutzerbereich übertragen werden müssen oder möglicherweise nicht einmal die CPU passieren (bekannt als "Zero-Copy-I/O").