2012-03-17 165 views
1

我的客戶端程序首先連接到服務器,然後服務器發送一個文件,然後客戶端輸入一個命令並將其發送到服務器並接收result.i想要通過非阻塞io實現此輸入命令和無阻塞的插座。 但我的程序無法讀取輸入從stdin.when我只設置標準輸入read_set它的工作原理,但在這種情況下,它不起作用。 這是我的客戶端代碼:非阻塞套接字和io

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

它是如何失敗? – 2012-03-17 09:16:32

回答

1

記住select更新的讀,寫和異常fd_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和&使用FD_ISSET copy_writes根據需要,或者,你可以使用FD_ZERO,並通過循環重建每次跳閘的整個集。繞過這個問題的另一種方法是完全使用poll,甚至epoll或kqueues或類似)。

+0

感謝您的回答 – Mushtu 2012-03-17 10:35:18