2010-09-07 22 views
16

Zastanawiam się, czy istnieje łatwy sposób iteracji przez fd_set? Powodem, dla którego chcę to zrobić, jest brak konieczności przechodzenia przez wszystkie podłączone gniazda, ponieważ select() zmienia te ustawienia fd_, tak aby zawierały tylko te, które mnie interesują. Wiem też, że używanie implementacji typu, który nie jest przeznaczony do bezpośredniego dostępu, jest generalnie złym pomysłem, ponieważ może się różnić w różnych systemach. Jednak potrzebuję jakiegoś sposobu, aby to zrobić i brakuje mi pomysłów. Moje pytanie brzmi:Jak iterować przez fd_set

Jak wykonać iterację za pomocą fd_set? Jeśli jest to naprawdę zła praktyka, czy istnieją inne sposoby rozwiązania mojego "problemu", z wyjątkiem przeplatania wszystkich podłączonych gniazd?

Dzięki

+0

Aby podkreślić, co mam na myśli. Nie chcę używać podejścia FD_ISSET, ponieważ wymaga on, aby przechodzić przez wszystkie podłączone gniazda. Ale ponieważ, z definicji, select() usuwa nieistotne deskryptory plików z zestawu, chcę przechodzić przez zestaw. – Andreas

+6

To nie musi oznaczać "wszystkie połączone". Możesz przekazać podzbiór podłączonych gniazd, aby wybrać, a następnie użyć FD_ISSET tylko na tym podzbiorze po wybraniu opcji return. Czy istnieje również problem z zapętlaniem się nad nimi wszystkimi? Jeśli nie masz do czynienia z wieloma tysiącami podłączonych gniazd, pętla prawdopodobnie zajmie nieistotny czas. – Rakis

+1

Zgadzam się z Rakis. Jest to jedna z tych rzeczy, która wydaje się być nieskuteczna, ale w większości przypadków tak naprawdę nie jest. Czas przejścia przez pętlę będzie mniejszy niż czas potrzebny do obsługi tylko jednego zestawu FD. – Duck

Odpowiedz

5

Wybierz ustawia bit odpowiadający deskryptorowi pliku w zestawie, więc nie musisz iterować przez wszystkie fds, jeśli interesujesz się tylko kilkoma (i możesz ignorować inne), przetestuj tylko te deskryptory plików, które Cię interesują.

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

if(FD_ISSET(fd0, &read_fds)) 
{ 
    //do things 
} 

if(FD_ISSET(fd1, &read_fds)) 
{ 
    //do more things 
} 

EDIT
Oto struct fd_set:

typedef struct fd_set { 
     u_int fd_count;    /* how many are SET? */ 
     SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */ 
} fd_set; 

Gdzie, fd_count jest liczba gniazd set (tak, można dodać optymalizacji przy użyciu tego) i fd_array jest bit-wektor (o rozmiarze FD_SETSIZE * sizeof (int) , który jest zależny od maszyny). W moim komputerze jest to 64 * 64 = 4096.

Twoje pytanie brzmi: jaki jest najskuteczniejszy sposób na znalezienie pozycji bitów 1 w bit-wektorze (o wielkości około 4096 bitów)?

Chcę wyczyścić jedną rzecz tutaj:
"Pętla przez wszystkie podłączone gniazda" nie oznacza, że ​​faktycznie czytasz/robisz rzeczy do połączenia. FD_ISSET() sprawdza tylko pogodę, czy bit w fd_set ustawiony na przypisanym numerze pliku file_descriptor jest ustawiony czy nie. Jeśli efektywność jest Twoim celem, to czy nie jest to najbardziej efektywne? używając heurystyki?

Poinformuj nas, co jest nie tak z tą metodą i co próbujesz osiągnąć za pomocą alternatywnej metody.

+0

Dziękuję również. Ale proszę, zobacz mój komentarz, być może nie wyjaśniłem wystarczająco wyraźnie, że jest to podejście, którego nie chcę przyjąć. – Andreas

+3

Jeśli to nie jest odpowiedź, która [jest poprawna/chcesz], dlaczego została oznaczona jako odpowiedź? –

+0

Z dwóch powodów. a) edycja dostarczyła informacje, których szukałem b) Zmieniłem zdanie i dlatego odpowiedź stała się istotna. – Andreas

1

Zobacz tę sekcję 7.2 Beej 's Przewodnik do sieci -' 7.2. select() - Synchroniczne wywoływanie we/wy "za pomocą FD_ISSET.

w skrócie, trzeba wykonać iterację fd_set w celu ustalenia, czy deskryptor pliku jest gotowy do odczytu/zapisu ...

+0

Dzięki za odpowiedź. Wiem, że to standardowe podejście, ale chciałbym z niego skorzystać, zobacz mój komentarz do mojego posta. – Andreas

4

dość prosta go:

