【问题标题】:Why redirecting to stdout with dup2 works only if printf was called before redirecting为什么使用 dup2 重定向到标准输出仅在重定向之前调用 printf 时才有效
【发布时间】:2019-04-08 08:30:12
【问题描述】:

我正在尝试编写一个程序,该程序连接到在另一个终端上使用nc -v -l 1337 打开的服务器,并将标准输入、标准输出、标准错误重定向到套接字。 意思是,另一个终端将写入套接字,我的程序将使用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); 
} 

【问题讨论】:

    标签: c printf stdout dup2


    【解决方案1】:

    修复:

    您需要告诉您的程序在写入时刷新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");,您强制执行第一个行为。

    【讨论】:

    • 这个解释听起来很合理(虽然如果我能在某个地方找到它会很好:-),谢谢,这就是我正在寻找的答案!
    【解决方案2】:

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

    setbuf(stdout, NULL);
    

    【讨论】:

      【解决方案3】:

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

      dup2() 使 newfd 成为 oldfd 的副本,必要时先关闭 newfd

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

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

      在终端 1(您启动了侦听端口 1337 的服务器)中输入的文本将通过套接字发送,在终端 2 通过 fd 0(现在是 sockfd 的副本)接收,然后发回由于标准输出(fd 1)重定向到sockfd,因此通过套接字。

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

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

      希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 2018-04-05
        • 1970-01-01
        • 1970-01-01
        • 2019-09-23
        • 1970-01-01
        • 1970-01-01
        • 2020-03-26
        • 1970-01-01
        相关资源
        最近更新 更多