Számítógépes hálózatok HETEDIK ELŐADÁS SOCKET programozás C/C++ nyelven AZ ELŐADÁS DIÁKAT KÉSZÍTETTE: ÁCS ZOLTÁN
Hálózati programozás bevezetés Általában egy klasszikus hálózati alkalmazás két fő részre bontható: egy kliens oldali alkalmazás illetve egy szerver oldali alkalmazás. Mi különbözteti meg a két alkalmazás fajtát? Milyen példa kliens programokat ismerünk? Milyen példa szerver programokat ismerünk? 2
Socket programozás bevezetés A socket a számítógépes hálózatokkal kapcsolatos alapvető technológiák egyike. A socket-ek lehetővé teszik az alkalmazások részére a kommunikációt az operációsrendszerbe illetve a hálózati adapterbe épített standard mechanizmusok felhasználásán keresztül. A kommunikáció jellegét tekintve két egyszerűsített modell különböztethető meg: 1. Összeköttetés-nélküli modell (angolul connectionless model) 2. Összeköttetés-orientált modell (angolul connection-orientated model) 3
Áttekintés és alapok
Memória Hálózati adatábrázolás A bájt sorrend eltérő lehet a különböző hardverek esetén Big Endian. Little Endian. A hálózati bájt sorrend a Big Endian. a: 0D a+1: 0C a+2: 0B a+3: 0A Little endian 0A 0B 0C 0D 32-bites egész Big endian a: 0A a+1: 0B a+2: 0C a+3: 0D Memória 5
Socket könyvtár C/C++ nyelven Mind Linux, Windows operációs rendszeren használható a socket, de vannak apróbb eltérések. Legfontosabb Linux állományok: netdb.h Hálózati adatbázis műveleteket definícióit tartalmazza. sys/types.h Adattípusok definícióit tartalmazza. sys/socket.h A főbb SOCKET fejléceket tartalmazza. arpa/inet.h Internet műveletek definícióit tartalmazza. 6
Socket könyvtár C/C++ nyelven Windows-os környezetben egy picit eltér a socket könyvtár a Linux-os társától. Legfontosabb Windows specifikus állományok: winsock2.h A legtöbb socket funkciót, struktúrát és definíciót tartalmazza. ws2tcpip.h Újabb funkciókat és definíciókat tartalmaz az IP cím meghatározására. Az előadás hátralévő részében a Linux-os környezetre koncentrálunk. 7
Összeköttetés-nélküli modell A kliens és a szerver nem épít fel és menedzsel külön kapcsolatot a kommunikációhoz. Kommunikáció datagram alapon megy. Nincs garancia, hogy a másik fél megkapja az üzenetet. Milyen ilyen szolgáltatásokat tudna felsorolni, amelyek ilyen átviteli formát használnak? 8
Összeköttetés-nélküli modell SZERVER OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Adatok fogadásának megkezdése (recvfrom() hívás ) KLIENS OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Kérés küldése a szervernek (sendto() hívás ) Válasz küldése a kérésre (sendto() hívás ) Válasz küldése a kérésre (recvfrom() hívás ) 9
Összeköttetés-orientált modell A kliens és a szerver felépít és menedzsel külön kapcsolatot a kommunikációhoz. Kommunikáció stream alapon megy. Hiba jelzések. Milyen ilyen szolgáltatásokat tudna felsorolni, amelyek ilyen átviteli formát használnak? 10
Összeköttetés-orientált modell SZERVER OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Bejövő kérések figyelésének megkezdése (listen() hívás ) Bejövő csatlakozási kérés kiszolgálása (accept() hívás ) Adatfogadása a csatlakozott klienstől (recv() hívás ) Adatküldése a csatlakozott kliensnek (send() hívás ) KLIENS OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Csatlakozás a távoli szerverhez (connect() hívás ) Kérés küldése a szervernek (send () hívás ) 11
Könyvtári funkciók és típusok
Címzés Hogyan lehet egy távoli alkalmazást elérni? Címzés kell, amelynek két komponense van: 1. Alkalmazás OS-en belüli címe (szolgáltatás port). 2. Alkalmazás hálózati címe. (IP cím) Protokoll megadása Helyi cím adatok (IP,port) Kapcsolat-orientált szerver socket() bind() accept() Kapcsolat-orientált kliens socket() connect() Távoli cím adatok (IP,port) Kapcsolat nélküli szerver socket() bind() recvfrom()/sendto() Kapcsolat nélküli kliens socket() bind() recvfrom()/sendto() 13
Socket létrehozása Hívás szintaxis: socket(address_family, type_of_communication, protocol_specification) Hiba mentes futás esetén visszatérési értéke egy fájlleíró, amely az új socket-re hivatkozik. Hiba esetén visszatérési értéke -1. Alapértelmezés szerint a létrejövő socket blokkolja vezérlési szálat IO műveletek esetén. (O_NONBLOCK) Kapcsolódó eljárások: getsockname( ) és setsockopt( ) 14
Socket helyi címének megadása Az alkalmazást egy 2 bájtos úgynevezett port cím azonosítja. Vannak standard portok, amelyek különféle szolgáltatásokhoz tartozik: Web szerver (80, 443 és 8080), ssh szolgáltatás (22), FTP szolgáltatás (21), stb. Hívás szintaxis: bind(socket_descriptor, address, length_of_address) Hiba mentes futás esetén a socket címezhetővé válik az OS-n belül. Hiba esetén visszatérési értéke -1. 15
Bejövő hívások kezelése Kapcsolat-orientált szervernél az alkalmazást képessé kell tenni a bejövő kérések kezelésére, ez lesz a listen(..) rendszerhívás feladata. Hívás szintaxis: listen(socket_descriptor, queue_length) Az elfogadott kapcsolatok szeparált socket-et kapnak a kommunikációhoz. Hívás szintaxis: accept(server_socket_descriptor, peer_address,peer_address_length) A visszatérési érték sikeres hívás esetén egy socket leíró lesz, egyébként -1. A hívás alapértelmezetten blokkolja a végrehajtási szálat egy beérkező kérés megérkezéséig. 16
Kapcsolódás egy távoli szolgáltatáshoz Kapcsolat-orientált kliensnek kapcsolódni a kell a távoli alkalmazáshoz, ez lesz a connect( ) rendszerhívás feladata. Hívás szintaxis: connect(socket_descriptor, peer_address,peer_address_length) A sikeres végrehajtás esetén a kapcsolat kiépül, ezért a socket-en a címzés megadása nélkül lehet adatot küldeni illetve fogadni. A hívás alapértelmezés szerint blokkolja a végrehajtási szálat a kapcsolódás idejére. 17
Adatok fogadása Adatok fogadására két lehetőség van: recv( ) és recvfrom() Hívás szintaxis: recv(socket_descriptor, message_buffer, maximum_length_of_message, receive_options) A sikeresen fogadott bájtok számával tér vissza; hiba esetén -1-gyel tér vissza. Hívás szintaxis: recvfrom(socket_descriptor, message_buffer, maximum_length_of_message, receive_options, partner_address,partner_address_length) A sikeresen fogadott bájtok számával tér vissza; hiba esetén -1-gyel tér vissza. 18
Adatok küldése Adatok küldésére két lehetőség van: send( ) és sendto() Hívás szintaxis: send(socket_descriptor, message, length_of_message, send_options) A sikeresen elküldött bájtok számával tér vissza; hiba esetén -1-gyel tér vissza. Hívás szintaxis: sendto(socket_descriptor, message, length_of_message, send_options, partner_address,peer_address_length) A sikeresen elküldött bájtok számával tér vissza; hiba esetén -1-gyel tér vissza. 19
Kapcsolat normál lezárása A socket rendszer erőforrásokat foglal le, ezért illik lezárni az alkalmazás végén. Két lehetőség van a kommunikáció befejezésére: shutdown és close. Hívás szintaxis: shutdown(socket, how) A sikeres végrehajtás esetén 0-ával tér vissza; egyébként -1-gyel. Hívás szintaxis: close(socket) A sikeres végrehajtás esetén 0-ával tér vissza; egyébként -1-gyel. Normál esetben a close( ) még a lezárás előtt megpróbálja befejezni az átvitelt. 20
UDP mintaprogram
Példa UDP kapcsolat Írjon olyan UDP alapon kommunikáló parancssori kliens alkalmazást, amely a szerver alkalmazástól lekéri egy ASCII karakter kódjának megfelelő modulációs szabályt Manchester vagy DifferentialManchester szabálynak megfelelően. A szerver az egy óraütemet két karakterrel jellemez. Például HL-lel, ami azt jelenti, hogy az ütem első felében magas jelszint kell a második felében pedig alacsony. A hét óraütemet vesszőkkel elválasztva adja vissza a szerver, azaz például az LH,HL,LH,LH,LH,LH,LH egy lehetséges válasz. A kliens alkalmazás tegye lehetővé, hogy több lekérdezést is kezdeményezhessen a felhasználó egy futás alatt. Azaz például a standard inputról olvassuk be a kéréseit valamilyen leállási feltételig. 22
Példa UDP szerver 23
Példa UDP szerver SZERVER OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Adatok fogadásának megkezdése (recvfrom() hívás ) Válasz küldése a kérésre (sendto() hívás ) 24
Példa UDP szerver SZERVER OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Adatok fogadásának megkezdése (recvfrom() hívás ) Válasz küldése a kérésre (sendto() hívás ) 25
Példa UDP szerver SZERVER OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Adatok fogadásának megkezdése (recvfrom() hívás ) Válasz küldése a kérésre (sendto() hívás ) 26
Példa UDP szerver SZERVER OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Adatok fogadásának megkezdése (recvfrom() hívás ) Válasz küldése a kérésre (sendto() hívás ) 27
Példa UDP kliens 28
Példa UDP kliens KLIENS OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Kérés küldése a szervernek (sendto() hívás ) Válasz küldése a kérésre (recvfrom() hívás ) 29
Példa UDP kliens KLIENS OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Kérés küldése a szervernek (sendto() hívás ) Válasz küldése a kérésre (recvfrom() hívás ) 30
Példa UDP kliens KLIENS OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Kérés küldése a szervernek (sendto() hívás ) Válasz küldése a kérésre (recvfrom() hívás ) 31
Példa UDP kliens KLIENS OLDALI ALKALMAZÁS A datagram socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Kérés küldése a szervernek (sendto() hívás ) Válasz küldése a kérésre (recvfrom() hívás ) 32
TCP mintaprogram
Példa TCP kapcsolat Írjon olyan TCP alapon kommunikáló hálózati alkalmazásokat, amelyben a kliens alkalmazás egy szerver alkalmazástól lekérheti különböző domének, hálózati helyek IPv4-es címét. Azaz a szerver, mint egy névfeloldó szolgáltatás működik megbízható átvitel felhasználásával. 34
Példa TCP szerver 35
Példa TCP szerver SZERVER OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Bejövő kérések figyelésének megkezdése (listen() hívás ) Bejövő csatlakozási kérés kiszolgálása (accept() hívás ) Adatfogadása a csatlakozott klienstől (recv() hívás ) Adatküldése a csatlakozott kliensnek (send() hívás ) 36
Példa TCP szerver SZERVER OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Bejövő kérések figyelésének megkezdése (listen() hívás ) Bejövő csatlakozási kérés kiszolgálása (accept() hívás ) Adatfogadása a csatlakozott klienstől (recv() hívás ) Adatküldése a csatlakozott kliensnek (send() hívás ) 37
Példa TCP szerver SZERVER OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Bejövő kérések figyelésének megkezdése (listen() hívás ) Bejövő csatlakozási kérés kiszolgálása (accept() hívás ) Adatfogadása a csatlakozott klienstől (recv() hívás ) Adatküldése a csatlakozott kliensnek (send() hívás ) 38
Példa TCP szerver SZERVER OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Bejövő kérések figyelésének megkezdése (listen() hívás ) Bejövő csatlakozási kérés kiszolgálása (accept() hívás ) Adatfogadása a csatlakozott klienstől (recv() hívás ) Adatküldése a csatlakozott kliensnek (send() hívás ) 39
Példa TCP szerver SZERVER OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Bejövő kérések figyelésének megkezdése (listen() hívás ) Bejövő csatlakozási kérés kiszolgálása (accept() hívás ) Adatfogadása a csatlakozott klienstől (recv() hívás ) Adatküldése a csatlakozott kliensnek (send() hívás ) 40
Példa TCP szerver SZERVER OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Helyi címzés az alkalmazás lokális azonosítására (bind() hívás ) Bejövő kérések figyelésének megkezdése (listen() hívás ) Bejövő csatlakozási kérés kiszolgálása (accept() hívás ) Adatfogadása a csatlakozott klienstől (recv() hívás ) Adatküldése a csatlakozott kliensnek (send() hívás ) 41
Példa TCP kliens 42
Példa TCP kliens KLIENS OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Csatlakozás a távoli szerverhez (connect() hívás ) Kérés küldése a szervernek (send () hívás ) 43
Példa TCP kliens KLIENS OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Csatlakozás a távoli szerverhez (connect() hívás ) Kérés küldése a szervernek (send () hívás ) 44
Példa TCP kliens KLIENS OLDALI ALKALMAZÁS A stream socket létrehozása (socket() hívás ) Csatlakozás a távoli szerverhez (connect() hívás ) Kérés küldése a szervernek (send () hívás ) 45
SELECT rendszerhívás C++
IO blokkolás kezelése Mivel a socket egy fájlhoz hasonlítható bizonyos hívások blokkolják a végrehajtási szálat: Adatok fogadása, Bejövő hívások fogadása, Két megoldás van: 1. O_NONBLOCK és fork() használata, 2. select(...) használata. 47
select() rendszerhívás Mire szolgál? Fájlok státuszának felügyeletére szolgál. Szintaxisa informálisan, hogy írható le? select( <ellenőrzés eddig>, <mutató az olvashatósági státusz ellenőrzés socket fájlleíróihoz>, <mutató az írhatósági státusz ellenőrzés socket fájlleíróihoz>, <mutató a hiba státusz ellenőrzés socket fájlleíróihoz>, <mutató várakozás időtartamára> ); Sajátosságok A fájlleírókat egy speciális szerkezetben lehet definiálni, aminek a neve fd_set. Linux esetén bármilyen fájlra alkalmazható. Windows esetén csak socket leírókra alkalmazható. 48
fd_set adatszerkezet Mire szolgál? A socket fájlleírók tárolására. A select() hívás ezeken operál. Milyen műveletei vannak? Nullázás, azaz a beállított elemek kiürítés. void FD_ZERO(fd_set *fdset) Egy elem hozzáadása a tömbhöz. void FD_SET(int fd, fd_set *fdset), Egy elem törlése a tömbből. void FD_CLR(int fd, fd_set *fdset) Egy elem jelenlétének ellenőrzése. int FD_ISSET(int fd, fd_set *fdset) 49
Példa az fd_set és a select() használatára FD_ZERO(&master); FD_ZERO(&slave); FD_SET(serversocket1, &master); FD_SET(serversocket2, &master); index_max=(serversocket1>serversocket2)?serversocket1:serversocket2; slave = master; if(select(index_max+1, &slave, NULL, NULL, NULL) == -1) { } perror("select"); return -1; Bejövő kérés érkezik a serversocket2-n, viszont a másik szerver SOCKET-re nem érkezik semmi A slave listán már csak a serversocket2 lesz benne. 50
Vége Köszönöm a figyelmet!