Oprogramowanie systemów równoległych i rozproszonych Gniazda

Transkrypt

Oprogramowanie systemów równoległych i rozproszonych Gniazda
Gniazda - Wstep
˛
Podstawa˛ mechanizmu wejścia-wyjścia dla sieci w systemie
Unix jest abstrakcyjny mechanizm znany jako gniazda (ang.
socket).
Oprogramowanie systemów
równoległych
i rozproszonych
Gniazdo należy traktować jako jako uogólnienie mechanizmu
dostepu
˛
do plików udostepniaj
˛
ace
˛ jeden z końców łacza
˛
komunikacyjnego.
Każdy z komunikujacych
˛
sie˛ ze soba˛ procesów tworzy po
swojej stronie jedno gniazdo.
Dr inż. Tomasz Olas
Główna różnica pomiedzy
˛
deskryptorami plików i gniazdami
polega na tym, że system operacyjny wiaże
˛ deskryptor
z określonym plikiem lub urzadzeniem,
˛
natomiast gniazda
można tworzyć bez wiazania
˛
ich z określonym adresem
odbiorcy.
[email protected]
Instytut Informatyki Teoretycznej i Stosowanej
Politechnika Cz˛estochowska
Gniazdo jest opisywane za pomoca kombinacji trzech
atrybutów: domeny adresowej, sposobu komunikacji
i protokołu sieciowego.
Wykład 5 – p
Wykład 5 – p. 1/??
Sposób komunikacji
Domena adresowa
Gniazda typu SOCK_STREAM - komunikacja w modelu
połaczeniowym
˛
(zwykle zwiazane
˛
z protokołem TCP warstwy
transportowej)
Domena adresowa określa domene˛ w której bedzie
˛
realizowana komunikacja:
PF_LOCAL (PF_UNIX) - komunikacja w obrebie
˛
jednej
maszyny,
PF_INET - Internet, czyli używamy protokołów z rodziny
TCP/IP,
PF_IPX - protokoły IPX/SPX (Novell),
PF_PACKET - niskopoziomowy interfejs do odbierania
pakietów w tzw. surowej (ang. raw) postaci.
Gniazda typu SOCK_DGRAM - komunikacja w modelu
bezpołaczeniowym
˛
korzystajaca
˛ z tzw. datagramów (zwykle
zwiazane
˛
z protokołem UDP warstwy transportowej)
Gniazda typu SOCK_RAW - komunikacja na niskim poziomie dostep
˛ do surowych pakietów na poziomie warstwy sieciowej
TCP/IP.
Zamiennie używa sie˛ notacji PF_xxx (PF - Protocol Family)
oraz AF_xxx (AF - Address Family).
Wykład 5 – p. 3/??
Wykład 5 – p
Zamkniecie
˛
gniazda
Funkcja socketpair
Do tworzenia gniazdek w domenie unixowej może zostać
wykorzystana funkcja socketpair:
Funkcje˛ close służy do zamykania gniazda i kończenia
połaczenia:
˛
#include <sys/types.h>
#include <sys/socket.h>
int close(int fd);
int socketpair(int family, int type, int protocol, int sockvec[2]);
Funkcja zwraca deskryptor gniazda.
family - oznacza domene˛ komunikacyjna˛ (czyli rodzine˛ protokołów adresów) w
jakiej bedzie
˛
funkcjonować nowe gniazdo. W systemie Linux jedyna˛ obsługiwana˛
domena˛ jest PF_UNIX.
type - określa rodzaj gniazda,
fd - deskryptor gniazda,
wynik:
0 - w przypadku sukcesu,
-1 - jeśli wystapił
˛ bład.
˛
Przy wywoływaniu funkcji close zmniejszany jest licznik
odwołań do deskryptora.
Gdy proces sie˛ kończy, system operacyjny zamyka wszystkie
gniazda, które były wówczas otwarte.
protocol - protokół z jakiego gniazdo korzysta,
sockver - gdy nie wystapi
˛ bład
˛ zostana˛ do tej zmiennej zapisana para
deskryptorów gniazd w domenie Unixa. Otrzymuje sie˛ w ten sposób łacze
˛
strumieniowe z możliwościa˛ dwustronnej komunikacji.
Wykład 5 – p
Wykład 5 – p. 5/??
Przesyłanie danych
read/write
Przesyłanie danych poprzez gniazda może odbywać sie˛ z
wykorzystaniem pieciu
˛
różnych funkcji systemowych:
send/write, sendto/recvfrom, sendmsg/recvmsg,
write/read, writev/readv.
Procedury send/recv, sendto/redvfrom oraz
sendmsg/recvmsg wymagaja˛ aby gniazdo było przyłaczone,
˛
gdyż nie umożliwiaja˛ podania adresu odbiorcy.
wiele
dodatkowe
adres
informacje
buforów
znaczniki
partnera
kontrolne
read/write
readv/writev
+
send/recv
+
sendto/recvfrom
sendmsg/recvmsg
+
+
+
+
+
ssize_t read(int fd, void *buf, size_t count)
ssize_t write(int fd, const void *buf, size_t count)
fd - deskryptor,
buff - adres danych do odczytania/zapisania,
length - rozmiar danych do odczytania/zapisania.
wynik:
liczba odczytanych/zapisanych bajtów - w przypadku
sukcesu,
-1 - w przypadku błedu.
˛
+
Wykład 5 – p. 7/??
Wykład 5 – p
socketpair - przykład (1)
socketpair - przykład (2)
...
else
{
// proces macierzysty
close(sockets[0]);
char buf[SIZE];
read(sockets[1], buf, SIZE);
std::cout << "#0: " << getpid() << "\n#0: " << buf << std::endl;
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
const int SIZE = 40;
int main()
{
int sockets[2];
socketpair(PF_UNIX, SOCK_STREAM, 0, sockets);
int pid = fork();
if (pid == 0) {
// proces potomny
close(sockets[1]);
char buf[SIZE];
sprintf(buf, "proces potomny: %d", getpid());
write(sockets[0], buf, sizeof(buf) + 1);
read(sockets[0], buf, SIZE);
close(sockets[0]);
std::cout << "#1: " << getpid() << "\n#1: " << buf << std::endl;
}
sprintf(buf, "proces macierzysty: %d", getpid());
write(sockets[1], buf, sizeof(buf) + 1);
close(sockets[1]);
}
return 0;
}
Wykład 5 – p.
Wykład 5 – p. 9/??
socketpair - przykład (3)
Model klient serwer
Przykładowy rezultat działania programu:
#0:
#0:
#1:
#1:
Termin serwer odnosi sie˛ do każdego programu, który oferuje
usługe˛ dostepn
˛ a˛ poprzez sieć. Serwer przyjmuje przez sieć
zamówienia, wykonuje usługe˛ i zwraca wyniki zamawiajacemu.
˛
1458
proces potomny: 1459
1459
proces macierzysty: 1458
staje sie˛ program, który wysyła zamówienie do
serwera i czeka na odpowiedź.
Klientem
W przypadku modelu klient serwer każde połaczenie
˛
sieciowe
jest tworzone przez klienta wysyłajacego
˛
żadania
˛
do stale
oczekujacego
˛
na nie klienta.
Gniazda używane przez procesy klienckie nazywane sa˛
gniazdami aktywnymi (one inicjuja˛ połaczenie),
˛
natomiast
gnizda wykorzystywane w serwerach nazywane sa˛
analogicznie gniazdami pasywnymi.
Wykład 5 – p. 11/??
Wykład 5 – p.
Komunikacja połaczeniowa
˛
Tworzenie gniazd
Użytkownik identyfikuje gniazda za pomoca˛ deskryptorów
(wykorzystywanych przy każdorazowym odwoływaniu sie˛ do
gniazda)
Funkcja socket tworzy nowe gniazdo i zwraca jego
deskryptor:
int socket (int family, int type, int protocol);
family - oznacza domene˛ komunikacyjna˛ (czyli rodzine˛ protokołów adresów) w
jakiej bedzie
˛
funkcjonować nowe gniazdo, np:
PF_INET - intersieć TCP/IP,
PF_UNIX - system plików systemu Unix,
PF_APPLETALK - itersieć firmy Apple Computer Incorporated,
type - określa rodzaj gniazda,
protocol - protokół z jakiego gniazdo korzysta (w przypadku 0 bedzie
˛
to
domyślny protokół dla danego rodzaju gniazda).
Wykład 5 – p.
Wykład 5 – p. 13/??
Określanie adresu lokalnego
Struktura sockaddr
Poczatkowo
˛
nowo utworzone gniazdo nie jest zwiazane
˛
z
żadnym adresem lokalnym ani odległym.
Po utworzeniu gniazda serwer wiaże
˛ z nim adres lokalny za
pomoca˛ funkcji systemowej bind:
Dla domeny uniksowej:
struct sockaddr_un
{
short sun_family;
char sun_data;
};
int bind(int fd, struct sockaddr *my_addr, int addrlen)
fd - deskryptor gniazda,
my_addr - wskaźnik na strukture˛ adresów odpowiednia˛ dla
rodziny protokołów, do której należy gniazdo,
addrlen - rozmiar tej struktury.
/* AF_UNIX */
/* ścieżka */
Dla domeny internetowej:
struct sockaddr_in
{
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
/*
/*
/*
/*
/*
AF_INET */
16-bitowy numer portu */
32-bitowy internetowy */
adres hosta */
zarezerwowane */
};
Wykład 5 – p. 15/??
Wykład 5 – p.
Serwer - przyjmowanie połacze
˛
ń (I)
Serwer używa funkcji socket, bind i listen do utworzenia
gniazda. Wywołanie bind wiaże
˛ gniazdo z powszechnie
znanym portem protokołu, ale nie powoduje podłaczenia
˛
gniazda do żadnego odległego odbiorcy. Zamiast odległego
odbiorcy podaje sie˛ adres uogólniajacy
˛ (ang. wildcard), by
gniazdo mogło przyjmować połaczenia
˛
od dowolnych klientów.
Po utworzeniu gniazda serwer czeka na połaczenia.
˛
W tym
celu wywołuje procedure˛ systemowa˛ accept.
Serwer - przyjmowanie połacze
˛
ń (II)
Gdy nadchodzi żadanie,
˛
system wypełnia strukture˛ addr
adresem klienta, który je zgłosił.
Nastepnie
˛
system tworzy nowe gniazdo, które jest połaczone
˛
z
klientem i dostarcza wywołujacemu
˛
programowi deskryptor
tego gniazda.
Pierwotne gniazdo nadal nie jest połaczone
˛
z żadnym
konkretnym odległym klientem i pozostaje w dalszym ciagu
˛
otwarte. Dzieki
˛ temu serwer nadal może czekać na żadania
˛
nadchodzace
˛ za pośrednictwem tego gniazda.
Wywołanie accept kończy sie˛ po nadejściu żadania
˛
połaczenia.
˛
Serwer może obsługiwać nadchodzace
˛ żadania
˛
po kolei, badź
˛
też współbieżnie.
Wykład 5 – p. 17/??
Wykład 5 – p.
accept
listen
Funkcja listen ustala maksymalna˛ długość kolejki klientów
oczekujacych
˛
na połaczenie.
˛
#include <sys/types.h>
#include <sys/socket.h>
int listen(int fd, int backlog);
Funkcja accept służy do oczekiwania na połaczenie
˛
- proces
jest blokowany aż do momentu nawiazania
˛
połaczenia
˛
przez
inny proces. W momencie nawiazania
˛
połaczenia
˛
i funkcja
zwraca nowy deskryptor gniazda, który nastepnie
˛
jest używany
do wymiany danych z klientem:
#include &ltsys/types.h>
#include &ltsys/socket.h>
int accept(int fd, struct sockaddr *upeer_sockaddr,
int *upeer_addrlen);
fd - deskryptor gniazda,
backlog - maksymalna liczba zgłoszeń połaczenia
˛
z
serwerem.
wynik:
0 - w przypadku sukcesu,
wartość ujemna - jeśli wystapił
˛ bład.
˛
fd - deskryptor gniazda,
upeer_sockaddr - wskaźnik na nazw˛e gniazda klienta (jako dodatkowy wynik)
upeer_addrlen - rozmiar nazwy upeer_sockaddr (jako dodatkowy wynik)
W przypadku rodziny Unixa gniazdo musi być strumieniowe i
mieć dowiazan
˛
a˛ nazwe.
˛
wynik:
0 - w przypadku sukcesu,
-1 - jeśli wystapił
˛ bład.
˛
W przypadku rodziny Internetu, jeśli gniazdo nie ma nazwy, to
listen automatycznie je dowiaże.
˛
Wykład 5 – p. 19/??
Wykład 5 – p.
Serwer - przykład (I)
#include
#include
#include
#include
#include
Server - przykład (II)
<sys/types.h>
<sys/socket.h>
<sys/un.h>
<unistd.h>
<iostream>
listen(orginal_socket, 1);
sockaddr_un client_address;
socklen_t client_length = sizeof(client_address);
int new_socket = accept(orginal_socket, (sockaddr*)&client_address,
&client_length);
const char name[] = "my_socket";
char buf[255];
read(new_socket, buf, sizeof(buf));
std::cout << buf << std::endl;
int main()
{
int orginal_socket = socket(AF_UNIX, SOCK_STREAM, 0);
close(new_socket);
close(orginal_socket);
unlink(name);
sockaddr_un server_address;
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, name);
unlink(name);
return 0;
}
bind(orginal_socket, (sockaddr*)&server_address,
sizeof(server_address.sun_family) +
sizeof(server_address.sun_path));
Wykład 5 – p. 21/??
Łaczenie
˛
gniazda z adresem odbiorcy
Bezpośrednio po utworzeniu gniazda znajduje sie˛ ono w stanie
co oznacza, że nie jest ono zwiazane
˛
z żadnym
adresatem.
niepołaczonym
˛
,
Procedura systemowa connect trwale łaczy
˛
gniazdo z
adresem odbiorcy, zmieniajac
˛ jednocześnie stan gniazda na
połaczony
˛
.
W przypadku gniazda odpowiadajacego
˛
niezawodnym
strumieniom klient przed rozpocz˛eciem przesyłania danych za
pośrednictwem gniazda musi wywołać procedure˛ connect w
celu uzyskania połaczenia.
˛
Gniazda używane z usługami bezpołaczeniowego
˛
przesyłania
datagramów nie wymagaja˛ podłaczania
˛
przed użyciem, ale
dzieki
˛ podłaczeniu
˛
można wysyłać dane do gniazdka, nie
określajac
˛ adresu odbiorcy.
Wykład 5 – p. 23/??
Wykład 5 – p.
connect
Funkcja connect umożliwia nawiazanie
˛
połaczenia
˛
przy
użyciu podanego gniazda z gniazdem nasłuchujacym:
˛
int connect(int fd, struct sockaddr *uservaddr, int addrlen);
fd - deskryptor gniazda,
uservaddr - struktura określajaca
˛ adres, z którym należy
zwiazać
˛
wskazane gniazdo,
addrlen - rozmiar adresu serwera.
wynik:
0 - w przypadku sukcesu,
wartość ujemna - w przypadku błedu,
˛
np:
· EBADF - fd jest nieprawidłowym deskryptorem,
· ENOTSOCK - fd nie jest deskryptorem gniazda,
· EISCONN - jest już zrealizowane polaczenie.
Wykład 5 – p.
Komunikacja bezpołaczeniowa
˛
Klient - przykład
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
const char name[] = "my_socket";
int main()
{
int orginal_socket = socket(AF_UNIX, SOCK_STREAM, 0);
sockaddr_un server_address;
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, name);
connect(orginal_socket, (sockaddr*)&server_address,
sizeof(server_address.sun_family) + sizeof(server_address.sun_path));
char buf[] = "hello";
write(orginal_socket, buf, sizeof(buf));
close(orginal_socket);
return 0;
}
Wykład 5 – p.
Wykład 5 – p. 25/??
Funkcje send i sendto
Funkcje recv i recvfrom
ssize_t send(int s, const void *buf, size_t len, int flags);
ssize_t sendto(int s, const void *buf, size_t len, int flags,
const struct sockaddr *to, socklen_t tolen);
ssize_t recvfrom(int s, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen);
ssize_t recv(int s, void *buf, size_t len, int flags);
ssize_t recvfrom(int s, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen);
s - deskryptor gniazda, przez które odbieramy dane,
buf - adres (wskaźnik) bufora, który bedzie
˛
zawierał dane po
ich odebraniu,
s - deskryptor gniazda, przez które wysyłamy dane,
buf - wskaźnik do bufora, który chcemy wysłać,
len - liczba bajtów bufora do wysłania,
len - rozmiar bufora (maksymalna liczba bajtów, która˛ można
jednorazowo odebrać),
flags - dodatkowe flagi (np. codeMSG_DONTWAIT Przełacza
˛
funkcje˛ w tryb nieblokujacy),
˛
flags - dodatkowe flagi,
to - wskaźnik do struktury zawierajacej
˛ adres odbiorcy,
to - wwskaźnik do struktury, przez która˛ zwrócony zostanie
adres nadawcy,
addrlen - rozmiar struktury zawierajacej
˛ adres.
addrlen - rozmiar struktury zawierajacej
˛ adres.
wynik:
liczba odczytanych/zapisanych bajtów - w przypadku
sukcesu,
-1 - w przypadku błedu.
˛
Wykład 5 – p. 27/??
Wykład 5 – p.
Przykład - serwer
Przykład - klient
const char name[] = "my_socket";
int main()
{
int orginal_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
const char name[] = "my_socket";
int main(int argc, char** argv)
{
int orginal_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
unlink(name);
sockaddr_un server_address;
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, name);
bind(orginal_socket, (sockaddr*)&server_address,
sizeof(server_address.sun_family) + sizeof(server_address.sun_path));
char buf[255];
while (true) {
socklen_t client_length;
recvfrom(orginal_socket, buf, sizeof(buf), 0,
(sockaddr*)&server_address, &client_length);
std::cout << buf << std::endl;
}
close(orginal_socket); return 0;
}
sockaddr_un server_address;
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, name);
bind(orginal_socket, (sockaddr*)&server_address,
sizeof(server_address.sun_family) + sizeof(server_address.sun_path));
sendto(orginal_socket, argv[1], sizeof(argv[1]), 0,
(sockaddr*)&server_address, sizeof(sockaddr));
close(orginal_socket);
return 0;
}
Wykład 5 – p.
Wykład 5 – p. 29/??
Numer portu
Adres sieciowy
Każdy host w sieci ma przynajmniej dwa tylko właściwe sobie
adresy:
48-bitowy adres ethernetowy (przypisywany ethernetowej
karcie sieciowej przez producenta),
32-bitowy adres internetowy (tzw. numer IP). W interakcji z
użytkownikiem jest on podzielony na cztery 8-bitowe liczby
dziesietne
˛
separowane kropkami. Każda z tych liczb może
sie˛ zawierać w przedziale od 0 do 255, choć wartości
brzegowe sa˛ używane jako wartości specjalnego
przeznaczenia.
Wykład 5 – p. 31/??
Numer portu jest liczba˛ 16-bitowa˛ bez znaku.
Jednoznacznie identyfikuje połaczenie
˛
sieciowe w ramach
jednego adresu IP.
Para (adres IP, numer portu) jednoznacznie określa komputer
w sieci, jak również konkretny proces działajacy
˛ na tym
komputerze.
Wykład 5 – p.
Funkcja gethostbyname (I)
Funkcja gethostbyname (II)
W przypadku domeny internetowej procesy musza˛
dysponować swoimi adresami hostów i numerami portów, aby
mogły ze soba˛ nawiazać
˛
kontakt.
Funkcja gethostbyname zwraca pełne informacje o hoście,
którego nazwe˛ podano w argumentach:
#include
#include
#include
#include
<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<netdb.h>
struct hostent
{
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
#define h_addr h_addr_list[0]
};
h_name - oficjalna nazwa hosta,
h_aliases - lista aliasów,
hostent* gethostbyname(const char* name);
h_addrtype - typ adresu hosta,
name - wskaźnik na napis zawierajacy
˛ nazw˛e hosta.
h_length - długość adresu,
Jeśli nazwa hosta zostanie znaleziona funkcja zwróci wskaźnik na strukture˛
hostent, w przeciwnym wypadku zwróci NULL.
h_addr_list - lista adresów z serwera nazw.
Wykład 5 – p. 33/??
Wykład 5 – p.
gethostbyname - przykład (I)
Zmiana kolejności bajtów
Poszczególne typy komputerów różnia˛ sie˛ sposobem
przechowywania liczb, a protokoły TCP/IP określaja˛ niezależny
od maszyny standard porzadku
˛
bajtów liczb.
System Unix BSD oferuje cztery procedury biblioteczne
służace
˛ do konwersji miedzy
˛
porzadkiem
˛
bajtów na danym
komputerze oraz sieciowym porzadkiem
˛
bajtów
(<netinet/in.h>):
Do zamiany liczby z reprezentacji sieciowej do reprezentacji lokalnej (network to
host) służa˛ funkcje:
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);
Do konwersji liczb z porzadku
˛
danego komputera na porzadek
˛
sieciowy (host to
network) służa˛ funkcje:
uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);
Wykład 5 – p. 35/??
hostent *host;
std::string hostName;
std::cout << "Podaj nazwe hosta: " << std::flush;
std::getline(std::cin, hostName);
host = gethostbyname(hostName.c_str());
if (host != NULL)
{
std::cout << "Oficialna nazwa: " << host->h_name << std::endl
<< "Aliasy: " << std::endl;
while (*host->h_aliases)
{
std::cout << "
" << *host->h_aliases << std::endl;
++host->h_aliases;
}
std::cout << "Typ adresu: " << host->h_addrtype << std::endl
<< "Dlugosc adresu: " << host->h_length << std::endl
<< "Lista adresów: " << std::endl;
Wykład 5 – p.
gethostbyname - przykład (II)
Domena internetowa - serwer (I)
#include
#include
#include
#include
#include
#include
#include
while (*host->h_addr_list) {
in_addr in;
memcpy(&in.s_addr, *host->h_addr_list, sizeof(in.s_addr));
std::cout << "[" << host->h_addr_list << "] = "
<< *host->h_addr_list
<< inet_ntoa(in) << std::endl;
++host->h_addr_list;
}
}
}
<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<arpa/inet.h>
<unistd.h>
<iostream>
"local.h"
int main()
{
int orginal_socket = socket(AF_INET, SOCK_STREAM, 0);
if (orginal_socket < 0)
{
perror("blad generowania");
exit(1);
}
sockaddr_in server_address;
memset(&server_address, 0, sizeof(sockaddr_in));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(PORT);
Przykładowy wynik działania programu:
Podaj nazwe hosta: localhost
Oficialna nazwa: localhost
Aliasy:
Typ adresu: 2
Dlugosc adresu: 4
Lista adresów:
[0x804a648] = 127.0.0.1
Wykład 5 – p.
Wykład 5 – p. 37/??
Domena internetowa - serwer (II)
Domena internetowa - serwer (III)
if (bind(orginal_socket, (sockaddr*)&server_address,
sizeof(server_address)) < 0) {
perror("blad dowiazania");
exit(2);
}
if (listen(orginal_socket, 5) < 0) {
perror("blad nasluchu");
exit(3);
}
if (fork() == 0) {
int len;
const int BUFSIZE = 255;
char buf[BUFSIZE];
while ((len = read(new_socket, buf, BUFSIZE)) > 0)
{
for (int i = 0; i < len; i++)
buf[i] = toupper(buf[i]);
write(new_socket, buf, len);
if (buf[0] == ’.’)
break;
}
close(new_socket);
exit(0);
}
else
close(new_socket);
do {
sockaddr_in client_address;
socklen_t client_len = sizeof(client_address);
int new_socket = accept(orginal_socket,
(sockaddr*)&client_address,
&client_len);
if (new_socket < 0) {
perror("blad akceptowania");
exit(4);
}
}
while (true);
}
Wykład 5 – p. 39/??
Wykład 5 – p.
Domena internetowa - klient (I)
Domena internetowa - klient (II)
int main(int argc, char** argv)
{
hostent *host = gethostbyname(argv[1]);
char buf[255];
std::string line;
do
{
std::cout << "> " << std::flush;
getline(std::cin, line);
sockaddr_in server_address;
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
memcpy(&server_address.sin_addr, host->h_addr, host->h_length);
server_address.sin_port = htons(PORT);
write(orginal_socket, line.c_str(), line.size()+1);
read(orginal_socket, buf, 255);
std::cout << buf << std::endl;
int orginal_socket = socket(AF_INET, SOCK_STREAM, 0);
if (orginal_socket < 0) {
perror("blad generowania");
exit(3);
}
}
while (line[0] != ’.’);
close(orginal_socket);
exit(0);
}
if (connect(orginal_socket, (sockaddr*)&server_address,
sizeof(server_address)) < 0) {
perror("blad polaczenia");
exit(4);
}
Wykład 5 – p. 41/??
Lokalne i odległe adresy gniazd
Wykład 5 – p.
Obsługa wielu klientów
Do ustalenia adresu odbiorcy, z którym jest połaczone
˛
dane
gniazdo służy funkcja:
Funkcje accept, send, recv, sendto, recfrom sa˛
blokujace.
˛
#include <sys/socket.h>
int getpeername(int s, struct sockaddr *name, socklen_t *namelen);
Aby serwer mógł obsługiwać wielu klientów należy:
utworzyć dla każdego klienta oddzielny proces,
obsługiwać poszczególnych klientów przy użyciu
dodatkowych watków,
˛
wykorzystać funkcje˛ select.
Procedura getsockname dostarcza lokalnego adresu
zwiazanego
˛
z danym gniazdem:
#include <sys/socket.h>
int getsockname(int s, struct sockaddr *name, socklen_t *namelen);
Wykład 5 – p. 43/??
Wykład 5 – p.

Podobne dokumenty