Как отправить файл через именованный канал в C?

У меня есть две программы, сервер и клиент. Сервер должен прочитать файл, а затем отправить его содержимое через именованный канал клиенту. Но мой сервер читает только два символа из файла, а затем выходит. Что не так с этим кодом?

сервер.с:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define FIFO_NAME "american_maid"

int main(void)
{
    char line[300];
    int num, fd;
    FILE *fp;
    fp = fopen("out.txt","r");

    mknod(FIFO_NAME, S_IFIFO | 0666, 0);

    printf("waiting for readers...\n");
    fd = open(FIFO_NAME, O_WRONLY);
    printf("got a reader--type some stuff\n");

    while (fgets(line, sizeof(line), fp)) {
        if ((num = write(fd, line, strlen(line))) == -1)
            perror("write");
        else
            printf("speak: wrote %d bytes\n", num);
    }

    fclose(fp);

    return 0;
}

клиент.с:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define FIFO_NAME "american_maid"

int main(void)
{
    char s[300];
    int num, fd;

    mknod(FIFO_NAME, S_IFIFO | 0666, 0);

    printf("waiting for writers...\n");
    fd = open(FIFO_NAME, O_RDONLY);
    printf("got a writer\n");

    do {
        if ((num = read(fd, s, 300)) == -1)
            perror("read");
        else {
            s[num] = '\0';
            printf("tick: read %d bytes: \"%s\"\n", num, s);
        }
    } while (num > 0);

    return 0;
}

person yak    schedule 11.04.2013    source источник
comment
Если он прочитает 300 байт, клиент запишет символ после конца буфера 's'. Это не та проблема, которую вы описываете.   -  person Lee Meador    schedule 11.04.2013
comment
Извините, мое предложение (не создавать файл в клиентском коде) не помогло. Я удалил ответ, чтобы вы привлекли больше внимания.   -  person luser droog    schedule 11.04.2013
comment
Является ли «текущая» папка одинаковой для обеих программ? В имени файла FIFO нет пути (например, /tmp/american_maid)   -  person Lee Meador    schedule 11.04.2013
comment
нет, они в одном каталоге (клиент, сервер и файл с именем american_maid)   -  person yak    schedule 11.04.2013
comment
Используйте функции ferror() и feof(), чтобы различать состояние ошибки и состояние конца файла, когда fgets() возвращает NULL, и посмотрите, есть ли интересный код ошибки.   -  person Lee Meador    schedule 12.04.2013
comment
Убедитесь, что ваши open() звонки сработали. Обратите внимание, что если вы читаете 300 байтов в клиенте, запись в s[num] запишет за конец вашего массива; это может испортить количество прочитанных байтов данных.   -  person Jonathan Leffler    schedule 12.04.2013
comment
@Jonathan Leffler: проверил открытие - открытие сработало. Итак, что вы предлагаете написать s[num], как я могу это сделать? Я все еще не могу заставить это работать должным образом :/   -  person yak    schedule 12.04.2013


Ответы (2)


Когда я запускаю код, показанный ниже, используя последовательность команд:

$ ln -s server.c out.txt
$ ./client &
$ ./server
$

Я получаю копию исходного кода, распечатанную клиентской программой. Точно так же, когда я запускаю команды, используя:

$ ./server &
$ ./client
$

Пересмотренный код не сильно изменился. Он избегает циклов do { } while(...) — они редко бывают действительно полезными — и очень внимательно следит за тем, чтобы буферы не переполнялись. В коде также удалены лишние заголовки.

server.c

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define FIFO_NAME "american_maid"

int main(void)
{
    const char infile[] = "out.txt";
    FILE *fp = fopen(infile, "r");

    if (fp == 0)
    {
        fprintf(stderr, "Failed to open %s for reading", infile);
        return(1);
    }

    mknod(FIFO_NAME, S_IFIFO | 0666, 0);

    printf("waiting for readers...\n");
    int fd = open(FIFO_NAME, O_WRONLY);
    if (fd > 0)
    {
        char line[300];
        printf("got a reader--type some stuff\n");

        while (fgets(line, sizeof(line), fp))
        {
            int len = strlen(line);
            int num = write(fd, line, len);
            if (num != len)
                perror("write");
            else
                printf("speak: wrote %d bytes\n", num);
        }
        close(fd);
    }

    fclose(fp);

    return 0;
}

клиент.с

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

#define FIFO_NAME "american_maid"

int main(void)
{
    const char outfile[] = "client.out";
    FILE *fp = fopen(outfile, "w");

    if (fp == 0)
    {
        fprintf(stderr, "Failed to open %s for writing\n", outfile);
        return 1;
    }

    printf("waiting for writers...\n");
    mknod(FIFO_NAME, S_IFIFO | 0666, 0);

    int fd = open(FIFO_NAME, O_RDONLY);
    if (fd > 0)
    {
        int num;
        char s[300];
        printf("got a writer\n");

        while ((num = read(fd, s, sizeof(s))) > 0)
        {
            printf("tick: read %d bytes: \"%.*s\"\n", num, num, s);
            fprintf(fp, "%.*s", num, s);
        }

        close(fd);
    }
    fclose(fp);

    return 0;
}

Обратите внимание, что эта версия записывает свой вывод в файл client.out; даже если для обработки задан файл с очень длинными строками (2049 байт, включая новую строку в конце), вывод в client.out точно соответствует вводу в out.txt.

person Jonathan Leffler    schedule 12.04.2013

Удалите строку mknod(FIFO_NAME, S_IFIFO | 0666, 0); из файла client.c. Тогда программа будет работать как положено. Сервер создаст файл и отправит содержимое файла в fifo.

person Rishabh Mishra    schedule 11.04.2013
comment
делал уже, не помогло (проблема такая же после того как я закомментировал эту строчку в клиентском коде) - person yak; 11.04.2013
comment
На самом деле, если обе программы пытаются создать один и тот же файл, это неплохо, это означает, что любая из них может начаться первой, а игнорирование возврата mknod означает, что он может просто создать там обычный файл. Это не обычно, но и неплохо в данном случае. - person jthill; 12.04.2013