Autor Wątek: Użycie funkcji select  (Przeczytany 1960 razy)

Offline Yabol

  • Użytkownik

# Lipiec 08, 2013, 00:06:50
Witam!

Mam mały problem z funkcją select, nie rozumiem dlaczego w liście temp_list zostają deskryptory w który aktualnie nie ma danych do odczytania, i efekt tego jest taki że program zatrzymuje się na funkcji recv
void TCPSerwer::sel()
{
fd_set temp_list = m_RecvList;
if(select(m_fdmax + 1, &temp_list, NULL, NULL, NULL) == - 1 )
{
perror( "select" );
exit( 1 );
}

// Przejdź przez obecne połączenia szukając danych do odczytania
for(int i = 0; i <= m_fdmax; i++ )
{
if(FD_ISSET(i, &temp_list))
{
// Nowe połączenie
if(i == m_ListenSocket)
std::cout << "swiezak:" << std::endl;
acceptClient();
}

// Dane od klientów
else
{
char buf[255];

// Odbieranie danych od klienta
int nbytes = recv(i, buf, sizeof(buf), 0);

// Jeżeli odebranie danych nie powiedzie się z powodu jakiegoś błędu to należy zamknąć połączenie
if(nbytes != 1)
{
//close(i); // Zamknięcie połączenia
FD_CLR(i, &m_RecvList);
}
// Jeżeli odebranie danych powiodło się bez żadnego problemu
else
{
// Reakcja na odebrane dane

}
}
}
}
}

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Xion

  • Redaktor
    • xion.log

# Lipiec 08, 2013, 00:16:05
Taka sytuacja (tzn. select() oznacza socket jako gotowy do odczytu ale recv()/read() czyta zero bajtów) oznacza rozłączenie drugiej strony i powinieneś ją odpowiednio obsługiwać.

Offline Yabol

  • Użytkownik

# Lipiec 08, 2013, 00:21:27
Ale recv nie zwraca 0 tylko cały czas czeka na bajty, a tak być nie powinno bo o ile się nie mylę to select ma oznaczyć gniazda które zawierają dane do odczytania

# Lipiec 08, 2013, 15:48:52
Ja ignoruję błędy
if(nbytes > 0)
ObslugaPakietu(buf);

Offline Yabol

  • Użytkownik

# Lipiec 08, 2013, 18:49:04
Problem rozwiązałem, pomogło ustawienie timeoutu dla funkcji select a nie wiem dlaczego bez tego funkcja ustawia deskryptory w których nie ma nic do odczytania jako gotowe do odczytania, a z timeoutem jest już ok, drugie pytanie po co w select jest pierwszy argument ? Bez tego też funkcja działa, ale po co podaje się numer największego deskryptora ?
Napisałem funkcję do odczytywania wszystkich danych z gniazda, wygląda ona tak:
Kod: (cpp) [Zaznacz]
int recvData(const SOCKET& socket, char* data_bufor, int data_size)
{

//
// Funkcja obsluguje niepełne odebranie danych, klient może wysłać np 15 bajtów danych a w pierwszych wywołaniu recv zostanie
// odebrane tylko 7 bajtów, w takim przypadku funkcja recv zostaje wywoływana aż do odebrania wszystkich bajtów.
//
// Wywołanie recv wygląda tak:
// recv(socket, data_bufor + offset, data_size - offset, 0);
//
// Gdzie:
// data_bufor jest wskaźnikiem na bufor do zapisu odczytanych danych
// data_size przechowuje ilość danych do odebrania
// offset jest przesunięciem, funkcja recv zapisuje odebrane dane na początku bufora i zwraca ilość odczytanych danych,
// przesunięcie jest sumą wszystkich odebranych danych i jest dodawane do wskaźnika żeby dane były dopisywane do ostatnio odebranych danych.
// Przesunięcie jest odejmowane od rozmiaru danych do odebrania ponieważ za każdym razem kiedy recv odczyta dane to zostaje ich już mniej do odebrania.
//

// Inforamcja o przesunięciu
int offset = 0;

// Ilość aktualnie odebranych danych
int recv_data_length;

//
// Odebranie danych od klienta
//
while( (recv_data_length = recv(socket, data_bufor + offset, data_size - offset, 0)) > 0 )
{
// Zwiększenie przesunięcia o wartośc odebranych danych
offset += recv_data_length;
// std::cout << offset << std::endl;

// Jeżeli zostały odebrane wszystkie dane
if(offset == data_size)
{
// Wyjście z pętli
break;
}
}

//
// Jeżeli wartość zwrócona przez funkcję recv wyniosi 0 lub jest mniejsza od zera
// to oznacza że wystąpił błąd lub połączenie zsotało zerwane
//
if(recv_data_length <= 0)
{
// Jeżeli wartość wynosi 0 to połączenie zostało zerwane
if(recv_data_length == 0)
{
// Połączenie zostało zerwane
std::cout << "selectserver: socket  hung up : " << socket << std::endl;
}
else
{
// Wystąpił inny błąd
perror( "recv" );
}

// error
return recv_data_length;
}

return 1;
}

Nie wiem dlaczego korzystając z tej funkcji otrzymuję -1 a nie zero przy rozłączeniu się klienta, wszystko wygląda normalnie. Nie wiem czy w pętli while może być warunek taki jaki jest, i czy nie da się jeszcze bardziej tego uprościć ?