2011-12-14 26 views
1

我已經編寫了一個程序來接受Linux 2.6.14機器的TCP套接字連接(下面的服務器和客戶機代碼)。我的設計是爲了忽略一些(套接字)輸入並回應其他人。問題是,對於我編寫的代碼,當我忽略(不響應)時,我的select(2)函數看不到更多數據。爲什麼我的套接字接口在select()調用中停止註冊?

所以在下面的例子中,只要我發送一個t命令,服務器似乎停止聽取端口實例的操作。直到發送t命令爲止,服務器將以其Ok.響應。服務器代碼不會被凍結,因爲它將接受新的端口實例,並且fork(2),但它未對t(測試)命令做出響應的端口仍然看起來凍結。爲什麼是這樣?

這裏是我有用捕獲輸出的嘗試......

~/test$ ./client-test 192.168.20.171 48884 

Ok. 
Ok. 
Ok.t 






^Z 
[1]+ Stopped     ./client-test 192.168.20.171 48884 
~/test$ ./client-test 192.168.20.171 48884 

Ok. 
Ok. 

這裏是(去除錯誤代碼和#include S)爲我的服務器代碼:

#define PORT_NUM 48884 
#define CLIENT_CMD_LENGTH 256 
static void dostuff(int fd) 
{ 
    char buf[CLIENT_CMD_LENGTH]; 
    char bye[] = "Bye!", ok[] = "Ok."; 
    const struct timeval wait_tv = { .tv_usec = 1000000/20, .tv_sec = 0 }; 
    fd_set read_fd; 

    do 
    { 
     struct timeval tv; 
     int n; 

     tv = wait_tv; 
     FD_ZERO(& read_fd); 

     FD_SET(fd, &read_fd); 
     if (select(fd+1, &read_fd, NULL, NULL, &tv)==-1) 
     { 
      /* error */ 
     } 
     else if (FD_ISSET(fd, & read_fd)) 
     { 
      printf(".\n"); 
      FD_CLR(fd, & read_fd); 
      n = read(fd,buf, (sizeof buf) - 1); 

      switch (* buf) 
      { 
       case 'q': write(fd,bye,sizeof bye); 
          return; 
       case 't': break; /* Ignore */ 
       default: write(fd, ok, sizeof ok); 
          break; 
      } 
     } 
    } while (1); 
    return; 
} 

static void server_work(void) 
{ 
    int sockfd; 
    socklen_t clilen; 
    struct sockaddr_in serv_addr, cli_addr; 

    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd < 0) return; 
    memset((char *) &serv_addr, '\0', sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    serv_addr.sin_port = htons(PORT_NUM); 
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 
      return; 
    } 
    listen(sockfd,5); 
    clilen = sizeof(cli_addr); 
    while (1) { 
     int newsockfd, pid; 
     newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); 
     if (newsockfd < 0) 
      continue; 
     pid = fork(); 
     if (pid < 0) 
      break; 
     if (pid == 0) { 
      close(sockfd); 
      dostuff(newsockfd); 
      exit(0); 
     } 
     else close(newsockfd); 
    } /* end of while */ 
    close(sockfd); 
    return; /* we never get here */ 
} 

int main(int argc, char *argv[]) { 
    server_work(); 
    return 0; 
} 

和我的測試客戶端。 ..

void error(const char *msg) 
{ 
    perror(msg); 
    exit(0); 
} 

int main(int argc, char *argv[]) 
{ 
    int sockfd, portno, n; 
    struct sockaddr_in serv_addr; 
    struct hostent *server; 

    char buffer[256]; 
    if (argc < 3) { 
     fprintf(stderr,"usage %s hostname port\n", argv[0]); 
     exit(0); 
    } 
    portno = atoi(argv[2]); 
    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd < 0) 
     error("ERROR opening socket"); 
    server = gethostbyname(argv[1]); 
    if (server == NULL) { 
     fprintf(stderr,"ERROR, no such host\n"); 
     exit(0); 
    } 
    bzero((char *) &serv_addr, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    bcopy((char *)server->h_addr, 
     (char *)&serv_addr.sin_addr.s_addr, 
     server->h_length); 
    serv_addr.sin_port = htons(portno); 
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
     error("ERROR connecting"); 
    do { 
    bzero(buffer,256); 
    fgets(buffer,255,stdin); 
    n = write(sockfd,buffer,strlen(buffer)); 
    if (n < 0) 
     error("ERROR writing to socket"); 
    bzero(buffer,256); 
    n = read(sockfd,buffer,255); 
    if (n < 0) 
     error("ERROR reading from socket"); 
    printf("%s",buffer); 
    } while (*buffer != 'B'); 
    close(sockfd); 
    return 0; 
} 
+0

當你用select()代碼遍歷代碼時,你會發現什麼情況?你的代碼是否掛起,還是繼續?是否接收到t的選擇超時? – 2011-12-14 03:03:23

回答

2

這是因爲在後馬上讀您的客戶端塊做它寫,WH在收到t後,服務器不會發送任何內容,只返回select(2)。經典的僵局。

在發送服務器忽略的命令後,或者總是從服務器發送某種確認,可以跳過客戶端上的讀取操作。

附註 - 您假設接收器獲得發送者發送的相同數據塊。錯誤。 TCP是字節流,並且不保留任何類型的應用程序級消息邊界。你必須自己擔心這些。處理它的兩種常見方式是1)諸如零字節之類的消息分隔符,以及2)以每個消息的大小預先掛起每個消息。

希望這會有所幫助。

+0

我認爲會。我將通過在客戶端添加一個select調用來測試它...至於讀取長度,我使用`ioctl(fd,FIONREAD,(void *)&nbytes)`調用和消息大小來獲得這些讀取長度。謝謝。 – Jamie 2011-12-14 12:49:02

相關問題