Почему перенаправление на стандартный вывод с dup2 работает, только если printf был вызван перед перенаправлением - PullRequest
2 голосов
/ 08 апреля 2019

Я пытаюсь написать программу, которая подключается к серверу, открытому с nc -v -l 1337 на другом терминале, и перенаправляет stdin, stdout, stderr в сокет. Это означает, что другой терминал будет писать в сокет, а моя программа будет читать его с getchar() и отвечать с помощью printf().

Я столкнулся с чем-то странным - все работает нормально, если я закомментирую первое использование printf (до dup2(sockfd,1)). Если нет, печать ничего не делает. Что может вызвать это?

int main() 
{ 
    int sockfd, connfd; 
    struct sockaddr_in servaddr, cli; 

    // socket create and varification 
    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    bzero(&servaddr, sizeof(servaddr)); 

    servaddr.sin_family = AF_INET; 
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    servaddr.sin_port = htons(1337); 

    connect(sockfd, (SA*)&servaddr, sizeof(servaddr));
    dup2(sockfd,0);
//---------------------------
//  printf("%s\n","hi" );
//---------------------------
    dup2(sockfd,1);
    dup2(sockfd,2);

    char buff[80]; 
    int n; 
    while(1){
        n = 0;
        while ((buff[n++] = getchar()) != '\n');
        buff[n-1] = 0;
        printf("message %s excepted\n",buff ); 
    }
    // close the socket 
    close(sockfd); 
} 

Ответы [ 3 ]

0 голосов
/ 08 апреля 2019

Ваша программа делает то, что ожидается, dup2(sockfd,0); закроет стандартный ввод вашей клиентской программы (т. Е. Текст, введенный с клавиатуры в терминале, в котором вы запустили программу), и используйте sockfd в качестве вашегоновый стандартный ввод.Цитируется здесь :

dup2 () делает newfd копией oldfd, сначала закрывая newfd, если необходимо

Чтобы доказать это,попробуйте запустить программу прослушивания сокетов следующим образом:

  • cat | nc -v -l 1337 в терминале 1
  • оберните свою клиентскую программу сокета с помощью strace (например, strace a.out) в терминале 2

Текст, набранный в терминале 1 (где вы запустили сервер, прослушивающий порт 1337), будет отправлен через сокет, полученный в терминале 2 через fd 0 (который теперь является копией sockfd) и отправлено обратно через сокет из-за перенаправления стандартного вывода (fd 1) на sockfd.

Вы можете закомментировать dup2(sockfd,1);, если не хотите отправлять текст обратносервер, но вместо этого он отображается в терминале 2.В качестве альтернативы вы можете закомментировать dup2(sockfd,0);, чтобы сохранить стандартный ввод вашей клиентской программы на клавиатуре, и отправить текст, введенный из терминала 2, через сокет.

Не уверен, что отвечает на ваш вопрос, но ядумаю, strace + cat поможет вам понять, как вы можете управлять различными дескрипторами файлов на месте.

Надеюсь, это поможет!

0 голосов
/ 08 апреля 2019

Я бы сказал, что это может быть проблема буферизации.Обычное исправление, как предлагается, - очистка сокета, в противном случае попытка установить буфер для stdout в 0 может исправить это поведение.

setbuf(stdout, NULL);
0 голосов
/ 08 апреля 2019

Fix:

Вы должны указать вашей программе сбросить stdout при записи в нее:

    while(1){
        n = 0;
        while ((buff[n++] = getchar()) != '\n');
        buff[n-1] = 0;
        printf("message %s excepted\n",buff ); 

        /* ask the system to flush stdout */
        fflush(stdout);
    }

Причина: (я просто догадываюсь)

Не все потоки FILE буферизуются одинаково.

Поток FILE, подключенный к терминалу, будет сброшен при печати \n.

Поток FILE, подключенный к сокету, будет сброшен при заполнении буфера.

Вызывая printf("...\n"); до dup2();, вы заставляете первое поведение.

...