SOISK - SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE
Tomasz Puchała

PUS 6

Aplikacje wykorzystujące protokół UDP
 
1) Wstęp teoretyczny
Celem ćwiczenia jest wykonanie aplikacji serwera i klienta, wykorzystującego gniazdową strukturę
protokołu UDP.
W odróżnieniu od protokołu TCP/IP, protokół UDP nie zawiera mechanizmów sprawdzania
poprawności przesyłanych danych. Serwer powinien charakteryzować się możliwością obsługi wielu
klientów chcących wymienić porcje informacji. W rzeczywistości serwer obsługuje wszystkich
klientów, którzy oczekują w kolejce po wysłaniu datagramów. W procesie komunikacji wielu klientów
nie jest tworzony proces potomny obsługujący osobno każdego podłączonego klienta. Jeden proces
obsługuje wszystkich klientów, przy czym ich kolejność obsługiwania jest jednoznacznie określona,
klienci są obsługiwani zgodnie z kolejnością podłączania FIFO.
Z datagramu IP Klienta Serwer TCP Serwer UDP
Adres IP accept recvfrom
Port accept recvfrom
Adres IP serwera getsockname recvmsg
Port serwera getsockname getsockname
Każde gniazdo UDP posiada bufor odbiorczy i każdy datagram wysyłany do tego gniazda jest
umieszczany w buforze. Bufor danych ma ograniczony rozmiar (parametr SO_RCVBUF), stosuje się gdy
chcemy poprawić skuteczność odbioru datagramów – zwiększenie wielkości bufora.
Należy pamiętać, że w przypadku połączenia, gdy użyjemy funkcji odczytu recvfrom, a serwer lub
router zagubi pewne datagramy funkcja zablokuje proces klienta. Można temu zapobiec przez
zastosowanie np.: czasu oczekiwania na odpowiedź.
Aby zrealizować zadanie niniejszej instrukcji, należy wykorzystać programy iteracyjne z poprzednich
zajęć -> dokonać modyfikacji aplikacji tak aby do komunikacji wykorzystywały protokół UDP !!!
Aby zrealizować zadania niniejszej instrukcji student powinien mieć opanowany materiał na temat:
· Protokołu UDP
· Gniazdowej struktury adresowej
· Gniazd protokołu UDP
Przykładowe aplikacje:
Serwer:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define MAXLINE 4096
#define LISTENQ 1024
void err(char* s)
{
printf("%sn", s);
printf("Errno: %dn", errno);
fprintf(stderr, "%sn", strerror(errno));
exit(-1);
}
int main(int argc, char **argv)
{
int listenfd;
int l[3], j=0,len;
float wynik;
struct sockaddr_in servaddr,cliaddr;
char buff[MAXLINE], recvline[MAXLINE],buff2[MAXLINE],str[MAXLINE],z;
int currpos = 0, i, n;
if (argc != 2)
err("Uzycie: SERWER PORT");
if ((listenfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
err("socket error");
int nn = 240 * 1024; //ustawienie rozmiaru bufora odbioru
setsockopt(listenfd, SOL_SOCKET, SO_RCVBUF, &nn, sizeof(nn));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if ((servaddr.sin_port = htons(atoi(argv[1]))) < 0)
err("Zly format PORT");
if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
err("bind error");
while (1)
{
len = sizeof(cliaddr);
if ((n = recvfrom(listenfd, buff, MAXLINE, 0, (struct sockaddr *) &cliaddr, &len)) >0)
{
j=0;
for (i = 0; i < n; i++)
{
if (j==2) j=0;
if (buff[i]==' ')
{
currpos=0;
l[j]=atoi(recvline);
if (j==1) z=buff[i-1];
j++;
}
recvline[currpos++] = buff[i];
if (buff[i] == 'n')
{
buff[i + 1] = 0;
if (!strcmp(buff, "n"))
{
printf("pusta linian");
}
else
{
l[2]=atoi(recvline);
wynik=0;
strncat(buff2, buff, n-1);
strcat(buff2," = ");
if (z=='+')
wynik=l[0]+l[2];
if (z=='-')
wynik=l[0]-l[2];
if (z=='*')
wynik=l[0]*l[2];
if(z=='/')
{
if(l[2]==0)
{
sprintf(buff,"nie dzieli się przez zero!n");
if (sendto(listenfd, buff, sizeof(buff), 0, (struct sockaddr *) &cliaddr, len) < 0)
err("sendto error");
bzero(buff,sizeof(buff));
bzero(buff2,sizeof(buff2));
bzero(l,sizeof(l));
bzero(recvline,sizeof(recvline));
currpos=0;
j=0;
continue;
}
wynik=(float)l[0]/l[2];
}
if (z=='%')
wynik=l[0]%l[2];
sprintf(str, "%f", wynik);
strcat(buff2,str);
bzero(buff,sizeof(buff));
strcat(buff,buff2);
strcat(buff,"n");
bzero(buff2,sizeof(buff2));
bzero(l,sizeof(l));
if (sendto(listenfd, buff, sizeof(buff), 0,
(struct sockaddr *) &cliaddr, len) < 0)
err("sendto error");
printf("do klienta => %s wysłałem %s",(char *)inet_ntoa(cliaddr.sin_addr),buff);
bzero(recvline,sizeof(recvline));
currpos = 0;
bzero(buff,sizeof(buff));
}
currpos = 0;
break;
}
}
}
else
err("recvfrom error");
}
return 0;
}
Klient:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define MAXLINE 4096
int err(char* s)
{
printf("%sn", s);
printf("Errno: %dn", errno);
fprintf(stderr, "%sn", strerror(errno));
exit(-1);
}
int main(int argc, char **argv)
{
int sockfd, n;
int i=0;
char k[100];
char recvline[MAXLINE+1], buff[MAXLINE+1];
struct sockaddr_in servaddr;
if (argc != 3)
err("Sposub uzycia: klient IP_ADDRESS PORT");
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
err("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0)
err("Zly format IP_ADDRESS");
else
printf("Address: %sn", argv[1]);
if ((servaddr.sin_port = htons(atoi(argv[2]))) < 0)
err("Zly format PORT");
else
printf("Port polaczenia: %dn", servaddr.sin_port);
while (1)
{
bzero(buff,sizeof(buff));
printf("wprowadź działanie w postaci a + bnq - wyjście z programun");
fgets(k, sizeof(k), stdin);
if (k[0]=='q')
break;
strcat(buff,k);
if (sendto(sockfd, buff, strlen(buff), 0,
(struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
err("sendto error");
printf("wysłałemn");
if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0)
err("recvfrom error");
recvline[n] = 0;
if (fputs(recvline, stdout) == EOF)
err("fputs error");
if (n < 0)
err("read error");
} //end while
if (close(sockfd) < 0)
err("close error");
return 0;
} //end main
2) Zadania do wykonania
Aby zaliczyć laboratorium 6 należy wykonać jedno z poleceń:
· Zmodyfikować aplikację serwera i klienta iteracyjnego z poprzednich zajęć tak aby
komunikacja odbywała się „po protokole UDP”.
· Zmodyfikować programy zawarte w instrukcji tak aby istniała możliwość przepełnienia bufora
danych – określić bardzo mały rozmiar kilkadziesiąt bajtów.
· Powrócić do domyślnego rozmiaru bufora danych UDP (servaddr.sin_len=sizeof(servaddr).
· Zliczyć ilość wysłanych datagramów (na ocenę 5)
Ponadto zabezpieczyć programy przed błędami funkcjonowania, przerwanie działania, błędami
komunikacji itp..