【问题标题】:non-blocking socket and io非阻塞套接字和io
【发布时间】:2012-03-17 08:47:35
【问题描述】:

我的客户端程序首先连接到服务器,然后服务器发送一个文件,然后客户端输入一个命令并将其发送到服务器并接收结果。我想通过输入命令的非阻塞 io 和非阻塞套接字来实现这一点。 但是我的程序无法从标准输入读取输入。当我在 read_set 中仅设置标准输入时它可以工作,但在这种情况下它不起作用。 这是我的客户代码:

main (int argc, char *argv[])
{
   int  len, rc,max_sd;
   int  sockfd;
   int on=1;
   char recv_buff[100]="";
   char command[100]="";
   char result[100]="";
   char instr_buff[100]="";
   struct sockaddr_in   addr;
   struct timeval timeout;
   fd_set read_set,write_set;

   /*************************************************/
   /* Create an AF_INET stream socket               */
   /*************************************************/
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   if (sockfd < 0)
   {
      perror("socket() failed");
      exit(-1);
   }
   rc = setsockopt(sockfd, SOL_SOCKET,  SO_REUSEADDR,(char *)&on, sizeof(on)); // Allow socket descriptor to be reuseable
    if(rc<0)
    {
        perror("setsockopt() failed");
        close(sockfd);
        exit(-1);
    }
    //Set socket to be non-blocking.
    rc = ioctl(sockfd, FIONBIO, (char *)&on);
    if(rc<0)
    {
        perror("ioctl() faild");
        close(sockfd);
        exit(-1);   
    }
   //making stdin to be non-blocking
   rc = ioctl(0, FIONBIO, (char *)&on);
   if(rc<0)
   {
        perror("ioctl() faild");
        close(sockfd);
        exit(-1);   
   } 
   /*************************************************/
   /* Initialize the socket address structure       */
   /*************************************************/
   memset(&addr, 0, sizeof(addr));
   addr.sin_family      = AF_INET;
   addr.sin_addr.s_addr = htonl(INADDR_ANY);
   addr.sin_port        = htons(SERVER_PORT);

   /*************************************************/
   /* Connect to the server                         */
   /*************************************************/
   int count=0;

   do
   {
       rc = connect(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in));
       count++;
   }while(rc<0&&count<20);
   if(rc<0)
   {
    printf("can not connect to server\n");
    exit(-1);   
   }
   else
        printf("Connect completed.\n"); 

   timeout.tv_sec = 60;
   timeout.tv_usec = 0;
   FD_ZERO(&read_set);
   FD_ZERO(&write_set);
   FD_SET(sockfd,&read_set);
   FD_SET(0,&read_set);
   while(1)
   {   
    rc=select(sockfd+1,&read_set,&write_set,NULL,&timeout);
    if(rc<0)
    {
        perror("select failed()\n");
        break;
    }
    if(rc==0)
    {
        printf("select timed out.End Program\n");
        break;

    }
    if(FD_ISSET(sockfd,&write_set))
    {
        //sending data
        rc=send(sockfd,command,strlen(command)+1,0);
        if(rc<0)
        {
            perror("sending command failed");
            break;
        }
        if(rc==0)
        {
             printf("connection closed by server\n");
             break;
        }
        printf("command send\n");
        FD_CLR(sockfd,&write_set);

    }   
    if(FD_ISSET(sockfd,&read_set))
    {
        //receiving data
        rc=recv(sockfd,recv_buff,sizeof(recv_buff),0);
        if (rc < 0)
        {
            if(errno == EAGAIN||errno == EWOULDBLOCK)
            {
                printf("no message\n");
                continue;
            }
            perror("  recv() failed");
            break;
        }
        if(rc==0)
        {
         printf("connection closed by server\n");   
         break;   
        }
        //data received
        printf("data received\n");
        //check to see if is instr.conf or result
        if(recv_buff[strlen(recv_buff)-1]=='@') //is file
        {
         //save to instr_buff
         strcpy(instr_buff,recv_buff);   
        }
        else
        {
         //result received
         printf("Result:\n");
         printf("%s",recv_buff);   
        }
        printf("Enter Command:\n");
    }
    if(FD_ISSET(0,&read_set))
    {
        scanf("%s",command);
        FD_SET(sockfd,&write_set);
    }

   }//end of while

    close(sockfd);   
}

【问题讨论】:

    标签: c sockets io nonblocking


    【解决方案1】:

    记住select 更新你传入的读取、写入和异常fd_set 项。这意味着如果您选择两个可能的输入并且其中一个已准备好,则读取set 中只设置了一个位:

    /* read_set has two bits set here ... */
    rc1 = select(maxfd, &read_set, ...);
    /* but now read_set has only one bit set here */
    rc2 = select(maxfd, &read_set, ...);
    

    第二个select 调用将检查多少个文件描述符?只有一个“准备读取”,第一次调用清除了另一个描述符的位。

    请注意,您的循环永远不会重新初始化 read_set。因此,即使您没有连续两次直接调用select,您实际上是在调用select,并在第二次及以后的循环中清除了一些位。

    (使用select 时的一个典型习惯用法是拥有用于输入的“主集”,并复制它们以在调用中进行修改:

    copy_reads = master_reads;
    copy_writes = master_writes;
    rc = select(maxfd, &copy_reads, &copy_writes, ...);
    

    然后根据需要在 &copy_reads 和 &copy_writes 上使用FD_ISSET。或者,您可以使用FD_ZERO 并在每次循环中重建整个集合。完全绕过这个问题的另一种方法是使用poll,甚至是epoll或kqueues或类似的。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-24
      • 1970-01-01
      • 1970-01-01
      • 2010-10-31
      • 2013-10-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多