Opis problemu
Załóżmy, że napisaliśmy program działający jako serwer pewengo protokołu (np. xmpp), który oczekuje na komunikaty od użytkownika, ale jednocześnie co stały okres czasu wysyła mu pewne informacje (np. o zmianie statusu innych użytkowników).
Poniższy kod pokazuje działanie najprostszego programu czytającego dane z deskryptora i wykonującego na nich operacje:
Problem, na który trafiamy to np.:
- odbiór danych od wielu użytkowników (z wielu różnych socketów) w jednym procesie,
- jednoczense oczekiwania na dane od strony użytkownika i niezależna od tego możliwość wysłania do użytkownika pewnych danych.
Do dyspozycji mamy jeden deskryptor, więc to co chcemy zrobić to oczekiwać na dane z niego pochodzące, ale z możliwością przerwania tego oczekiwania i wykonania innego zadania (np. zapisania do socketu komunikatu lub odczytania z innego socketu komunikatu).
W powyższym przykładzie funkcja read(sock1,c,20) zadziała w sposób blokujący – tj. do czasu aż nie otrzymamy danych w sock1, funkcja zablokuje działanie programu.
Rozwiązanie 1
Poniżej prezentujemy metodę na ustawienie deskryptora tak by nie był blokujący (tj. by operacja odczytu nie zatrzymywała działania aplikacji):
Rozwiązanie 2
Funkcja select czeka aż jeden z grupy deskryptorów readfds będzie gotowy do odczytu lub jeden z grupy deskryptorów writefds będzie gotowy do zapisu (lub jeden z grupy deskryptorów exceptfds) będzie miał wyjątki. Struktura tiemout zawiera maksymalny czas czekania.
W przypadku gdy którąś grupę deskryptorów ustawimy na NULL, select nie będzie na nie czekała.
Funkcja:
- int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) – funkcja pozwalająca na monitorowanie kilku deskryptorów (należących do zbioru o nazwie exceptfds) czekająca aż jeden z nich będzie gotowy do wykonania operacji I/O (tj. deskryptory readfds do operacji read, writefds do operacji write), w przypadku braku gotowości, funkcja kończy swoje działanie po upłynięciu czasu określonego w timeout),
- int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask) – funkcja dla chętnych.
Makra FD:
- void FD_CLR(int fd, fd_set *fdset) – usuwa deskryptor fd ze zbioru fdset,
- int FD_ISSET(int fd, fd_set *fdset) – sprawdza czy deskryptor fd jest elementem zbioru fdset,
- void FD_SET(int fd, fd_set *fdset) – dodaje deskryptor fd do zbioru fdset,
- void FD_ZERO(fd_set *fdset) – czyści zbiór fdset,
Zadania:
- Napisz aplikację serwera i klienta, która bedzie działała jak “mini czat”. Serwer po uruchomieniu będzie nasłuchiwał na porcie 10000, a po połączeniu klienta pozwoli na komunikację użytkownikowi serwera i użytkownikowi klienta poprzez przesyłanie całych linii tekstu odczytanego z klawiatury.