Mushtu
Mushtu

Reputation: 488

non-blocking socket and io

my client program first connects to server,then server send a file and then client enters a command and send it to server and receives result.i want to implement this by non-blocking io for input command and non-blocking socket. but my program can not read input from stdin.when i set only stdin in read_set it works but in this case it doesn't work. this is my client code:

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);   
}

Upvotes: 1

Views: 2067

Answers (1)

torek
torek

Reputation: 487993

Remember that select updates the read, write, and exception fd_set items you pass in. This means that if you select on two possible inputs and one of them is ready, the read set has only one bit set in it:

/* 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, ...);

How many file descriptors will the second select call check? With only one "ready to read", the first call cleared the bit for the other descriptor.

Note that your loop never re-initializes read_set. So even though you don't have two select calls directly in a row, you are in effect calling select with some bit(s) cleared on the second and later trips through the loop.

(One typical idiom when using select is to have "master sets" that you use for inputs, and copy them to be modified in the call:

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

and then use FD_ISSET on &copy_reads and &copy_writes as needed. Or, you can use FD_ZERO and rebuild the entire set on each trip through the loop. Another way to bypass this issue entirely is to use poll, or even epoll or kqueues or similar.)

Upvotes: 1

Related Questions