Programowanie dla sieci SOCKET
Transkrypt
Programowanie dla sieci SOCKET
Komunikacja przez sieć z wykorzystaniem biblioteki WINSOCK dr inż. Piotr Kaczmarek Instytut Automatyki i Inżynierii Informatycznej LAN (Local Area Network) Warstwa fizyczna Podstawowe topologie: -Gwiazda -Magistrala -Ring Medium: -UTP, optical fiber, 2.4GHz ... Model ISO OSI Warstwa fizyczna (physical layer) Warstwa fizyczna: (physical layer) Jest odpowiedzialna za transmisję strumienia bitów między węzłami sieci. Definiuje protokoły opisujące interfejsy fizyczne, to jest ich aspekty: mechaniczny, elektryczny, funkcjonalny i proceduralny. Do funkcji tej warstwy należą: sprzęgniecie z medium transmisji danych, dekodowanie sygnałów, określanie zakresu amplitudy prądu lub napięcia i określanie parametrów mechanicznych łączówek (kształtu, wymiarów i liczby styków) oraz inne kwestie związane z transmisją bitów. Warstwa łącza danych Zapewnia niezawodne łącze pomiędzy sąsiednimi węzłami. Nadzoruje przepływ informacji przez łącze i w związku z podatnością warstwy fizycznej na zakłócenia i wynikające stąd błędy oferuje własne mechanizmy kontroli błędów w przesyłanych ramkach lub pakietach (CRC - Cyclic Redundancy Check). Adres MAC MAC (ang. Media Access Control) podwarstwa warstwy łącza danych (ang. Data Link Layer, DLL) w modelu OSI sprzętowy adres karty sieciowej Ethernet i Token Ring, unikatowy w skali światowej, nadawany przez producenta danej karty podczas produkcji. Adres MAC (ang. MAC address) jest 48-bitowy i zapisywany jest heksadecymalnie (szesnastkowo). Pierwsze 24 bity oznaczają producenta karty sieciowej, pozostałe 24 bity są unikatowym identyfikatorem danego egzemplarza karty. Na przykład adres 00:0A:E6:3E:FD:E1 oznacza, że karta została wyprodukowana przez Elitegroup Computer System Co. (ECS) i producent nadał jej numer 3E:FD:E1. Mechanizmy wykrywania kolizji CSMA/CD (Carrier Sense Multiple Access with Collision Detect) Metoda dostępu CSMA/CD została zastosowana w takich sieciach jak: • Ethernet (IEEE 802.3), • IsoEthernet (IEEE 802.9) • Fast Ethernet (IEEE 802.3u) - rozszerzenie 802.3 • (inna nazwa ISO 8802.3u) lub 100Base-T, • Gigabitowy Ethernet. CSMA/CD 1) stacja, która wykryje kolizję jako pierwsza, natychmiast przestaje nadawać i wysyła charakterystyczny sygnał zakłócający - tzw. jamming signal. 2) Wszystkie pozostałe stacje usłyszawszy ten sygnał, przerywają swoje transmisje na losowo wybrany przedział czasu t. (Czas ten nie jest zupełnie losowy, jest bowiem wyznaczany na podstawie odpowiedniego algorytmu, który wykorzystuje 6-o bajtowy adres karty sieciowej. Zabezpieczona jest wobec tego sytuacja, w której stacje wylosowałyby tą samą chwilę na retransmisję. CSMA/CD 3) Po tym czasie każda ze stacji, która brała udział w kolizji ponawia próbę transmisji. Jeżeli znowu nastąpi kolizja, wówczas przedział czasu, jaki stacja ma odczekać jest równy 2*t. Przy ponownej kolizji czas ten ponownie jest podwajany. 4. Jeżeli 16 razy pod rząd stacja nie będzie w stanie przeprowadzić transmisji z powodu ciągle występujących kolizji, wówczas kolejne próby są zaniechane, a informacja o błędzie przekazywana jest do wyższych warstw modelu ISO/OSI i ostatecznie dociera do użytkownika. Warstwa sieciowa (network layer) Dostarcza środków do ustanawiania, utrzymania i rozłączania połączeń sieciowych miedzy systemami otwartymi, w których rezydują komunikujące się aplikacje, i odpowiada, za obsługę błędów komunikacji. Ponadto warstwa sieciowa jest odpowiedzialna za funkcje routingu, który wyznacza optymalną pod względem liczby połączeń drogę przesyłania pakietu przez sieć. TCP (Transsmition Control Protocol) warstwa transpotrowa • Ustala połączenie pomiędzy dwoma komputerami mającymi wymieniać dane (połączenie wirtualne) • Połączenie nie kończy się na interface sieci, sięga aż do warstwy aplikacji (do określonego procesu uruchomionego na komputerze • Każdy komputer tworzy gniazdo (socket), a punkty końcowe połączenia dołączane są do tegoż gniazda. Każde gniazdo posiada adres nazwany (numer portu) TCP komunikacja • Dane są przesyłane w segmentach TCP (ramkach z 20bajtowym nagłówkiem) • Protokół zapewnia niezawodność transmisji (kontroluje dostarczenie pakietu (potwierdzenia), oraz jego poprawność (suma kontrolna) • Pakiet jest dostarczany wyłącznie do adresata • W pierwszej fazie żądanie połączenia musi być wysłane przez nadawcę a uzyskanie połączenia potwierdzone przez odbiorcę UDP (User Datagram Protocol) warstwa transpotrowa Protokół bezpołączeniowy, więc nie ma narzutu na nawiązywanie połączenia i śledzenie sesji. Brak mechanizmów kontroli przepływu i retransmisji. Korzyści: większa szybkość transmisji danych i brak dodatkowych zadań, którymi musi zajmować się host posługujący się tym protokołem. Z tych względów UDP jest często używany w takich zastosowaniach jak wideokonferencje, strumienie dźwięku w Internecie i gry sieciowe, gdzie dane muszą być przesyłane możliwie szybko, a poprawianiem błędów zajmują się inne warstwy modelu OSI. Przykładem może być VoIP lub protokół DNS. UDP udostępnia mechanizm identyfikacji różnych punktów końcowych (np. pracujących aplikacji, usług czy serwisów) na jednym hoście dzięki portom. UDP zajmuje się dostarczaniem pojedynczych INTER-NET • połączenie wielu sieci między sobą • Musi istnieć sposób komunikacji miedzy różnymi standardami sieci • Należy określić ścieżkę przesyłania danych IP (Internet Protocol) • Dane z datagramów sieci lokalnej są wyłuskiwane i umieszczane w ramkach standardu IP • IP pozwala na określenie adresu hosta w innej sieci • Protokół zapewnia efektywne przesyłanie pakietów, nie pozwala jednak na kontrolę ich dostarczenia • Określenie trasy przesyłania datagramu oraz translacja pakietu IP z i na pakiet protokołu wykorzystywanego w sieci lokalnej spoczywa na Routerach (traserach) • Komputery w sieci lokalnej mogą być adresowane na podstawie adresu MAC (Medium Acces Control) karty sieciowej • Do adresowania komputerów w środowisku między sieciowym stosuje się adres IP (IPv4 przedstawiony obok) • Do poszerzenia przestrzeni adresowej stosuje się CIDR(Classes Interdomain Routing) lub IPv6 (z 16bajtowym adresem) • Można również wykorzystywać nazwy domenowe (w sieci musi istnieć server DNS (Domain Name Server) tłumaczący nazwę na adres IP komputera Adresowanie IP v6 - adres składa się z 128b liczby (zazwyczaj zapisuje się w postaci 8x 16b wartości - bloki złożone z 0 można pominąć zastępijąc je :: Przykłady: 2001:0db8:0000:0000:0000:0000:1428:57ab 2001:0db8:0:0:0:0:1428:57ab 2001:0db8:0:0::1428:57ab 2001:0db8::1428:57ab 2001:db8::1428:57ab IPv6 typy adresów W adresacji stosuje się trzy typy adresów: adresy unicast – identyfikujące pojedynczy interfejs; pakiety, które są kierowane na ten typ adresu dostarczane są tylko do odbiorcy adresy multicast – identyfikujące grupę interfejsów (mogą one należeć do różnych węzłów), pakiety wysyłane na ten adres dostarczane są do wszystkich członków grupy adresy anycast – podobnie jak adresy multicast, identyfikują one grupę interfejsów, jednak pakiet wysyłany na ten adres dostarczany jest tylko do najbliższego węzła (węzeł ten jest wyznaczany przez protokół rutingu) Zakresy adresów adresy lokalne dla łącza (link-local address) – są to adresy wykorzystywane tylko do komunikacji w jednym segmencie sieci lokalnej lub przy połączeniu typu point-to-point. Rutery nie przekazują pakietów z tego rodzaju adresem. Z puli pozostałych adresów wyróżniane są przez prefiks FE80::/10. Każdy interfejs musi mieć przydzielony co najmniej jeden adres lokalny dla łącza, nawet jeżeli posiada adres globalny lub unikatowy adres lokalny. unikatowe adresy lokalne (unique local adress) – są to adresy będące odpowiednikami adresów prywatnych wykorzystywanych w protokole IPv4. Z puli pozostałych adresów wyróżniane są przez prefiks FC00::/7. Od adresów lokalnych łącza odróżnia je także prefiks rutingu. adresy globalne (global unicast address) – widoczne w całym internecie, są odpowiednikami adresów publicznych stosowanych w IPv4; wszystkie pozostałe Adresy specjalne IPv6 ::/128 – adres nieokreślony (zawierający same zera). ::1/128 – loopback, adres wskazujący na host lokalny. ::/96 – pula zarezerwowana dla zachowania kompatybilności z protokołem IPv4 (pierwsze 96 bitów stanowią 0, pozostają 32 bity na adresy w formacie IPv4). ::ffff:0:0/64 – jw., ale pozwala wykorzystywać komunikację według protokołu IPv6 w sieci IPv4. 2001:7f8::/32 – pula zarezerwowana dla punktów wymiany ruchu, każdy z nich dostaje jedną podsieć /48. 2001:db8::/32 – pula wykorzystywana w przykładach i dokumentacji – nigdy nie będzie wykorzystywana produkcyjnie. 2002::/24 – adresy typu IPv6 do IPv4. Są to adresy wygenerowane na podstawie istniejących, publicznych adresów IPv4, dostępne dla każdego użytkownika. Autokonfiguracja IPv6 Dla podsieci będących LAN-em przydzielana jest pula adresów z maską /64, co umożliwia tworzenie unikatowych adresów lokalnych dla łącza w oparciu o numery sprzętowe MAC (IEEE 802). Adres taki (dla adresu MAC 10:22:33:44:55:66) będzie miał postać: 64bitowy_prefiks_sieci:1222:33FF:FE44:5566 Routing pakietów TCP/IP • Stanowi połączenie dwóch protokołów • Umożliwia efektywne przesyłanie pakietów między sieciami (IP) oraz zapewnia kontrolę i niezawodność przesyłanie pakietów między dwoma komputerami (TCP) • Protokół ten działa również w sieciach lokalnych w których komputery posiadają adresy IP. • Mechanizm ten można również wykorzystywać do komunikacji między aplikacjami uruchomionymi na pojedynczym komputerze z wykorzystaniem adresu loop back 127.0.0.1 (localhost) TCP/IP w modelu OSI - połączenie można ustanowić pomiędzy 2 dowolnymi hostami, - połączenie jest tworzone pomiędzy dwoma aplikacjami z których jedna pełni rolę serwera (nasłuchuje i przyjmuje połączenie) a drugi klienta (inicjującego połączenie) - serwer musi mieć publiczny adres IP lub musi znajdować się w tej samej sieci LAN co klient - serwer prowadzi nasłuch (czeka na zgłoszenia) na przypisanym mu porcie Brak gniazd Wykorzystanie gniazd Gniazda – usługi serwera • Standardowo stosuje się gniazda (odpowiadające numerom telefonów) w celu realizacji pewnych usług. Gniazda są identyfikowane przez ich numer, aplikacja klienta może połączyć się z określonym gniazdem serwera 80 – httpd, 21 – ftp, powyżej numeru 1024 znajdują się gniazda z których mogą korzystać inne aplikacje • Połączenie z gniazdem można uzyskać przez wpisanie adresu IP i nr gniazda w postaci xxx.xxx.xxx:gniazdo Protokoły Http: - port 80 Ftp: - port 21 Ssh: - port 22 Smtp: - port 25 Pop3: - port 110 Bezpieczeństwo Pakiety danych mogą być przechwycone w trakcie ich transmisji, co może skutkować odczytaniem poufnych informacji Szyfrowanie polega na zakodowaniu danych po stronie klienta i rozkodowaniu ich po stronie odbiorcy. Do kodowania wykorzystuje się protokół TSL/SSL oparty o klucz publiczny i prywatny Klucz prywatny i publiczny (RSA) Tworzenie klucza: 1. Wybieramy losowo dwie duże liczby pierwsze p i q (najlepiej w taki sposób, aby obie miały zbliżoną długość w bitach, ale jednocześnie były od siebie odległe wartościami - maksymalizuje to bezpieczeństwo klucza) 2. Obliczamy wartość n = pq 3. Obliczamy wartość funkcji Eulera dla n: φ(n)=(p-1)(q-1) 4. Wybieramy liczbę e (1 < e < φ(n)) względnie pierwszą z φ(n) 5. Znajdujemy liczbę d odwrotną do e mod φ(n); TSL/SSL TLS (ang. Transport Layer Security) – rozwinięcie protokołu SSL (ang. Secure Socket Layer), zaprojektowanego pierwotnie przez Netscape Communications Corporation. TLS zapewnia poufność i integralność transmisji danych, a także uwierzytelnienie serwera, a niekiedy również klienta. Serwer wyposażony jest w certyfikat (CA) pozwalający na jego jednoznacze zidentyfikowanie. Przy nawiązywaniu połączenia klient otrzymuje klucz publiczny do szyfrowania danych sesji. Forwarding portów Adresy w sieci LAN zazwyczaj pochodzą z przestrzeni adresowej przypisanej sieciom prywatnym (komputery nie mają publicznych adresów IP. Możliwe jest jednak przekierowanie ruchu z pewnego portu na IP publicznym na pewien określony port komputera w sieci LAN. Dzięki temu można zadedykować do konkretnej usługi osobny komputer, bez konieczności zwiększania puli publicznych Publiczne IP Prywatne IP np. 172.16.0.2 Wirtualne sieci prywatne VPN Udostępnienie na zewnątz wszystkich zasobów sieci lokalnej byłoby dużym zagrożeniem bezpieczeństwa Z drugiej stony udostępnienie tylko niektórych usług (przez forwardowanie portów) jest również luką bezpieczeństwa I nie umożliwia korzystanie z wszystkich zasobów sieci. Sieć wirtualna tworzy szyfrowany tunel przez który komputer znajdujacy się poza siecią LAN może mieć dostę do wszystkich zasobów tej sieci (otrzymuje adres IP w sieci lokalnej) Architektura Klient-Serwer • Architektura ta dotyczy warstwy aplikacji • Wszystkie zasoby zgromadzone są na serwerze i są udostępniane przez niego komputerom klientom wyłącznie w takim zakresie w jakim ich potrzebują, • Architektura ta umożliwia ograniczenie dostępu do pewnych zasobów, hermetyzację • W architekturze tej można stosować wolne komputery klientów z małą pamięcią dyskową, ponieważ większość zadań wykonywana jest w obrębie serwera, zaś komputer klienta służy wyłącznie do wyświetlania wyniku operacji • Komunikacja między serwerem a klientami może odbywać się z wykorzystaniem protokołu TCP/IP, lecz wymaga sprecyzowania interface’u komunikacji Algorytm komunikacji • Serwer oczekuje na żądania klientów i obsługuje je w określonej kolejności, sam jednak nie inicjuje połączenia • Należy ustalić (jeśli nie korzysta się ze standardowego protokołu (np. http lub ftp) w jaki sposób przekazywać żądania do servera • Należy ustalić jakie zadania leżą w kompetencji servera a jakie klienta • W celu aktualizacji swoich danych to aplikacja klienta musi zgłaszać odpowiednie żądanie Interface komunikacji • Żądanie przesyłane do serwera, wywołuje w aplikacji działającej na serwerze odpowiednią funkcję, której argumentami stają się dane przesłane przez klienta • Należy zdefiniować wszystkie możliwe typy żądań oraz dane przesyłane przy każdym z tych żądań i odpowiedzi serwera i umieścić je w dwóch klasach interface’u osobno dla klienta i dla serwera Przykładowa klasa interface’u Pomiędzy klientem i serwerem przesyłane są dane opisane strukturą: struct CSFRAME{ int ID; long DataSize void *Data }; Strona klienta Żądania: ID Opis 1 PrześlijAktualneDane 2 SprawdzDane dane NULL dane do sprawdzenia (np. Wykonany ruch) Strona serwera Odpowiedzi servera ID opis 1 AkualneDane 2 Błąd dane Aktualne dane (np. plansza) kod błędu Interface - implementacja • Po stronie serwera i klienta powinny znajdować się dwie klasy które realizują komunikację • Powinny one posiadać metody nazwane tak jak żądania klienta (przesłanie żądania klienta wywołuje odpowiednią funkcję na serwerze) Strona klienta class CClientInterface{ public: CPlansza* PrzeslijAktualneDane(); int SprawdzDane(CPlansza* plansza) private: CSFRAME KomunikatOdebrany; CSFRAME KomunikatWysłany; }; Strona serwera class CServerInterface{ public: void OdbierzŻądanie(); CPlansza* PrzeslijAktualneDane(); int SprawdzDane(CPlansza* plansza private: CSFRAME KomunikatOdebrany; CSFRAME KomunikatWysłany; }; Interface – implementacja cd. Strona klienta CPlansza* CClientInterface::PrzeslijAktualneDane() { KomunikatWyslany.ID=1; KomunikatWyslany.DataSize=0; KomunikatWyslany.Data=NULL; WyslijDoServera(KomunikatWyslany); OdbierzZSerwera(&KomunikatOdebrany); if(KomunikatOdebrany.ID==1) return KomunikatOdebrany. Data; else return NULL; } int CClientInterface:: SprawdzDane(CPlansza* plansza) { KomunikatWyslany.ID=2; KomunikatWyslany.DataSize=sizeof(CPlansza); KomunikatWyslany.Data=plansza; WyslijDoServera(KomunikatWyslany); OdbierzZSerwera(&KomunikatOdebrany); return (int*) KomunikatOdebrany. Data; } Strona serwera CPlansza* CServerInterface:: OdbierzŻądanie() { CPlansza *plansza; int KodBladu; CzekajNaDaneOdKlienta(&KomunikatOdebrany); switch(KomunikatOdebrany.ID){ case 1: plansza=PrzeslijAktualneDane(); KomunikatWyslany.ID=1; KomunikatWyslany.DataSize=sizeof(CPlansza); KomunikatWyslany.Data=plansza; case 2: KodBladu= SprawdzDane(KomunikatOdebrany.Data) KomunikatWyslany.ID=2; KomunikatWyslany.DataSize=sizeof(int); KomunikatWyslany.Data=&KodBladu; } WyslijDoKlienta(KomunikatWyslany); } Inicjalizacja WinSock (klient/server) // Initialize Winsock. WSADATA wsaData; int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); if ( iResult != NO_ERROR ) printf("Error at WSAStartup()\n"); Ten kod musi zostać wywołany jednorazowo przy uruchomieniu aplikazcji wykorzystującej bibliotekę WinSock, dodatkowo należy dołączyć plik nagłowkowy #include "winsock2.h" i bibliotekę “WS2_32.lib” Tworzenie nienazwanego gniazda (klient/server) // Create a socket. SOCKET m_socket; m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( m_socket == INVALID_SOCKET ) { printf( "Error at socket(): %ld\n", WSAGetLastError() ); WSACleanup(); return; } Port stworzony przez klienta otrzymuje numer w chwili połączenia z serwerem. Numer portu na którym serwer nasłuchuje musi zostać przydzielony w następnym kroku Połączenie z nazwanym portem (serwer) // Bind the socket. sockaddr_in service; service.sin_family = AF_INET; Inet_addr(„IP”) przekształca ciąg znaków zwierających adres IP komputera do właściwej postaci (IN_ADDR) service.sin_addr.s_addr = inet_addr( "127.0.0.1" ); Do przypisania adresu można service.sin_port = htons( 27015 ); if ( bind( m_socket, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) { printf( "bind() failed.\n" ); closesocket(m_socket); return; } rwnież wykorzystać nazwę hosta (jeśli jest zarejestrowana w DNS: gethostbyname( „www.w p.pl”) htons konwertuje numer portu dany jako unsigned short na numer w formacie standardu TCP/IP (big-endian) Po wywołaniu funkcji bind na serwerze zostaje aktywowany port o wybranym numerze Serwer oczekuje na żądanie klienta // Listen on the socket. if ( listen( m_socket, 1 ) == SOCKET_ERROR ) printf( "Error listening on socket.\n"); Serwer „nasłuchuje” na wybranym porcie (m_sock)., drugi argument funkcji listen określa długość kolejki zgłoszeń, dla aplikacji z wieloma klientami należy ją ustawić na wartość >1 listen pozostaje zawieszona tak długo jak nie pojawi się żądanie ze strony klienta Połączenie klienta z serwerem (klient) sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" ); clientService.sin_port = htons( 27015 ); if ( connect( m_socket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) { printf( "Failed to connect.\n" ); WSACleanup(); return; } Wywołanie connect powadzi do nawiązania połączenia z serwerem zlokalizowanym pod określonym adresem (clientService) i słuchającym na wybranym porcie (27015). Serwer dla celu tego połączenia dedykuje odrębny port (jego adres przypisany zostaje do m_sock). Port nasłuchu nie służy do komunikacji z klientami a jedynie do nawiązania połączenia!!! Akceptacja połączenia przychodzącego (serwer) // Accept connections. SOCKET AcceptSocket; printf( "Waiting for a client to connect...\n" ); while (1) { AcceptSocket = SOCKET_ERROR; while ( AcceptSocket == SOCKET_ERROR ) { AcceptSocket = accept( m_socket, NULL, NULL ); } printf( "Client Connected.\n"); break; } Po tym jak funkcja listen została przerwana przez żądanie ze strony klienta, accept aprobuje połączenie i dedykuje dla niego nowy port (AcceptSocket) Wysyłanie i odbiór danych (klient/server) Odbiór danych: #define BUFF_LEN 32 int bytesRecv = SOCKET_ERROR; char recvbuf[BUFF_LEN] = ""; //bufor danych //Odbiera dane i zapisuje je do bufora. //Zwraca ilość odebranych bajtów (bytesRecv) bytesRecv = recv( m_socket, recvbuf, BUFF_LEN, 0 ); Wysyłanie danych: int bytesSent; char sendbuf[] = „ten tekst zostanie wyslany” bytesSent = send( m_socket, sendbuf, strlen(sendbuf), 0 ); Dla serwera należy zamiast m_sock wykorzystać port stworzony do komunikacji z klientem AcceptSocket !!!! Komunikacja raz jeszcze WSAStartup(...) socket(...) WSAStartup(...) socket(...) bind(...) listen(...) connect(...) accept(...) send(...) recv(...) recv(...) send(...) Czarne strzałki wskazują miejsca, w których powinny znaleźć się poszczególne funkcje wykorzystywane do komunikacji Czerwone strzałki pokazują przepływ danych między klientem a serwerem Obsługa wielu klientów w architekturze klient-serwer Opis problemu: • Dostarczenie wielu użytkownikom tych samych danych (synchronizacja) • Równoczesna szybka obsługa wielu zgłoszeń Możliwe rozwiązania: • Multicasting lub broadcasting - serwer wsyła dane na adres sieciowy, który jest traktowany jako adres wszystkich komputerów • Klienci co pewien określony czas żądają od serwera aktualizacji danych (serwer obsługuje każdego z klientów osobno) Zwiekszenie efektywności obsługi klientów Dotychczasowy schemat (serwer) • Serwer tworzy nazwane gniazdo 'GniazdoServera' za pomocą funkcji bind • Serwer tworzy kolejkę zgłoszeń za pomocą funkcji listen • Serwer oczekuje (nasłuchuje) na porcie 'GanizdoServera' na zgłoszenie się klienta i po jego zgłoszeniu tworzy za pomocą funkcji accept noew gniazdo (m_socket) dedykowane do komunikacji z tymże klientem • Po przeprowadzeniu pojedynczej transakcji gniazdo klienta jest zamykane a serwer wraca do pkt. nr 3 Zwiekszenie efektywności obsługi klientów Dotychczasowy schemat (klient) • Klient tworzy gniazdo nienzawane (socket) • Klient łączy się z serverem (connect) • Klient wysyła / odbiera dane z serwera • Klient zamyka gniazdo. Ten schemat jest realizowany za każdym razem kiedy klient chce wysłać/odebrać dane z serwera Zwiekszenie efektywności obsługi klientów Nowy schemat (serwer) • Serwer tworzy nazwane gniazdo 'GniazdoServera' za pomocą funkcji bind • Serwer tworzy kolejkę zgłoszeń za pomocą funkcji listen • Serwer tworzy tablicę deskryptorów gniazd i dodaje do niego 'GniazdoServera' • serwer przegląda tablicę deskryptorów gniazd i wybiera z niej gniazdo na którym pojawiła się aktywność za pomocą funkcji select • Jeśli aktywność pojawiła się na gnieździe 'GniazdoSerwera' oznacza to że zgłosił się nowy klient. Wtedy serwer za pomocą funkcji accept tworzy nowe gniazdo dedykowane do komunikacji z tym klientem i umieszcza je w tablicy deskryptorów • serwer odbiera/przesyła dane przez gniazdo klienta i wraca do pkt 4. Zwiekszenie efektywności obsługi klientów Nowy schemat (klient) • Klient tworzy gniazdo nienzawane (socket) • Klient łączy się z serverem (connect) • Klient wysyła / odbiera dane z serwera • Aby ponownie wysłać dane klient realizuje pkt 3. Punkty 1-2 realizowane sa jednorazowo, a stworzone gniazdo może być wykorzystywane aż do czasu zakończenia połączenia przez closesocket(.) Zwiekszenie efektywności obsługi klientów – kod serwera //W kodze pominęto etap tworzenia gniazda nazwanego fd_set TablicaGniazd, TablicaGniazdTest; sockaddr_in sinRemote; int nAddrSize = sizeof(sinRemote); listen(GniazdoSerwera,5); //tworzy kolejkę FD_ZERO(&TablicaGniazd); //czysci tablice gniazd FD_SET(GniazdoSerwera,&TablicaGniazd); //dodaje gniazdo serwera do tablicy gniazd while(1){ //nieskonczona pęta serwera TablicaGniazdTest=TablicaGniazd; if (select(0, &TablicaGniazdTest, (fd_set*)NULL, (fd_set*)NULL, 0) > 0) { //// Cos sie stalo na jednym z gniazd. Zwiekszenie efektywności obsługi klientów – kod serwera if (FD_ISSET(GniazdoSerwera, &TablicaGniazdTest)) { //nowe połączenie SOCKET sd = accept(GniazdoSerwera, (sockaddr*)&sinRemote, &nAddrSize); if (sd != INVALID_SOCKET) { FD_SET(sd,&TablicaGniazd); //dodanie gniazda do tablicy // Wyswietlenie adnych klienta cout << "Accepted connection from " << inet_ntoa(sinRemote.sin_addr) << ":" << ntohs(sinRemote.sin_port) << ", socket " << sd << "." << endl; else { cout<<”BLAD”; return; }} Zwiekszenie efektywności obsługi klientów – kod serwera else{ //poszukiwanie gniazda na którym wystąpił ruch for(int i=0;i<TablicaGniazdTest.fd_count;i++) if(FD_ISSET(TablicaGniazdTest.fd_array[i],&TablicaGniazdTest) { SOCKET m_socket=TablicaGniazdTest.fd_array[i]; int odczytano=0; ioctlsocket(m_socket,FIONREAD,&odczytano); if(odczytano==0) { closesocket(m_socket); FD_CLR(m_socket,TablicaGniazd); } } Zwiekszenie efektywności obsługi klientów – kod serwera if(odczytano==0) //połączenie zamkniete { closesocket(m_socket); FD_CLR(m_socket,TablicaGniazd); } else { recv(m_socket,.....) send(m_socket,.....) } }//powrot do początku petli while(1) class Cklient { char IP[16]; int port; sRamka S; sRamka R; SOCKET m_socket; public: Cklient(); Cklient(char *_IP, int _port); void SetIP(char *_IP, int _port); void Connect(); void Send(sRamka *_S); void Receive(sRamka *_R); }; Przykładowa klasa klienta Wykorzystanie klasy { Cklient klient(“128.1.1.100”,2018); klient.Connect(); Sramka R; R.ID=1; Klient.Send(&R); Klient.Receive(&R); }