为什么只有在重定向之前调用了 printf 才能使用 dup2 重定向到标准输出

Why redirecting to stdout with dup2 works only if printf was called before redirecting

我正在尝试编写一个程序,连接到在另一个终端上使用 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); 
} 

修复:

你需要告诉你的程序在写入时刷新 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 流的缓冲都相同。

当要打印 \n 时,连接到终端的文件流将被刷新。

当缓冲区已满时,连接到套接字的文件流将被刷新。

通过在 dup2(); 之前调用 printf("...\n");,您可以强制执行第一个行为。

您的程序正在执行预期的操作,dup2(sockfd,0); 将关闭客户端程序的标准输入(即在您启动程序的终端中从键盘输入的文本),并使用 sockfd 作为你的新标准输入。引自 here:

dup2() makes newfd be the copy of oldfd, closing newfd first if necessary

为了证明这一点,请尝试以这种方式启动您的侦听套接字程序:

  • cat | nc -v -l 1337 在航站楼 1
  • 在终端 2
  • 中用 strace(例如 strace a.out)包装您的套接字客户端程序

在终端 1 中输入的文本(您在其中启动侦听端口 1337 的服务器)将通过套接字发送,在终端 2 中通过 fd 0(现在是 [=11= 的副本)接收]), 并由于标准输出 (fd 1) 重定向到 sockfd.

而通过套接字发回

如果您不想将文本发送回服务器而是将其显示在终端 2 上,您可能需要注释掉 dup2(sockfd,1);。或者,您可能希望注释掉 dup2(sockfd,0); 以将客户端程序的标准输入保留在键盘上,并通过套接字发送从终端 2 输入的文本。

不确定是否回答了您的问题,但我认为 strace + cat 将帮助您了解如何就地管理各种文件描述符。

希望对您有所帮助!

我想说这可能是一个缓冲问题。正如建议的那样,一个常见的修复方法是刷新套接字,否则尝试将 stdout 的缓冲区设置为 0 可能会修复此行为。

setbuf(stdout, NULL);