V prvních jedenácti dílech seriálu jsme se seznámili se základy práce se sokety. V článcích jsou jednoduše popsány základy pro posílání a příjem dat v operačních systémech Linux a MS WindowsŽ v takzvaném blokovacím režimu. Další díly seriálu „Sokety a C++“ začnou vycházet na ROOTovi.
Protože server root je zaměřen na operační systémy unixového typu, nebudu zde psát kapitoly věnující se operačnímu systému MS WindowsŽ (knihovně WinSock). Pouze se v každém článku okrajově zmíním, jaké jsou rozdíly mezi klasickými sokety a knihovnou WinSock.
Proč seskupovat sokety?
V dosud napsaných článcích jsem popisoval situace, kdy nějaké dva programy spolu komunikují. Programy posílají a přijímají data. Zavoláním funkce recv (nebo obdobné recvfrom) program začne čekat na příchozí data. Až po jejich příchodu se ukončí volání funkcí recv a recvfrom. Tyto funkce program zablokují. Tak, jak jsme se sokety pracovali doposud, je možné pouze v banálních ukázkových příkladech. Obvykle chceme, aby program kromě čekání na data ještě prováděl nějakou činnost. Řešením je použít neblokovací režim nebo více vláken. Ne vždy je ale takové řešení vhodné. Například chceme-li pracovat s více sokety najednou, ještě nemusíme nutně používat neblokovací režim nebo vlákna. Můžeme sokety (v blokovacím režimu) seskupit do takzvané množiny soketů a množinu nechat zpracovávat jedním vláknem. Na to se ale podíváme příště. Dnes si ukážeme, jak množinu soketů vytvořit a jak s ní pracovat.
Množina soketů
Pro práci s množinou budeme používat makra. V Linuxu jsou všechna makra, která zde použiji, definována v hlavičkovém souboru sys/types.h. V MS WindowsŽ stačí použít hlavičkový soubor windows.h. Obecně nelze říci, jak jsou makra definována (implementována). Jednotlivé implementace mohou být systém od systému jiné. Makra a funkce, které je používají, jsou navrženy tak, aby programátor nemusel znát vnitřní implementaci.
Vytvoření množiny
Makro fd_set lze použít jako název datového typu. Jedná se o typ „množina“.
- FD_ZERO(fd_set *set); makro sloužící k vyprázdnění množiny. Parametrem makra je ukazatel na „datový typ“ množina.
- FD_SET(int fd, fd_set *set); makro sloužící k vložení prvku do množiny. Prvním parametrem je samotný vkládaný prvek, druhým parametrem je ukazatel na „datový typ“ množina.
- FD_CLR(int fd, fd_set *set); makro sloužící k odebrání prvku z množiny. Prvním parametrem je samotný prvek, druhým parametrem je ukazatel na „datový typ“ množina.
- FD_ISSET(int fd, fd_set *set); makro sloužící k otestování množiny na přítomnost nějakého prvku. První parametr je prvek, o kterém chceme zjistit, zda je obsažen v množině. Druhým parametrem je ukazatel na samotnou množinu. Makro se rozbalí na výraz, který vrací 0 (false) v případě, že prvek v množině není. V opačném případě vrátí jakoukoliv nenulovou hodnotu (true) .
Psal jsem tady o množině nějakých prvků. Co jsou ale ty prvky? Podle deklarací, které jsem zde napsal, se jedná o proměnné typu int. Doopravdy se jedná o množinu celých čísel, jenomže my budeme množiny používat jako parametry funkcí (například funkce select), které nebudou očekávat jako parametr množinu jakýchkoliv čísel. Bude se jednat o platné identifikátory souborů (file descriptor) v Linuxu nebo o sokety v MS WindowsŽ. O tom si ale povíme příště.
Ještě jen zbývá podotknout, že v MS WindowsŽ jsou prvky, se kterými se pracuje, typu SOCKET, nikoliv int. Není to ale žádný velký rozdíl, protože již dříve jsem psal, že SOCKET je nové pojmenování (pomocí typedef) typu int. Některé překladače by na případnou záměnu mohly upozornit warningem. Přesný popis maker pro MS WindowsŽ
Ukázkový příklad
Uvedeme si jednoduchý příklad práce s množinou.
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
// Vytvoření množiny
fd_set mySet;
// Vyprázdnění množiny
FD_ZERO(&mySet);
int a = 10, b = 1000;
// Vložím prvky do množiny
FD_SET(a, &mySet);
FD_SET(b, &mySet);
// Zjistím, jestli tam doopravdy jsou
if (FD_ISSET(a, &mySet))
{
cout << "A je tam" <<endl;
}
if (FD_ISSET(a, &mySet))
{
cout << "B je tam" <<endl;
}
if (!FD_ISSET(3, &mySet))
{
cout << "Trojka tam není" <<endl;
}
// Odeberu jeden prvek
FD_CLR(a, &mySet);
if (!FD_ISSET(a, &mySet))
{
cout << "A tam už není" <<endl;
}
return 0;
}
Zdrojový text je určen pro Linux. Přepsat jej pro MS WindowsŽ by neměl být problém. Stačí vyměnit hlavičkové soubory za windows.h a typ prvků z int na SOCKET.
Tímto jsme si ukázali, jak pracovat s množinou fd_set. V příštím článku si ukážeme, jak použít takovou množinu v souvislosti se sokety. Vytvoříme si množinu soketů a pomocí funkce select budeme čekat na nějakou událost (například doručení dat) na jakémkoliv soketu v množině. Tím budeme moci pracovat s více sokety v jednom vláknu. Jako příklad si ukážeme server, který bude poslouchat na více portech najednou.
Dalo by se říci, že makro fd_set lze využít, kdykoliv potřebujeme v programu datový typ „množina celých čísel“. Je to sice pravda, ale C++ nám nabízí daleko pohodlnější práci s množinou. Pomocí šablony set.
Počátek seriálu je na adresehttp://www.builder.cz/serial147.html.