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").