Hlavní navigace

Sokety a C/C++: Funkce select

Radim Dostál 2. 6. 2003

V minulém dílu jsme si ukázali, jak seskupit sokety do množiny. Nyní si předvedeme, k čemu je množina soketů dobrá.

Pro práci s více sokety najednou slouží funkce select. Funkce select je schopna pracovat se třemi skupinami soketů. V první skupině hlídá, zda v některém soketu nejsou příchozí data, která je možno číst. Ve druhé skupině hlídá, zda do některého soketu je možné data zapisovat. Sokety ve třetí skupině jsou kontrolovány, zda se nenacházejí v chybovém stavu.

Funkce select v Linuxu

V množinách, které jsou předávány funkcí select jako parametry, jsou celá čísla, která mohou představovat jakýkoliv identifikátor souboru (file descriptor). Protože tento seriál je zaměřen na sokety, budeme dále hovořit pouze o soketech. Funkce select je ale obecnější. Například v manuálových stránkách select je uveden příklad použití funkce select se standardním vstupem.

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

 – druhý parametr je ukazatel na množinu obsahující sokety (obecně jakékoliv file descriptory), z nichž chceme číst. Třetím parametrem je ukazatel na množinu soketů (obecně jakýchkoliv file descriptorů), do kterých chceme zapisovat. Čtvrtým parametrem je ukazatel na množinu soketů (obecně jakýchkoliv file descriptorů), u kterých chceme zjistit, zda nastala nějaká chyba. Ve třech množinách jsou vlastně celá čísla. Vezmeme nejvyšší z těchto čísel, zvýšíme ho o 1 a máme číslo, které předáme jako první parametr. Posledním parametrem je ukazatel na strukturu udávající, jak dlouho má funkce select tento dohled provádět. Funkce vrací –1 v případě chyby. Jinak vrací počet soketů, na nichž došlo k událostem, které mají být sledovány. V případě, že vyprší časový limit daný posledním parametrem, funkce select vrací 0, protože počet soketů na kterých došlo ke změně, je 0.

Funkce select v MS Windows Ž

V množinách, které jsou předávány funkcí select jako parametry, jsou celá čísla, jež mohou představovat POUZE soket. Zde je velký rozdíl oproti unixovým systémům.

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout); – prvním parametrem je jakékoliv celé číslo. Parametr je zde pouze z důvodu kompatibility s klasickým soketovým rozhraním. V dokumentaci je přímo napsáno, že prvním parametrem může být jakékoliv číslo. A je to pravda. Zkoušel jsem zadat 0 (což by v klasickém soketovém API určitě nešlo) a vše fungovalo tak, jak má. Druhý parametr je ukazatel na množinu obsahující sokety, z nichž chceme číst. Třetím parametrem je ukazatel na množinu soketů, do kterých chceme zapisovat. Čtvrtým parametrem je ukazatel na množinu soketů, u kterých chceme zjistit, zda nastala nějaká chyba. Posledním parametrem je ukazatel na strukturu udávající, jak dlouho má funkce select tento dohled provádět. Funkce vrací hodnotu makra SOCKET_ERROR v případě chyby. Jinak vrací počet soketů, na nichž došlo k událostem, které mají být sledovány. V případě, že vyprší časový limit daný posledním parametrem, funkce select vrací 0, protože počet soketů, na kterých došlo ke změně je 0.

Jak vlastně s funkcí select pracovat?

Nejprve musíme zaplnit tři množiny podle toho, jaké události chceme sledovat. Vlastně nám stačí zaplnit jen jednu. Místo dalších můžeme předat NULL. Poté zavoláme funkci select. Funkce zablokuje chod programu a čeká, dokud nenastane nějaká sledovaná událost na soketech. Je-li posledním parametrem NULL, funkce čeká, dokud doopravdy k události nedojde. Je-li jako poslední parametr předán ukazatel na strukturu s časovým údajem, funkce čeká, dokud nenastane nějaká požadovaná událost na soketech nebo dokud nevyprší časový limit. Po skončení volání funkce select zjistíme z návratové hodnoty, zda došlo k chybě nebo na kolika soketech se událo něco rozhodujícího. Každá ze tří množin obsahuje po skončení volání funkce select jen ty sokety, na kterých nastala požadovaná událost. Nikdy nesmíme zapomenout, že nemuselo dojít pouze ke změně na jednom soketu.

My budeme používat asi nejvíce první množinu (druhý parametr funkce select). Funkce nám pohlídá, zda na soketech v této množině jsou k dispozici nějaká data, zda na soket přišel požadavek na spojení nebo zda druhá strana spojení přerušila. Zjistíme vlastně, na které sokety můžeme v blokovacím režimu zavolat recv nebo accept a přitom mít jistotu, že tyto funkce nebudou na nic čekat.

Ukázkový kousek zdrojového textu

