lab5
Instrukcja Laboratoryjna 5
Serwer współbieżny - instrukcja select.
1) Wstęp teoretyczny
Po zastosowaniu w programie funkcji select, jądro usypia proces i oczekuje na
zdarzenie (np.: gotowy deskryptor połączenia) jądro wybudza proces w przypadku gdy
nastąpi połączenie. Wywołanie funkcji selekt blokuje proces i oczekuje do momentu
utworzenia jednego z deskryptorów gotowego do czytania, pisania, lub do pobrania wyjątku.
Plik nagłówkowy: sys/socked.h
Postać funkcji: int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
const struct timeval *timeout);
Argumenty:
maxfdp1 liczba większa o 1 od ostatniego przekazanego deskryptora
readset deskryptory, oczekiwanie na możliwość czytania, jeżeli nie wartość NULL
writeset deskryptory, oczekiwanie na możliwość pisania, jeżeli nie wartość NULL
exceptset deskryptory, oczekiwanie na wystąpienie wyjątku, jeżeli nie wartość NULL
timeout czas oczekiwania – maksymalny (w s oraz us)
Funkcja zwraca następujące wartości:
• ilość deskryptorów
• 0 – upłynął czas żądania, deskryptory nie gotowe
• -1 – wystąpił błąd
Wymieniony typ fd_set można obłsugiwać poprzez instrukcje:
void FD_ZERO (fd_set *fdset) czyszczenie zbioru
void FD_SET (int fd, fd_set *fdset) dodanie do zbioru deskryptora fd
void FD_CLR (int fd, fd_set *fdset) usuwa deskryptor ze zbioru
int FD_ISSET (int fd, fd_set *fdset) 1 – deskryptor należy do zbioru, 0 – nie należy
Przykładowy program:
Serwer ma działać w oparciu o funkcję select. Aplikacja serwera tworzy tablicę klientów,
której wielkość zdefiniowana jest przez zmienną globalną SIZE. Gdy klient próbuje połączyć
się z serwerem następuje przeszukanie w tablicy wolnego elementu (równego -1),
a następnie przypisanie wolnego miejsca. Zestawiane jest nowe połączenie dla klienta oraz
zapisywany jest identyfikator deskryptora (deskryptor dodawany jest do zbioru FD).
Następnie wykonywane są zadane przez klienta operacje, po czym serwer w pętli sprawdza
po kolei które z połączeń są nieaktywne i zamyka je, zwalniając miejsce w tabeli klientów.
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/time.h>
#define MAX 4096
#define LISTENQ 1024
#define SIZE 10
void err_sys(char *s)
{
printf("%sn",s);
exit (-1);
}
int main(int arg, char **argv)
{
int connfd, listenfd, sockfd, i, j=0, liczba[2], wynik=0, tmp=0, maxfd, maxi, nready, client[SIZE];
ssize_t n;
fd_set rset, allset;
struct sockaddr_in servaddr, cliaddr;
socklen_t clilen;
char bufor[MAX], bufortmp[MAX], tmpchar, tabtmp[MAX];
if((listenfd=socket(AF_INET, SOCK_STREAM, 0))<0)
err_sys("Błąd otwierania socketa");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port = htons(13);
if(bind(listenfd,(struct sockaddr *)&servaddr, sizeof(servaddr))<0)
err_sys("Błąd przybicia gniazda");
if(listen(listenfd,LISTENQ)<0)
err_sys("Błąd nasłuchu");
maxfd = listenfd;
maxi=-1;
for(i=0; i<SIZE;i++)
client[i] = -1;
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for(;;)
{
rset = allset;
nready = select(maxfd+1, &rset, NULL,NULL,NULL);
if(FD_ISSET(listenfd, &rset))
{
clilen = sizeof(cliaddr);
connfd = accept(listenfd,(struct sockaddr *) &cliaddr, &clilen);
for(i=0; i<SIZE;i++)
if(client[i]<0)
{
client[i]=connfd;
break;
}
if(i == SIZE)
err_sys("za duzo klientow");
FD_SET(connfd,&allset);
if(connfd>maxfd)
maxfd=connfd;
if(i>maxi)
maxi = i;
if(--nready <= 0)
continue;
}
for(i=0;i<=maxi;i++)
{
if((sockfd=client[i])<0)
continue;
if(FD_ISSET(sockfd, &rset))
{
if((n=read(sockfd,bufor,MAX))==0)
{
close(sockfd);
FD_CLR(sockfd,&allset);
client[i]=-1;
}
else
{
for(j=0; j<sizeof(bufor); j++)
{ if(bufor[j]=='+'||bufor[j]=='-'||bufor[j]=='*'||bufor[j]=='/')
{
tmpchar = bufor[j];
tmp = j;
break;
}
else
bufortmp[j] = bufor[j];
liczba[0]=atoi(bufortmp);
for(j=tmp+1; j<sizeof(bufor); j++)
bufortmp[j-tmp-1]=bufor[j];
liczba[1]=atoi(bufortmp);
if(tmpchar=='+')
wynik=liczba[0]+liczba[1];
if(tmpchar=='-')
wynik=liczba[0]-liczba[1];
if(tmpchar=='*')
wynik=liczba[0]*liczba[1];
if(tmpchar=='/')
wynik=liczba[0]/liczba[1];
char wynikstr[MAX];
bzero(wynikstr,sizeof(wynikstr));
sprintf(tabtmp, "%i", wynik);
strcat(wynikstr,"Wynik działania: ");
strcat(wynikstr,tabtmp);
bzero(bufor,sizeof(bufor));
bzero(bufortmp,sizeof(bufortmp));
bzero(liczba,sizeof(liczba));
if(write(connfd, wynikstr, strlen(wynikstr))<=0)
printf("Błąd wysyłania");
} if(--nready<=0)
break;
} //end else
} //end if
} //end for
} //end for
} //end main
2) Zadania do wykonania:
Warunkiem zaliczenia niniejszego laboratorium jest napisanie programów: serwera
współbieżnego oraz klienta, wykorzystujących do komunikacji funkcję select().
Można wykorzystać programy iteracyjne z zajęć poprzednich !!!
Student powinien mieć opanowany materiał:
zwielokrotnione operacje wejścia – wyjścia