Számítógép-hálózatok: Labor 2
SOCKET használata – UDP kliens
A gyakorlat célja: Kliens-szerver modell Megismerkedni a SOCKET API alapstrukturáival, működési elveivel UDP kliens megvalósítása (UDP visszhang kliens) Elméleti bevezető:
1. ábra. Kliens-Szerver modell A G1 és G2, G3 hosztok (számítógépek) hálózaton szeretnének kommunkálni. Ahoz, hogy létre jöjjön közöttük a kapcsolat, bizonyos feltételeknek kell teljesülniük. Ilyen feltétel a fizikai kapcsolat létezése, valamint az egymás megértését biztosító protokoll. Abban az esetben, ha a G1 hoszton működő alkalmazás, szolgáltatásokat nyújt a G2, G3 hosztokon futó alkalmazásoknak a G1 hosztott szervernek nevezzük és a G2, G3-as gépeket klienseknek. Abban az esetben, ha a kapcsolatott a kliens gépek kezdeményezik, akkor Kliens-Szerver modellről beszélünk. Protokoll A protokoll egy szabályhalmaz, amely meghatározza, hogy a hosztok miképpen tudnak egymással kommunikálni. Ez többnyire a kapcsolat létrehozására, bontására, adatcserére vonatkozik. Az első alapvető szabály a kommunikáció sikeressége szempontjából egy szabály rendszer létezése, amelyet mind a két fél helyesen használ.
Számítógép-hálózatok: Labor 2 Nezzük meg, hogy mit takar a „szabály rendszer” a hálózati alkalmazás tervezőjének a szemszögéből: 1. Meghatározzuk az alkalmazás célját. Célunk egy viszhang alkalmazás (G2-es kliens gép üzeneteit a G1-es szerver gép visszaküldi változatlanul a küldőnek) kliens oldalának megvalósítása. Üzenet küldése. Üzenet fogadása.
2. A cél megvalósítása érdekében azonósítjuk a megoldandó problémékat. CÍMZÉS - Hol van a szerveralkalmazás? És az üzenetem, hogyan találja meg mindig a szervert? SZINTAKTIKAI EGYÉRTELMÜSÉG (jelen esetben SZÁMÁBRÁZOLÁS) - Hogyan oldom meg, hogy az „üzent” fogalom alatt a szerver is „üzenet”-et értsen és ne más valamit. HIBAJAVÍTÁS – az üzenetek helyesen jussanak el a célgépig és vissza
3. Kivállasztjuk protokoll alapjait. A jelenleg alkalmazott számítógép architektúrának köszönhetően a hálózati alkalmazások tervezésénél eldönthetem, hogy melyik réteg szolgáltatásait veszem igénybe. Egyes rétegeken belül kiválaszthatom azt a protokollt, amelyiknek a szolgáltatásai a legtöbb segítséget nyújtanak a feladataim elvégzéséhez. A réteg és protokoll kivállasztásának előzetes feltétele ezeknek a szolgálatainak feltérképezése ismerete. 4. A cél elérése érdekében meghatározzuk a megvalósítandó tulajonságokat. A tervezés elöző fázisában kiválasztottam a számomra legmegfelelőbb architektúra szintet és protokollt, amelyre a saját protokollomat építem. A tervezésnek ebben a fázisában összehasonlítom az alap protokoll szolgáltatásait és a megoldandó problémákat. Abban az esetben, ha az alap protokoll megoldást kínál a problémára, akkor kihúzhatom a listából. Pl. kiválasztjuk az UDP protokollt amely az IP- protokollra épül és a szállítási rétegben helyezkedik el. Számunkra ez megoldja a CÍMZÉS és a SZÁMÁBRÁZOLÁS problémáját. Amit meg kell valósítsak az a HIBAJAVÍTÁS. Jelen esetben az egyszerűség kedvéért eltekíntönk ettől a követelmenytől. Ebben az esetben mi az UDP protokoll egyszerű felhasználóivá válunk. Csatlakozó (SOCKET) A kommunikációs végpontok azonosítója a csatlakozó. A hozzá tartozó rendszerhívások megfelelnek a szolgáltatás elérési pontoknak. Ezeknek a segítségével kezelhetjük a kommunikáziót pl. kommunikációs csatorna kiépítése, adtaok küldése és fogadása. Címzés Ahoz, hogy egy hálozati kommunikációt fel tudjunk építeni szükséges a hálózatban található hosztotok (pl. számítógépet) egyedi azonósítására. Ezt a célt szolgálja a hálózati architektúra különböző rétegeiben táláható címzési eljárások.
Számítógép-hálózatok: Labor 2 A legalcsonyabb színten, az adatkapcsolati rétegben, található a MAC cím, egy egyedi azonósító amellyel a gyartó látja el a hálozati interfészeket (hálózati kártya, routerek kimeneti portjai). Ez a címzési mechanizmus nem támogatja a gépek gyors keresését egy nagyméretű hálózatban. Ezért igen fontos a hálózati rétegben található címzési mechanizmus. A mi esetünkben az Internet Protokoll (IP) által használt címzés, amely transzparensen (felsőbb színten nem kell vele foglalkozni) ráépül a MAC címzési mechanizmusra és biztosítja, hogy hatékonyan megtalálható legyen a címzet. Két szabvanáya van az IP címeknek az IPv4, amely egy 32 bites szám. Ennek a felhasználó barát formálya (Human readeble form) a pontokkal elválasztott négy 1-3-számjegyű szám 0 és 255 között (pl: 123.44.5.321). Ez napjaink legelterjedtebb hálózati szabványa. Az IPv6-os szabvány hivatott felvaltani az IPv4-et, amely egy 128 bites rendszert használ a címzéshez. Fontos megjegyezni hogy egy IP cím nem egy gépet azonósít hanem egy hálózati interfész kártyát. Erre még ráépül egy mechanizmus a szállítási rétegben, amely elosztja a bejövő forgalmat a kiallakított csatlakozások (SOCKET) típusa szerint, és egy 16 bites port (65536 port minden egyes csatlakozók típusra) szerint, amely lehetővé teszi, hogy egyidejüleg több azonos típusú kapcsolatot alakítsak ki egy hosztról más hosztokkal. Számábrázolás Két elterjedt számábrázolás használnak: az Intel gépek által használt a kisebb fontosságú bájt van hátul – little endian, valamint ennek a forditottja a nagyobb fontoságú bájt van hátul – big endian. A hálózati komunikáció a big endián számábrázolást használja és úgy hivatkozik rá, hogy hálózati bájt sorrend (Network order). A tovabbiakban a hoszt által használt számábrázolásra gép bájt sorrendjéként (Host order) hivatkozunk. A socket csomag megvalósításában rendelkezésunkre állnak konverziós függvények amelyek megvalósítják az átalakítást Host order-ből Network order-be és vissza. htonl() - 32 bites mennyiséget a gép bájt sorrendjéből átvált hálózati bájt sorrendbe htons() - 16 bites mennyiséget a gép bájt sorrendjéből átvált hálózati bájt sorrendbe ntohl() - 32 bites mennyiséget hálózati bájt sorrendből átvált a gép bájt sorrendjébe ntohs() - 16 bites mennyiséget hálózati bájt sorrendből átvált a gép bájt sorrendjébe UDP Protokoll föbb tulajdonságai: Egy összekötetés nélküli protokoll, amely nem garanantálja a csomag célba megérkezését. A Winsock API programozása A Winsock API-nak három különböző verziója van: 1.0, 1.1 és Winsock 2. A jelölési konvenciók az 1.1-es verziótól az, hogy a Winsock-hoz tartozó változók, makrók konstansok és függvények WSA előtaggal kezdődnek. A Winsock inicializálása Használat előtt a
Számítógép-hálózatok: Labor 2 int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData); függvényt kell meghívnunk. Az első paraméterben a magasabb helyiértékű byte az igényelt fő verziószám, az alacsonyabb a mellék verzió. Ezt legegyszerűbben a MAKEWORD(x,y) makró alkalmazásával érhetjük el, ahol az x a magas, y az alacsony helyiértékű byte. Így például a MAKEWORD(2,2) számunkra megfelelő paraméter. A második paraméterben visszakapott WSAData struktúra felépítése a következő: typedef struct WSAData { WORD wVersion; WORD wHighVersion; char szDescription[WSADESCRIPTION_LEN+1]; char szSystemStatus[WSASYS_STATUS_LEN+1]; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; } WSADATA, *LPWSADATA; Az első paraméter az a verziószám, amit használni fogunk, a második paraméter a legmagasabb rendelkezésre álló verzió. a többi paraméter egy része verziófüggő, és egyik sem különösebben érdekes számunkra. Socketek létrehozása A socketek számára Windows alatt egy külön típust hoztak létre: ez a SOCKET típus. Socketeket a WSASocket illetve a socket függvénnyel hozhatunk létre. A socket függvény deklarációja: SOCKET socket ( int af, int type, int protocol );
Az első paraméter a címcsalád (address family). UDP és TCP socket számára ez AFX_INET. A második paraméter SOCK_STREAM, SOCK_DGRAM és SOCK_RAW lehet. Az utolsó paraméter a protokoll. A 2.2 táblázat mutatja a címcsalád, a típus és a protokollok közötti összefüggést.
Számítógép-hálózatok: Labor 2 Címcsalád Protokoll megnevezése Internet Protocol Internet Protocol Internet Protocol Internet Protocol
Típus megnevezése
Típus
AF_INET TCP SOCK_STREAM AF_INET UDP SOCK_DGRAM AF_INET Nyers Socket SOCK_RAW AF_INET ICMP SOCK_RAW 1 táblázat: Az Internet Protocol fajtái Win32 alatt
Protokoll
IPPROTO_IP IPPROTO_UDP IPPROTO_RAW IPPROTO_ICMP
Ezek után, a táblázat alapján, könnyen létrehozhatjuk a kívánt fajtájú socketet.
Kommunikáció Winsock alatt Kapcsolat nélküli kommunikáció esetéen a küldésre használjuk a int sendto ( SOCKET s, const char FAR *buf, int len, int flags, const struct sockaddr FAR *to, int tolen );
függvényt, aminél a címzett paramétereit adjuk meg a to struktúrában Az üzenetek fogadásához használjuk int recvfrom ( SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR *from, int FAR *fromlen );
aminek az utolsó két paramétere a figyelő socket címe és annak hossza. struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; };
A sin_family értéke IP esetén AF_INET, a sin_port a TCP port címe, a sin_addr pedig az IP cím.
Végül a program befejezése előtt meg kell hívni a WSACleanup() függvényt.
Számítógép-hálózatok: Labor 2 Feladat: Írjatok egy UDP kliensalkalmazást, amely megérdezi az időszervertől a pontos időt és kiírja a képernyőre. Időszerver protokollja: Ha kap egy csomagot (bármit tartalmazhat a csomag) akkor arra a címre ahonnan a csomag érkezett visszaküldi a pontos időt. Az alkalmazás szerkezete.
1. ábra. UDP Kliensalkalmazás szerkezete
Példaprogram: #include <stdio.h> #include "winsock2.h" void main() { WSADATA wsaData; SOCKET SendSocket; sockaddr_in RecvAddr; int Port = 27015; char SendBuf[1024]; int BufLen = 1024;
Számítógép-hálózatok: Labor 2
//--------------------------------------------// Initialize Winsock WSAStartup(MAKEWORD(2,2), &wsaData); //--------------------------------------------// Create a socket for sending data SendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //--------------------------------------------// Set up the RecvAddr structure with the IP address of // the receiver (in this example case "123.456.789.1") // and the specified port number. RecvAddr.sin_family = AF_INET; RecvAddr.sin_port = htons(Port); RecvAddr.sin_addr.s_addr = inet_addr("123.456.789.1"); //--------------------------------------------// Send a datagram to the receiver printf("Sending a datagram to the receiver...\n"); sendto(SendSocket, SendBuf, BufLen, 0, (SOCKADDR *) &RecvAddr, sizeof(RecvAddr)); //----------------------------------------------// Call the recvfrom function to receive datagrams // on the bound socket. printf("Receiving datagrams...\n"); recvfrom(RecvSocket, RecvBuf, BufLen, 0, (SOCKADDR *)&SenderAddr, &SenderAddrSize); // kiiratas //--------------------------------------------// When the application is finished sending, close the socket. printf("Finished sending. Closing socket.\n"); closesocket(SendSocket); //--------------------------------------------// Clean up and quit. printf("Exiting.\n"); WSACleanup(); return; }
Számítógép-hálózatok: Labor 2 Kérdések: 1. Mi a visszatérítési értéke a sendto-nak? 2. Mikor ter vissza a recvfrom és mi a visszaterítési értéke? Könyvészet: [1]. A. Tanenbaum : Számítógéphálózatok. Bp., Panem Könyvkiadó, 2004. [2]. Buraga, S. - Ciobanu, G.: Atelier de programare în reţele de calculatoare. Iaşi, Polirom, 2001 [3]. http://www.iana.org/assignments/port-numbers [4]. http://msdn.microsoft.com/en-us/library/ms741416(VS.85).aspx