2013-10-15 42 views
0

有人可以告诉我为什么下面的代码不起作用?非阻塞I/O与选择()

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <unistd.h> 
#include <string.h> 
#include <fcntl.h> 
#include <sys/select.h> 
#include <sys/time.h> 
#include <errno.h> 

int main(int argc, char **argv) 
{ 
    int sockfd, i, new = 0, maxfd, nready, on = 1; 
    struct sockaddr_in saddr; 
    ssize_t nbytes; 
    fd_set rfds, master; 
    char buffer[BUFSIZ]; 

    if(-1 == (sockfd = socket(PF_INET, SOCK_STREAM, 0))) 
    { 
     perror("socket()"); 
     exit(EXIT_FAILURE); 
    } 

    if(-1 == (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)))) 
    { 
     perror("setsockopt()"); 
     exit(EXIT_FAILURE); 
    } 

    if(-1 == (fcntl(sockfd, F_SETFD, O_NONBLOCK))) 
    { 
     perror("fcntl()"); 
     close(sockfd); 
     exit(EXIT_FAILURE); 
    } 

    (void)memset(&saddr, '\0', sizeof(struct sockaddr_in)); 

    saddr.sin_port = htons(1234); 
    saddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    saddr.sin_family = AF_INET; 

    if(-1 == (bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)))) 
    { 
     perror("bind()"); 
     close(sockfd); 
     exit(EXIT_FAILURE); 
    } 

    if(-1 == (listen(sockfd, 32))) 
    { 
     perror("listen()"); 
     close(sockfd); 
     exit(EXIT_FAILURE); 
    } 

    FD_ZERO(&rfds); 
    FD_ZERO(&master); 
    maxfd = sockfd; 
    FD_SET(sockfd, &master); 

    printf("maxfd = %d\n", maxfd); 

    for(;;) 
    { 
     (void)printf("select()ing...\n"); 

     (void)memcpy(&rfds, &master, sizeof(master)); 

     if(-1 == (nready = select(maxfd+1, &rfds, NULL, NULL, NULL))) 
     { 
      perror("select()"); 
      close(sockfd); 
      exit(EXIT_FAILURE); 
     } 

     for(i=0; i<FD_SETSIZE; i++) 
     { 
      if(FD_ISSET(i, &rfds)) 
      { 
       if(i == sockfd) 
       { 
        (void)printf("trying to accept new connection(s)\n"); 

        for(;;) 
        { 
         new = accept(sockfd, NULL, NULL); 

         if(new < 0) 
         { 
          if(errno != EWOULDBLOCK) 
          { 
           perror("accept()"); 
           exit(EXIT_FAILURE); 
          } 

          /* no incomming connection */ 
          break; 
         } 

         FD_SET(new, &master); 

         if(maxfd < new) 
          new = maxfd; 

         (void)printf("%d was added to set\n", new); 

        } 

       } 

       else 
       { 
        for(;;) 
        { 
         (void)printf("trying to read data from ready socket(s)\n"); 

         while((nbytes = recv(i, buffer, sizeof(buffer), 0)) > 0) 
          printf("%s", buffer); 

         if(nbytes < 0) 
         { 
          if(nbytes != EWOULDBLOCK) 
          { 
           perror("recv()"); 
           exit(EXIT_FAILURE); 
          } 

          break; 
         } 

         if(0 == nbytes) 
         { 
          close(i); 
          break; 
         } 

         FD_CLR (i, &master); 
        } 

       } 


      } 
     } 


    } 

    return 0; 
} 

它在*:1234上监听新连接。它的构建没有任何错误或警告,但它无法从ready描述符中recv()数据。我找不到问题的根源,有人能帮助我吗?

+0

请不要从零循环到'FD_SETSIZE'。相反,将连接的套接字保存在某个集合中,并仅循环该(有限)集合。 –

+0

为什么在每个recv()调用之后,您认为'buffer'包含C风格的以null结尾的字符串? –

回答

1

问题是读取循环,因为您读取了您收到的字节,但是当您完成该操作时,您尝试再次接收数据,从而导致recv数据块被阻止。您需要使套接字非阻塞。不,阻塞/非阻塞状态是而不是继承自被动侦听套接字。

+0

'不,阻塞/非阻塞状态不是从被动监听套接字继承' - 呃,我刚刚发表评论,我认为它是,然后你添加了你的编辑:) –

+1

你可以使用accept4 Linux上的SOCK_NONBLOCK标志避免两步accept()和fcntl()。 –