// Struktura s časovým limitem
timeval tv;
// Množina
fd_set myset;
// Množina obsahuje náhodná data. Odstraním je.
FD_ZERO(&myset);
// Zaplnění množiny sokety
FD_SET(socket, &myset);
FD_SET(jinySocket, &myset);
// Vyplním časový údaj (například na 3 minuty)
tv.tv_sec = 180;// Počet sekund
tv.tv_usec = 0;// Počet mikrosekund
// Zavolám select (V Linuxu musím mít nastavenou proměnnou max.)
int ret = select(max, &myset, NULL, NULL, &tv);
if (ret == -1)
{
  // Nastala chyba
}
if (ret == 0)
{
  // Vypršel časový limit
}
if (FD_ISSET(soket, &myset))
{
  /* Obsluha soketu. Zde je možné vytvořit nové vlákno nebo proces,
který obslouží událost na soketu. Server pak je vícevláknový.
*/
}
if (FD_ISSET(jinySoket, &myset))
{
  /* Obsluha soketu. Zde je možné vytvořit nové vlákno nebo proces,
který obslouží událost na soketu. Server pak je vícevláknový.
*/
}

Příklad serveru

Jako ilustrační příklad si předvedeme jednoduchý server, který bude čekat na TCP portu číslo 2902 na spojení. V případě navázání spojení bude s klientem komunikovat tak dlouho, dokud klient neuzavře spojení. Nebude-li se serverem tři minuty nikdo komunikovat, uzavře všechna spojení a ukončí se. Server očekává, že mu jednotliví klienti posílají text. Text, který server obdrží od jednoho klienta, bude rozeslán všem klientům. Jedná se vlastně o velice jednoduchý chat server. K serveru se lze připojit například telnetem nebo si můžete sami naprogramovat jednoduchého klienta na základě článku TCP klient v Linuxu.

V programu používám šablonu vector a standardní algoritmus for_each. Server je jednovláknový. Mohli bychom například pomocí fork vytvořit nový proces pokaždé, kdy by server obdržel data. S paralelismem ale přicházejí problémy se synchronizací. Tématem seriálu jsou sokety, proto se synchronizací zabývat nebudeme.

Tabulka č. 437
Soubor OS
lin13.tgz Linux
win13.zip MS Windows Ž

Příště se podíváme na práci se sokety v neblokovacím režimu.

Našli jste v článku chybu?

7. 2. 2006 13:07

ZeXx86 (neregistrovaný)
Napsal jsem hubsoft(server) pro DC++ clienty pomocí tohoto článku .. Funguje skvěle až na jednu věc .. Připojí se asi 60 lidí a pak hub zatuhne .. Po chvíli se ještě odsekne a pokračuje a pak zase zatuhne ale uz natvrdo .. Mám podezření že to je někde chyba v tomto čláku .. Děla to jak v linuxu tak ve windowsu .. Ve windowsu to tuhne a v linuxu program spadne celý .. Hubsoft jsem přepsal několikrát, ale dělá pokaždé to stejné .. Pokud chcete pošlu Vám zdrojový kód .. ICQ 302285423 / email: admin…

27. 8. 2005 18:13

uživatel si přál zůstat v anonymitě
Dobrý den .. chtěl bych Vás poprosit o radu . Snažím se o hubsoft (server pro clienty DC++). Všechno šlape jak má až na jedinou věc ... Když se nekdo připojí tak to laicky řečeno "po clientovi hned něco chce" čili clientova povinnost je poslat na server nejake data (text) a až pak mu na oplatku neco pošle server... Potřebuji aby každému clientovi, který se připojí zaslal server data a až pak neco prijímal (Takže to potrebuji obraceně) ... Jak sem s kódu pochopil mnelo by to fungovat ta…
Vitalia.cz: To není kašel! Správná diagnóza zachrání život

To není kašel! Správná diagnóza zachrání život

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

DigiZone.cz: Ohrozí Freedom TV přechodové sítě?

Ohrozí Freedom TV přechodové sítě?

Měšec.cz: Za palivo zaplatíte mobilem (TEST)

Za palivo zaplatíte mobilem (TEST)

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

120na80.cz: Rakovina oka. Jak ji poznáte?

Rakovina oka. Jak ji poznáte?

Podnikatel.cz: Babiše přesvědčila 89letá podnikatelka?!

Babiše přesvědčila 89letá podnikatelka?!

120na80.cz: Jak oddálit Alzheimera?

Jak oddálit Alzheimera?

DigiZone.cz: Recenze Westworld: zavraždit a...

Recenze Westworld: zavraždit a...

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Lupa.cz: Teletext je „internetem hipsterů“

Teletext je „internetem hipsterů“

DigiZone.cz: Flix TV má set-top box s HEVC

Flix TV má set-top box s HEVC

DigiZone.cz: Sat novinky: slovenská TV8 HD i ruský NTV Mir

Sat novinky: slovenská TV8 HD i ruský NTV Mir

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Vitalia.cz: Znáte „černý detox“? Ani to nezkoušejte

Znáte „černý detox“? Ani to nezkoušejte

Měšec.cz: Jak levně odeslat balík přímo z domu?

Jak levně odeslat balík přímo z domu?

Lupa.cz: Seznam mění vedení. Pavel Zima v čele končí

Seznam mění vedení. Pavel Zima v čele končí

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

Vitalia.cz: Chtějí si léčit kvasinky. Lék je jen v Německu

Chtějí si léčit kvasinky. Lék je jen v Německu

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?