Model klient – serwer
Transkrypt
Model klient – serwer
Model klient – serwer Większość komunikacji międzyprocesowej jest oparta na modelu klient-serwer. Jeden z procesów inicjuje komunikację a następnie pobiera i/lub wysyła dane. Klient musi posiadać informację co do adresu serwera ( w modelu TCP/IP także portu). Serwer nie musi wiedzieć o istnieniu klienta do momentu nawiązania połączenia. Od momentu nawiązania komunikacji obie strony mogą wysyłać i odbierać dane. Wywołania systemowe są różne dla klienta i serwera natomiast wykorzystują tę samą kontrukcję – gniazdo (socket ). Gniazdo jest zakończeniem kanału komunikacyjnego (przez analogię do np gniazda sieciowego czy elektrycznego). Każdy z procesów klient i serwer tworzą własne gniazda. Etapy nawiązywania połączenia. 1. za pomocą wywołania systemowego socket() tworzy się gniazdo 2. Połączenie do gniazda na serwerze przy użyciu wywołania systemowego connect() 3. wysyłanie i odbieranie danych: 1. najprostsze funkcje to read() i write() 2. send() recv() 4. zamknięcie połączenia – wywołanie systemowe close() Potrzebne struktury i funkcje Struktura sockaddr_in – przechowuje dane dotyczące gniazda protokołu TCP/IP zdefiniowana jest w pliku #include <netinet/in.h> struct sockaddr_in w następujący sposób struct sockaddr_in { short sin_family; /* tylko AF_INET */ u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; /* Nie używana – musi być 0 */ }; Wywołanie systemowe socket() Tworzy gniazdo odpowiedniego typu (typu internetowego AF_INET lub tzw gniazda unixowe, które odwołują się do pliku) #include <sys/socket.h> int socket(int socket_family, int socket_type, int protocol); parametry: socket_family - AF_INET – dla TCP/IP v4 lub AF_UNIX socket_type - typ gniazda – determinuje protokół warstwy transportowej SOCK_STREAM – tworzy sesję SOCK_DGRAM – tryb bezpołączeniowy protocol – zależne od implementacji w systemie, domyślnie 0 – tzn automatyczny wybór protokołu wartość zwracana : gdy >0 oznacza sukces i jest uchwytem gniazda < 1 błąd Wywołanie connect() Nawiązuje połączenie wykorzystując dane ze struktury socketaddr i utworzone wcześniej gniazdo #include <sys/socket.h> int connect(int socket, const struct sockaddr *address, socklen_t address_len); parametry: socket – uchwyt do gniazda otrzymany z funkcji socket() sockaddr – wskazanie do struktury sockaddr_in przykład inicjalizacji tej struktury struct sockaddr_in gn; struct hostent *hp; memset(&gn, 0, sizeof(gn)); gn.sin_family = AF_INET; gn.sin_addr.s_addr=((struct in_addr *)(hp->h_addr))->s_addr; gn.sin_port = htons(PORT); address_len – rozmiar struktury sockaddr_in Zwracana wartość: 0 – powodzenie -1 - błąd przykład użycia if (connect(sd,(struct sockaddr *) &gn, sizeof(gn)) == -1) { perror("błąd wywołania: connect"); exit(1); } Odczyt i zapis do gniazda #include <unistd.h> ssize_t read(int fd, void *buf, size_t count); #include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); #include <sys/types.h> #include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); send(sockfd, buf, len, flags); jest równoważne: sendto(sockfd, buf, len, flags, NULL, 0); #include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); Zwracane wartości: ilość bajtów w przypadku powodzenia operacji -1 w przypadku błędu 0 – zamkniecie połączenia przez drugą stronę Zamykanie połączenia close() przykład użycia: int sd; ... close (sd); Zadanie 1: Napisać program, który łączy się z serwerem o podanej w linii poleceń nazwie i porcie, odczytuje odpowiedź funkcją read(), wyświetla ją i zamyka połączenie. Zadane 2: Napisać program, który łączy się z serwerem o podanej w linii poleceń nazwie i porcie, wysyła ciąg znaków (imię i nazwisko) przez send() i odczytuje odpowiedź funkcją recv(), wyświetla ją i zamyka połączenie. Otrzymany w odpowiedzi token proszę dołączyć do kodu programu i wysłać na wskazany na zajęciach adres z tematem P-sieci-3-{grupa} {ew Imię i Nazwisko}