for(int fd = 0; fd < max_fd; fd++) 
    if (FD_ISSET(fd, &my_fd_set)) 
     do_socket_operation(fd); 
+0

Dzięki za odpowiedź. Proszę zobaczyć mój komentarz dla wyjaśnienia tego, co chcę zrobić. – Andreas

0

Nie sądzę, że to, co próbujesz zrobić, to dobry pomysł.

Po pierwsze zależy od systemu, ale wierzę, że już go znasz.

Po drugie, na poziomie wewnętrznym zestawy te są przechowywane jako tablica liczb całkowitych, a fds są przechowywane jako set bit. Teraz, zgodnie ze stronami man wyboru, FD_SETSIZE to 1024. Nawet jeśli chciałbyś powtórzyć i zdobyć interesujące FDA, musisz zapętlić tę liczbę wraz z manipulacją bałaganem. Więc jeśli nie czekasz na więcej niż FD_SETSIZE fd na wybierz, które nie wydaje mi się to możliwe, nie jest to dobry pomysł.

Oh wait !!. W każdym razie nie jest to dobry pomysł.

10

Musisz wypełnić strukturę fd_set przed wywołaniem select(), nie możesz bezpośrednio przekazać swojego oryginalnego std :: zestawu gniazd. select(), a następnie odpowiednio modyfikuje fd_set, usuwając wszelkie gniazda, które nie są "ustawione", i zwraca liczbę gniazd. Musisz przejść przez wynikowy zestaw fd_set, a nie twój std :: set. Nie ma potrzeby, aby zadzwonić FD_ISSET(), ponieważ powstały fd_set zawiera tylko „set” gniazd, które są gotowe, np:

fd_set read_fds; 
FD_ZERO(&read_fds); 

int max_fd = 0; 

read_fds.fd_count = connected_sockets.size(); 
for(int i = 0; i < read_fds.fd_count; ++i) 
{ 
    read_fds.fd_array[i] = connected_sockets[i]; 
    if (read_fds.fd_array[i] > max_fd) 
     max_fd = read_fds.fd_array[i]; 
} 

if (select(max_fd+1, &read_fds, NULL, NULL, NULL) > 0) 
{ 
    for(int i = 0; i < read_fds.fd_count; ++i) 
     do_socket_operation(read_fds.fd_array[i]); 
} 

Gdzie FD_ISSET() wchodzi w grę częściej jest przy użyciu sprawdzanie błędów z select() , np .:

fd_set read_fds; 
FD_ZERO(&read_fds); 

fd_set error_fds; 
FD_ZERO(&error_fds); 

int max_fd = 0; 

read_fds.fd_count = connected_sockets.size(); 
for(int i = 0; i < read_fds.fd_count; ++i) 
{ 
    read_fds.fd_array[i] = connected_sockets[i]; 
    if (read_fds.fd_array[i] > max_fd) 
     max_fd = read_fds.fd_array[i]; 
} 

error_fds.fd_count = read_fds.fd_count; 
for(int i = 0; i < read_fds.fd_count; ++i) 
{ 
    error_fds.fd_array[i] = read_fds.fd_array[i]; 
} 

if (select(max_fd+1, &read_fds, NULL, &error_fds, NULL) > 0) 
{ 
    for(int i = 0; i < read_fds.fd_count; ++i) 
    { 
     if(!FD_ISSET(read_fds.fd_array[i], &error_fds)) 
      do_socket_operation(read_fds.fd_array[i]); 
    } 

    for(int i = 0; i < error_fds.fd_count; ++i) 
    { 
     do_socket_error(error_fds.fd_array[i]); 
    } 
} 
+0

+1, chociaż znajduję w swoim kodzie kilka "aray" :) – Default

+0

Naprawiłem literówki –

+0

[wybierz stronę man] (http://linux.die.net/man/2/select) mówi: 'nfds jest deskryptorem pliku o najwyższym numerze w dowolnym z trzech zestawów, plus 1. "Użyj * najwyższego numeru *, a nie * liczby *! – MaPePeR

3

Ta pętla jest ograniczeniem interfejsu select(). Podstawowe implementacje fd_set są zwykle ustawione nieco, co oczywiście oznacza, że ​​poszukiwanie gniazda wymaga skanowania przez bity.

Właśnie z tego powodu stworzono kilka alternatywnych interfejsów - niestety wszystkie one dotyczą tylko OS. Na przykład Linux udostępnia epoll, który zwraca listę tylko aktywnych deskryptorów plików. FreeBSD i Mac OS X oba zapewniają kqueue, co daje taki sam rezultat.

+0

To powinna być zaakceptowana odpowiedź. – Agis

0

Nie sądzę, by można było efektywnie korzystać z połączenia select(). Informacje w "The C10K problem" są nadal aktualne.

będzie trzeba trochę platformy konkretne rozwiązania:

Albo można użyć biblioteki zdarzeń, aby ukryć szczegóły platformy dla ciebie libev

Powiązane problemy