2013-07-08 2 views
0

У меня есть несколько вопросов о функции выбора, я писал этот код:Select - несколько вопросов

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; 
       } 
      } 
     } 
    } 
} 

я могу дать первый ARG, чтобы выбрать функцию, и я могу не делать этого, но почему? Почему a должен дать первый аргумент для выбора? m_fdmax - это самое большое количество сокетов, но этот код работает без этого аргумента.

Следующий вопрос: зачем нужен тайм-аут? Когда я не даю этому аргументу, вы выбираете все сокеты как сокеты, которые могут быть прочитаны, но выберите это, когда сокет не имеет данных для чтения. Когда я даю этот аргумент, у меня нет этой проблемы. Но почему ?

Если 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 timeval, то возвращается select, даже если нет события (в вашем коде select будет возвращаться каждую секунду).

О fdmax: Да, вы должны найти наивысший сокет, а ваш фрагмент верен.

Прочее: У вас нет FD_SET в вашем коде. Обычно сокеты устанавливаются (через FD_SET так) в цикле, которые находят самый высокий сокет. EDIT: Мой плохой я не видел fd_set temp_list = m_RecvList; в вашем коде. Нам понадобится больше кода для анализа вашей проблемы с помощью select.

+0

Код OP работает в Windows, о чем свидетельствует тот факт, что Winsock спрашивается. Windows не использует первый параметр 'select()', поэтому он обычно устанавливается в 0. –

1

Я могу дать первый аргумент для выбора функции, и я не могу этого сделать, но почему? Почему a должен дать первый аргумент для выбора? m_fdmax - это самое большое количество сокетов, но этот код работает без этого аргумента.

Read the documentation. Функция select() в Windows игнорирует первый параметр, поэтому не имеет значения, что вы передаете ему.

Следующий вопрос: зачем нужен тайм-аут?

Это не НЕОБХОДИМОСТЬ тайм-аут, но вы можете OPTIONALLY предоставить тайм-аут, если это необходимо. Таким образом, если запрошенное состояние сокета не достигнуто до истечения таймаута, select() все равно может выйти, а не отключать вызывающий поток на неопределенный срок, что позволяет ему делать другие вещи.

Когда я не даю этому аргументу, вы можете отметить весь сокет как сокет, который может быть доступен для чтения, но выберите это, если у сокета нет данных для чтения.

Если вы не указали таймаут, select() ждет неопределенное время, пока на самом деле не возникнет требуемое состояние сокета.Сокет может быть помечен как читаемый, если у него есть данные для чтения, но он также может быть помечен как читаемый, если он был отключен другой стороной от gracefully. Последующий вызов 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 это должно выглядеть так:

Смежные вопросы