SOISK - SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE
Tomasz Puchała

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