2013-01-24 77 views
2

我已經寫了接受新客戶的數據和來自客戶的服務器代碼。但問題在於select並沒有等到超時,儘管沒有來自客戶端的數據。我想等待5秒鐘,併爲可用客戶端發送心跳。但它在第一次迭代中等待5秒鐘,然後在下一次迭代中快速發送心跳。如何解決這個問題呢。提前致謝。選擇在C++中的插座不等待超時值

void * Communicate(void * id) 
{ 
int *iSockID = (int *) id; 
int listener = *iSockID; 

fd_set master; // master file descriptor list 
fd_set read_fds; // temp file descriptor list for select() read 
fd_set write_fds; // temp file descriptor list for select() read 
int fdmax;  // maximum file descriptor number 

int i, j, rv; 

FD_ZERO(&master); // clear the master and temp sets 
FD_ZERO(&read_fds); 
FD_ZERO(&write_fds); 
// add the listener to the master set 
FD_SET(listener, &master); 
printf("Listener is %d \n" , listener); 

// keep track of the biggest file descriptor 
fdmax = listener; // so far, it's this one 
//accept 3 clients 


// main loop 
for(;;) { 
    read_fds = master; // copy it 
    write_fds = master; 
    struct timeval tv; 
    tv.tv_sec = 5; 
    tv.tv_usec = 0; 
    int iResult = select(fdmax+1, &read_fds, &write_fds, NULL, &tv) ; 
    if (iResult == -1) 
    { 
     perror("select"); 
     exit(4); 
    } 

    for(i = 0; i <= fdmax; i++) 
    {   
     //send work for clients 
     SendHeartBeats(write_fds , fdmax , listener , i); 

    } 

    // run through the existing connections looking for data to read 
    // ADD NEW CONNECTIONS READ FROM CONNECTIONS  
    for(i = 0; i <= fdmax; i++) 
    { 


     if (FD_ISSET(i, &read_fds)) 
     { // we got one!! 
      // handle new connections    
      if (i == listener) 
      {          
       AcceptNewClients(master , fdmax , listener); 
      } else 
      { 
       AccepeDataFromClients(i , master); 
      } // END handle data from client 
     } // END got new incoming connection 
    } // END looping through file descriptors 
    sleep(3); 
} // END for(;;) 
return 0; 
} 
+0

我不能沒有看到SendHeartBeats'的'的實施提供了更詳細的解答。 – jxh

回答

0

您在您使用Linux 2.6.27評論說。由於在Linux中,fd_set是一個包含整數類型數組的結構,使用指定將master複製到read_fds是很好的。

您的select呼叫未被阻止的原因是因爲您正在從master初始化read_fdswrite_fds。你閒置的客戶端套接字總是可寫的,所以你立即醒來。順便說一下,在你的listener插座上選擇寫入是毫無意義的。

當您嘗試寫入但應對EAGAIN/EWOULDBLOCK錯誤時,您應該只在write_fds中設置客戶端套接字。

1

您不能通過等於fd_set變量。你需要使用FD_COPY。如果你不這樣做,你只需要將一個句柄複製到已經標記爲已完成的實際數據。

+0

+1。 'fdmax = listener;'只是等着失敗,尤其是考慮到'AcceptNewClients(master,fdmax,listener)'的尷尬需要;''更新'fdmax'。而且,代碼不應該執行'sleep(3)' - 讓超時處理該事件,執行超時後超時檢查,以避免在您關心時過度頻繁地檢測心跳。 –

+0

我已經包含sys/time.h文件。但FD_COPY給我錯誤FD_COPY沒有在這個範圍內聲明。 @Tony我使用了睡眠(),因爲超時無效。你告訴我我在做什麼錯在這裏?? –

+0

@Zeus:如果你不重建你的FD設置並提供正確的最大值,超時將不起作用......一旦有任何事情被設置,我會保持沉默。對於FD_COPY - 您是否檢查了您的手冊頁或其他系統文檔? stys/time.h在某些系統(例如某些IBM)上絕對是正確的 - 但這些東西可能會有所不同,或者根本不可用。您可以重置/清除它並再次設置標誌,或者在某些不需要使用FD_COPY的系統上 - 例如,檢查GNU libc如何在http://www.gnu.org/software/libc/manual /html_node/Server-Example.html –

0

您需要更改代碼,重新初始化read_fdwrite_fd變量每次select()被調用時,它會修改他們退出後,所以你需要每次都重新設置。正如其他人所說,使用=運算符來複制master變量不是複製fd_set結構的正確方法。

試試這個:

void * Communicate(void * id) 
{ 
    int *iSockID = (int *) id; 
    int listener = *iSockID; 

    fd_set master; // master file descriptor list 
    fd_set read_fds; // temp file descriptor list for select() read 
    fd_set write_fds; // temp file descriptor list for select() read 
    struct timeval tv; 
    int fdmax;  // maximum file descriptor number 
    int i, j, rv; 

    printf("Listener is %d \n", listener); 

    // add the listener to the master set 
    FD_ZERO(&master); 
    FD_SET(listener, &master); 

    // keep track of the biggest file descriptor 
    fdmax = listener; // so far, it's this one 
    //accept 3 clients 

    // main loop 
    clock_t c1 = clock(); 
    while (1) 
    { 
     FD_ZERO(&read_fds); 
     FD_ZERO(&write_fds); 

     #ifdef MSWINDOWS 
     // Windows does not have FD_COPY() 
     for (u_int i = 0; i < master.fd_count; ++i) 
     { 
      FD_SET(master.fd_array[i], &read_fd); 
      FD_SET(master.fd_array[i], &write_fd); 
     } 
     #else 
     FD_COPY(&master, &read_fd); 
     FD_COPY(&master, &write_fd); 
     #endif 

     tv.tv_sec = 1; 
     tv.tv_usec = 0; 

     int iResult = select(fdmax+1, &read_fds, &write_fds, NULL, &tv); 
     if (iResult == -1) 
     { 
      perror("select"); 
      exit(4); 
     } 

     clock_t c2 = clock(); 
     if (((c2-c1)/CLOCKS_PER_SEC) >= 5) 
     { 
      c1 = c2; 
      for(i = 0; i <= fdmax; i++) 
      {   
       if ((i != listener) && FD_ISSET(i, &write_fds)) 
       { 
        //send work for client 
        SendHeartBeat(i); 
       } 
      } 
     } 

     // run through the existing connections looking for data to read 
     // ADD NEW CONNECTIONS READ FROM CONNECTIONS  
     for(i = 0; i <= fdmax; i++) 
     { 
      if (FD_ISSET(i, &read_fds)) 
      { 
       // we got one!! 
       if (i == listener) 
       {          
        AcceptNewClient(master, fdmax, listener); 
       } 
       else 
       { 
        AcceptDataFromClient(i); 
       } 
      } 
     } 
    } 

    return 0; 
} 
+0

非常感謝您的回答。問題是存在編譯錯誤。「FD_COPY未在此範圍內聲明」。 –

+0

如果套接字API爲你的編譯器沒有'FD_COPY()'中(而非所有平臺做),那麼你就必須手動循環throuh了'master'的名單,像我展示的Windows。 –