Перенаправить STDOUT и STDERR в сокет в C?

Я пытаюсь перенаправить STDOUT и STDERR в сокет.

Я сделал:

if(fork() == 0)
{
   dup2(newsock, STDOUT_FILENO);
   dup2(newsock, STDERR_FILENO);
   execvp();
}

Каким-то образом он показал только первую небольшую часть вывода.

например, это отображалось на «mkdir», когда я пытался выполнить ls или mkdir.

В чем проблема?

Я попробовал следующее, это работает, но я могу перенаправить только один из STDOUT или STDERR

close(1);
dup(newsock);

Большое спасибо.


person JJ Liu    schedule 11.11.2011    source источник
comment
@Dmitri, не могли бы вы помочь мне с этим? Благодарность!   -  person JJ Liu    schedule 12.11.2011


Ответы (4)


Ваше использование dup2() выглядит нормально, поэтому проблема, вероятно, в другом. Простая программа, которую я собрал для тестирования, не имеет проблем, с которыми вы столкнулись, поэтому я просто пройдусь по ее ядру (вокруг области fork()/execvp()), опустив для краткости некоторые проверки ошибок:

int    lsock, /* listening socket */
       csock; /* active connection's socket */
pid_t  cpid;  /* child process ID from fork() */
char   *cmd = "somecommand";
char   *cmd_args[] = { "somecommand",
                       "firstarg",
                       "secondarg",
                       "howevermanyargs",
                       NULL }; /* note: last item is NULL */
/*  ... 
    call socket(), bind(), listen(), etc.
    ... */

for (;;) {  /* loop, accepting connections */
  if ( (csock = accept( lsock, NULL, NULL )) == -1) exit(1);
  cpid = fork();
  if (cpid < 0) exit(1);  /* exit if fork() fails */
  if ( cpid ) {
    /* In the parent process: */
    close( csock ); /* csock is not needed in the parent after the fork */
    waitpid( cpid, NULL, 0 ); /* wait for and reap child process */
  } else {
    /* In the child process: */
    dup2( csock, STDOUT_FILENO );  /* duplicate socket on stdout */
    dup2( csock, STDERR_FILENO );  /* duplicate socket on stderr too */
    close( csock );  /* can close the original after it's duplicated */
    execvp( cmd, cmd_args );   /* execvp() the command */
  }
}

Вышеупомянутое является ядром очень простого сервера (только один клиент за раз), который, когда он получает соединение, создает новый процесс для запуска команды и отправляет свои stderr и stdout клиенту через сокет. Надеюсь, вы сможете решить свою проблему, изучив ее, но не копируйте код, не понимая, что он делает.

Попробуйте сначала подключиться к клиенту telnet... если он работает с telnet, но не с вашей клиентской программой, тогда ищите проблемы в вашей клиентской программе.

person Dmitri    schedule 12.11.2011

Вы используете dup2 правильно. Ваши вызовы записи не записывают весь буфер, который вы им предоставляете, поскольку данные еще не были получены удаленным узлом, и буфер ядра, выделенный для этого, вероятно, заполнен. Типичный размер буфера составляет 64 КБ. Вы должны убедиться, что получатель получает данные, и зациклить ваши записи. В качестве альтернативы используйте MSG_SENDALL и системный вызов send.

person Matt Joiner    schedule 12.11.2011
comment
Спасибо. оказывается, ls работает хорошо и может перечислить весь каталог. поэтому STDOUT работает нормально. но когда он должен вернуть mkdir: sampledirectory already exist, он возвращает только mkdir - person JJ Liu; 12.11.2011
comment
Привет, я только что увидел этот пост, и у меня возникла эта проблема. Когда я перенаправляю стандартный вывод на новый сокет и использую exec, иногда он сохраняет дату в буфере и печатает их через цикл. Что именно мне делать? Где сделать этот цикл WRITE?? - person Panagiss; 22.01.2020

Прочтите еще раз страницу man dup2 (отрывки):

 SYNOPSIS
   int dup2(int oldfd, int newfd);

 DESCRIPTION
   dup2() makes newfd be the copy of oldfd, closing newfd 

Так и должно быть dup2 (STDOUT_FILENO, newsock);

person Basile Starynkevitch    schedule 11.11.2011
comment
нет, это точно назад. оператор хочет, чтобы newsock стал STDOUT_FILENO. - person zwol; 12.11.2011
comment
Но он написал: я пытаюсь перенаправить STDOUT и STDERR на сокет. Я не являюсь носителем английского языка (я француз), но разве предлог to не указывает на пункт назначения? - person Basile Starynkevitch; 12.11.2011
comment
@BasileStarynkevitch Сокет является местом назначения, но это место назначения данных, что означает, что все, что пишется в стандартный вывод или стандартный вывод, должно быть отправлено в сокет. Для этого ему нужно создать дубликаты сокета для stdout и stderr. - person Dmitri; 12.11.2011
comment
@BasileStarynkevitch Это досадная ошибка на справочной странице dup2. Порядок аргументов обратный. - person Samy Dindane; 26.05.2013
comment
dup2 (STDOUT_FILENO, newsock); закроет сокет в соответствии с приведенным вами отрывком. OP хочет держать сокет открытым и перенаправлять на него вывод. Для этого ему нужно закрыть старый стандартный вывод и сделать его копией сокета с dup2 (newsock, STDOUT_FILENO); - person interjay; 20.12.2015

Я думаю, проблема в том, что stderr и stdout в некоторых системах являются одним и тем же.

person Brett Silverman    schedule 01.12.2020