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}