Nieblokujący odczyt

  1. Opis problemu blkowania deskryptora

    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:
    char c[20]; int i;
    while( i=read(sock, &c, 20)>0) do_something(&c,i);
    
    Problem pojawia się gdy program miałby obsłużyć więcej niż jednego użytkownika (kilka deskryptorów).
    char c[20]; int i;
    while (1)
    {
    	i=read(sock1, c,20);
    	if (i>0)
    		do_something(c,i)
    	else if (i==-1)
    		perror("Blad read(sock1,...)");
    
    	i=read(sock2, c,20);
    	if (i>0)
    		do_something(c,i)
    	else if (i==-1)
    		perror("Blad read(sock2,...)");
    }
    
    Niestety powyższy kod działa w sposób blokujący, tj. program zatrzyma się na linii czytającej z sock1 do czasu aż czegoś nie odczyta.

  2. 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):
    char c[20]; int i;
    fcntl(sock1,F_SETFL, fcntl(sock1,F_GETFL)|O_NONBLOCK ); //ustawiamy sock1 by nie był blokujący
    fcntl(sock2,F_SETFL, fcntl(sock2,F_GETFL)|O_NONBLOCK ); //ustawiamy sock2 by nie był blokujący
    while(1)
    {
    	....
    
  3. Rozwiązanie 2 - select() oraz makra FD()
    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.
    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) ;
    
    void FD_CLR(int fd, fd_set *fdset);
    int FD_ISSET(int fd,  fd_set *fdset);
    void FD_SET(int fd,  fd_set *fdset);
    void FD_ZERO(fd_set *fdset);
    
    Przykład:
    int main()
    {
    	.......
    	.......
    	//deklaracja grupy deskryptorów o nazwie Read0  oraz struktury timeval
    	fd_set Read0;
    	struct timeval tv;
    
    
    	while(1) {
    		// ustawiamy tv na 4 sekundy
    		tv.tv_sec = 4;
    		tv.tv_usec = 0;
    
    		//zerujemy Read0
    		FD_ZERO(&Read0);
    
    		//dodajemy sock1 i sock2 do grupy Read0
    		FD_SET(sock1, &Read0);
    		FD_SET(sock2, &Read0);
    
    		//wywołyjemy blokującą funkcję select 
    		select(FD_SETSIZE, &Read0, NULL, NULL, &tv);
    		
    		if( FD_ISSET(sock1,&Read0) ) { 
    			read(sock1,c,40); 
    			do_something(....)
    		}
    		if( FD_ISSET(sock2,&Read0) ) { 
    			read(sock2,c,40); 
    			do_something(....)
    		}
    	}	
    }
    
  4. Zadanie 1

    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.