Самое переносимое решение - просто прочитать файл порциями, а затем записать данные в сокет в цикле (и аналогично, наоборот при получении файла). Вы выделяете буфер, read
в этот буфер, и write
из этого буфера в свой сокет (вы также можете использовать send
и recv
, которые являются сокетом -специфичные способы записи и чтения данных). Контур будет выглядеть примерно так:
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;
}
}
Обязательно внимательно прочитайте документацию по read
и write
, особенно при обработке ошибок. Некоторые из кодов ошибок означают, что вы должны просто повторить попытку, например, просто повторить цикл с оператором continue
, в то время как другие означают, что что-то сломано, и вам нужно остановиться.
Для отправки файла в сокет существует системный вызов sendfile
, который делает именно то, что вы хотите. Он говорит ядру отправить файл из одного файлового дескриптора в другой, и тогда ядро может позаботиться обо всем остальном. Существует предостережение о том, что дескриптор исходного файла должен поддерживать mmap
(например, фактический файл, а не сокет), а местом назначения должен быть сокет (поэтому его нельзя использовать для копирования файлов или отправки данных. непосредственно из одной розетки в другую); он предназначен для поддержки описанного вами способа отправки файла в сокет. Однако это не помогает при получении файла; вам нужно было бы сделать цикл самостоятельно для этого. Я не могу сказать вам, почему есть sendfile
звонок, но нет аналогичного recvfile
.
Остерегайтесь, что sendfile
зависит от Linux; он не переносим на другие системы. Другие системы часто имеют собственную версию sendfile
, но точный интерфейс может отличаться ( FreeBSD , Mac OS X , Solaris ).
В Linux 2.6.17 системный вызов splice
был введен , а с 2.6.23 используется внутренне для реализации sendfile
, splice
является более универсальным API, чем sendfile
. Хорошее описание splice
и tee
см. В довольно хорошем объяснении самого Линуса . Он указывает, что использование splice
в основном похоже на цикл выше, используя read
и write
, за исключением того, что буфер находится в ядре, поэтому данные не должны передаваться между ядром и пользовательским пространством, или может даже не проходить через ЦП (известный как «ввод-вывод нулевой копии»).