2013-07-08 20 views
0

我有選擇功能的一些問題,我寫了這個代碼:選擇 - 幾個問題

void TCPSerwer::sel() 
{ 
    struct timeval tv = {1, 0}; 
    fd_set temp_list = m_RecvList; 
    //if(select(m_fdmax + 1, &temp_list, NULL, NULL, &tv) == SOCKET_ERROR) 
    if(select(0, &temp_list, NULL, NULL, &tv) == SOCKET_ERROR) 
    { 
     perror("select"); 
     exit(1); 
    } 

    for(int i = 0; i <= m_fdmax; i++) 
    { 
     if(FD_ISSET(i, &temp_list)) 
     { 
      // New connection 
      if(i == m_ListenSocket) 
      { 
       acceptClient(); 
      } 

      // Data from client 
      else 
      { 
       PacketHeader header; 
       int nbytes = recv(i, (char*)(&header), sizeof(PacketHeader), 

       // Error 
       if(nbytes < 0) 
       { 
        disconnectClient(i); 
       } 
       // success 
       else 
       { 
        std::cout << "type: " << header.type << " len: " << header.length << std::endl; 
       } 
      } 
     } 
    } 
} 

我可以給第一個參數來選擇功能,我可以不這樣做,但爲什麼呢?爲什麼a應該給第一個arg選擇? m_fdmax是套接字的最大數量,但此代碼在沒有此參數的情況下工作。

下一個問題是,爲什麼選擇需要超時?當我不給這個參數時,選擇標記所有套接字爲可讀的套接字,但在套接字沒有任何數據讀取時選擇這樣做。當我給這個arg我沒有這個問題。但爲什麼 ?

如果m_fdmax是插座的數量最多,我得找個插座的下一個最大的數,當我關閉連接,對不對?我應該這樣說:

int size = m_ClientVector.size(); 
for(int i = 0; i < size; i++) 
{ 
    if(m_ClientVector[i] > m_fdmax) 
      m_fdmax = m_ClientVector[i]; 
} 
+1

Windows'select()'的執行忽略了第一個參數。這在['select()'文檔](http://msdn.microsoft.com/en-us/library/windows/desktop/ms740141.aspx)中有很多說明。 –

回答

0

關於超時: select可以使用struct timeval超時。如果你傳遞一個NULL指針,select會等到事件來。如果你傳遞一個地址爲struct timevalselect會如果有不活動(在你的代碼,select將返回每秒)返回偶數。

關於fdmax:是的,你必須找到最高的插座,你的代碼段是正確的。

其他:您的代碼中沒有任何FD_SET。通常,插座設置(通過FD_SET所以)在找到最高插座的循環中。 編輯:我的壞我沒有看到在代碼中fd_set temp_list = m_RecvList;。我們需要更多代碼才能用select來分析您的問題。

+0

OP的代碼在Windows上運行,Winsock被詢問的事實表明了這一點。 Windows不使用'select()'的第一個參數,因此它通常設置爲0。 –

1

我可以給第一個參數選擇功能,我可以不這樣做,但爲什麼?爲什麼a應該給第一個arg選擇? m_fdmax是套接字的最大數量,但此代碼在沒有此參數的情況下工作。

Read the documentation。 Windows上的select()函數忽略第一個參數,所以傳遞給它的並不重要。

接下來的問題是,爲什麼選擇需要超時?

它不NEED超時,但如果需要,你可以OPTIONALLY提供超時。這樣,如果在超時過去之前未達到所請求的套接字狀態,則select()仍然可以退出並且不會無限期地死鎖調用線程,從而允許它執行其他操作。

當我不給這個ARG選擇標記所有插座作爲插座,可以是可讀的,但選擇這樣做,當插座沒有任何數據讀取。

如果您不提供超時,select()將無限期地等待,直到請求的套接字狀態實際發生。如果套接字有數據要讀取,套接字可以被標記爲可讀,但是如果套接字已被另一方斷開,它也可以被標記爲可讀。隨後調用recv()將告訴您是哪種情況(recv()錯誤返回-1,斷開連接0,數據> 0)。再次,read the documentation

如果m_fdmax是套接字的最大數量,當我關閉連接時,我必須找到下一個最高數量的套接字,對吧?

如果要計算最高插槽號(Windows不計較,但其他的平臺做的),那麼你就必須每次調用select()時間重新計算最高插座數量,或者至少無論何時重新準備fd_set結構(無論如何您每次撥打select()時都需要執行此操作)。

,我應該這樣做是

在Windows上,沒有。在其他平臺上,是的。

隨着中說,嘗試在Windows此代碼來代替:

void TCPSerwer::sel() 
{ 
    struct timeval tv = {1, 0}; 
    fd_set temp_list = m_RecvList; 

    int ret = select(0, &temp_list, NULL, NULL, &tv); 
    if (ret == SOCKET_ERROR) 
    { 
     perror("select"); 
     exit(1); 
    } 

    if (ret == 0) // timeout 
     return; 

    for(u_int i = 0; i < temp_list.fd_count; ++i) 
    { 
     SOCKET s = temp_list.fd_array[i]; 

     // New connection 
     if (s == m_ListenSocket) 
     { 
      acceptClient(); 
      continue; 
     } 

     // Data from client 

     PacketHeader header; 

     char *pheader = (char*) &header; 
     int nbytes = 0; 

     do 
     { 
      ret = recv(s, pheader, sizeof(PacketHeader)-nbytes, 0); 

      // success 
      if (ret > 0) 
       nbytes += ret; 
     } 
     while ((ret > 0) && (nbytes < sizeof(PacktHeader))); 

     // Error or disconnect 
     if (nbytes < sizeof(PacktHeader)) 
     { 
      disconnectClient(i); 
      continue; 
     } 

     // success 
     std::cout << "type: " << header.type << " len: " << header.length << std::endl; 
    } 
} 
0

感謝您的幫助,我想使用Windows和Linux上的代碼,現在我這樣做: 當我有一個新的連接:

bool TCPSerwer::acceptClient() 
{ 
SOCKET new_client = accept(m_ListenSocket, 0, 0); 

if(new_client == INVALID_SOCKET) 
     return false; 

m_ClientVector.push_back(new_client); 

    // Add to FD 
    FD_SET(new_client, &m_RecvList); 

if(new_client > m_fdmax) 
    m_fdmax = new_client; 

return true; 
} 

,當我想關閉連接:

void TCPSerwer::disconnectClient(const SOCKET& client) 
{ 
int size = m_ClientVector.size(); 
for(int i = 0; i < size; i++) 
{ 
    if(m_ClientVector[i] == client) 
    { 
     closesocket(m_ClientVector[i]); 

     // Delete from FD 
     FD_CLR(m_ClientVector[i], &m_RecvList); 

     m_ClientVector.erase(m_ClientVector.begin() + i); 

     break; 
    } 
} 

// re-calculateing the highest socket number 
size = m_ClientVector.size(); 
for(int i = 0; i < size; i++) 
{ 
    if(m_ClientVector[i] > m_fdmax) 
     m_fdmax = m_ClientVector[i]; 
} 

}

我havr一個問題,你雷米勒博,你recv函數看起來是:

recv(s, pheader, sizeof(PacketHeader)-nbytes, 0); 

但recv的在bufor的福祉保存數據? Meybe這應該看起來如下:

recv(s, pheader + nbytes, sizeof(PacketHeader)-nbytes, 0);