Работает ли Linux splice (2) при соединении из TCP-сокета? - PullRequest
9 голосов
/ 20 мая 2009

Я написал небольшую программу для развлечения, которая передает файлы по TCP на C в Linux. Программа читает файл из сокета и записывает его в файл (или наоборот). Первоначально я использовал чтение / запись, и программа работала правильно, но затем я узнал о splice и хотел попробовать.

Код, который я написал с помощью splice, отлично работает при чтении из stdin (перенаправленный файл) и записи в сокет TCP, но сразу завершается неудачно, когда splice устанавливает errno в EINVAL при чтении из сокета и записи в stdout. Страница man утверждает, что EINVAL устанавливается, когда ни один дескриптор не является каналом (не регистром), смещение передается для потока, который не может быть найден (без смещений), или файловая система не поддерживает сплайсинг, что приводит меня на мой вопрос: означает ли это, что TCP может соединять от канала, но не до ?

Я включил приведенный ниже код (за исключением кода обработки ошибок) в надежде, что я только что сделал что-то не так. Он основан на примере Wikipedia для соединения .

static void splice_all(int from, int to, long long bytes)
{
    long long bytes_remaining;
    long result;

    bytes_remaining = bytes;
    while (bytes_remaining > 0) {
        result = splice(
            from, NULL,
            to, NULL,
            bytes_remaining,
            SPLICE_F_MOVE | SPLICE_F_MORE
        );

        if (result == -1)
            die("splice_all: splice");

        bytes_remaining -= result;
    }
}

static void transfer(int from, int to, long long bytes)
{
    int result;
    int pipes[2];

    result = pipe(pipes);

    if (result == -1)
        die("transfer: pipe");

    splice_all(from, pipes[1], bytes);
    splice_all(pipes[0], to, bytes);

    close(from);
    close(pipes[1]);
    close(pipes[0]);
    close(to);
}

С другой стороны, я думаю, что вышеприведенное блокирует первый splice_all, когда файл достаточно большой из-за заполнения канала (?), Поэтому у меня также есть версия кода, которая fork s для чтения и записи из канала одновременно, но он имеет ту же ошибку, что и эта версия, и его труднее читать.

РЕДАКТИРОВАТЬ: Моя версия ядра 2.6.22.18-co-0.7.3 (работает на coLinux в XP.)

Ответы [ 2 ]

8 голосов
/ 20 мая 2009

Какая версия ядра это? Linux поддерживает сращивание из сокета TCP с 2.6.25 (commit 9c55e01c0 ), поэтому, если вы используете более раннюю версию, вам не повезло.

2 голосов
/ 28 декабря 2010

Вам необходимо splice_all от pipes[0] до to каждый раз, когда вы делаете однократное сращивание от from до pipes[1] (splice_all - для количества байтов, равного просто прочитанный последним единственным соединением). Причина: каналы представляют собой конечный буфер памяти ядра. Таким образом, если количество байтов больше этого, вы навсегда заблокируетесь в вашем splice_all(from, pipes[1], bytes).

...