Proveďte man 2 sendfile
. Stačí otevřít zdrojový soubor na klientovi a cílový soubor na serveru, pak zavolat sendfile a jádro data rozseká a přesune.
Nejpřenosnějším řešením je pouze číst soubor po částech a poté zapisovat data do soketu ve smyčce (a podobně i obráceně při příjmu souboru). Vy přidělujete vyrovnávací paměť, read
do této vyrovnávací paměti a write
z tohoto bufferu do vašeho soketu (můžete také použít send
a recv
, což jsou způsoby zápisu a čtení dat specifické pro socket). Obrys by vypadal asi takto:
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;
}
}
Nezapomeňte si přečíst dokumentaci pro read
a write
opatrně, zvláště při manipulaci s chybami. Některé z chybových kódů znamenají, že byste to měli zkusit znovu, například opakovat opakování s continue
prohlášení, zatímco ostatní znamenají, že je něco nefunkční a musíte s tím přestat.
Pro odeslání souboru do soketu existuje systémové volání sendfile
to dělá přesně to, co chcete. Říká jádru, aby poslalo soubor z jednoho deskriptoru souboru do druhého, a pak se jádro může postarat o zbytek. Existuje upozornění, že deskriptor zdrojového souboru musí podporovat mmap
(stejně jako v případě, být skutečným souborem, nikoli soketem) a cílem musí být soket (nemůžete jej tedy použít ke kopírování souborů nebo odesílání dat přímo z jednoho soketu do druhého); je navržen tak, aby podporoval použití, které popisujete, odesílání souboru do soketu. Nepomůže to však s přijetím souboru; na to budete muset udělat smyčku sami. Nemohu vám říci, proč existuje sendfile
volání, ale žádné analogové recvfile
.
Pozor, sendfile
je specifický pro Linux; není přenosný na jiné systémy. Jiné systémy mají často vlastní verzi sendfile
, ale přesné rozhraní se může lišit (FreeBSD, Mac OS X, Solaris).
V Linuxu 2.6.17 splice
bylo zavedeno systémové volání a od 2.6.23 se používá interně k implementaci sendfile
. splice
je API pro obecnější účely než sendfile
. Pro dobrý popis splice
a tee
, viz docela dobré vysvětlení od samotného Linuse. Poukazuje na to, jak používat splice
je v podstatě stejný jako smyčka výše s použitím read
a write
, kromě toho, že vyrovnávací paměť je v jádře, takže data se nemusí přenášet mezi jádrem a uživatelským prostorem, nebo dokonce nikdy nemusí projít CPU (známé jako "nulové kopie I/O").