background image

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

background image

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);

background image

       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}