由于dup2,scanf无法正常工作

时间:2015-08-20 19:48:27

标签: c linux sockets

在复制套接字描述符后,Scanf(实际上我尝试过任意数量的scanf' s)跳过。以下是您可以编译以查看问题的代码:

#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>


int main()
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    int dupfd = 0;
    dup2(listenfd, dupfd);

    puts("Type something to exit");
    int exit = -1;
    scanf("%d", &exit);
    puts("Got exit signal");

    return 0;
}

2 个答案:

答案 0 :(得分:2)

我猜错误是,你应该dup2()连接套接字,而不是监听套接字。以下示例适用于我(在端口32000中连接telnet进行测试):

#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <strings.h>


int main()
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    int dupfd = 0;

   int connfd,n;
   struct sockaddr_in servaddr,cliaddr;
   socklen_t clilen;

   bzero(&servaddr,sizeof(servaddr));
   servaddr.sin_family = AF_INET;
   servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
   servaddr.sin_port=htons(32000);
   bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

   listen(listenfd,1024);
   connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);
    dup2(connfd, dupfd);


    puts("Type something to exit");
    int exit = -1;
    scanf("%d", &exit);
    printf("%d\n", exit);
    puts("Got exit signal");

    return 0;
}

请记住,在套接字上使用scanf()是一个坏主意,因为scanf()只阻塞一次;也就是说,一旦它收到消息的任何部分,即使是单个字符,它也会解析它收到的字符串。如果不完整,解析将是不正确的。

网络套接字可以以任意数量的较小块传递消息。通常它会以数据包形式发送它,但它甚至可以逐个字符地发送。此示例“倾向于与Telnet一起工作”,因为客户端仅在按Enter时发送键入的字符串。但对于实际的网络服务器来说,这是完全不够的。

答案 1 :(得分:2)

如果您仅使用read代替scanf并控制错误,则会更清楚:

#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>


int main()
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    int dupfd = 0;
    dup2(listenfd, dupfd);

    puts("Type something to exit");
    int exit = -1;
    //scanf("%d", &exit);
    char buf[16];
    int n = read(0, buf, 16);
    printf("Got %d (%d)\n", n, errno);
    puts("Got exit signal");

    return 0;
}

程序输出Got -1 (57),因为在dup2之后,您正试图从侦听套接字读取。因此,您会收到原因为ENOTCONN的读取错误:Socket未连接

所有这些都是因为dup2而发生的。以下是手册页中的一些摘录: dup()系统调用复制现有对象描述符...在dup2()中,指定新描述符newd的值。如果此描述符已被使用且oldd!= newd,则首先释放描述符,就好像已使用close(2)系统调用一样。

0,1和2是标准文件描述符:1 =输入,2 =输出,3 =错误。因此,int dupfd = 0; dup2(listenfd, dupfd);会关闭标准输入并将其作为listenfd的副本。之后,所有scanfread(0, ...)都会对listendfd采取行动。恕我直言,你想做的只是:

int dupfd = dup(listenfd);

这样你实际上得到listenfd的副本,但是在未使用的文件描述符上而不是重用标准输入。