Hálózati architektúrák laborgyakorlat 10. hét Dr. Orosz Péter, Skopkó Tamás 2012. szeptember
A Unix (C) socket A kommunikációt lehetővé tevő programozási eszköz UNIX fájlleíró (ld. minden egy fájl filozófia) Passzív és aktív socketek használata read() és write() vs. send() és recv() Socket típusok (ld. szállítási réteg!): TCP-hez: stream (SOCK_STREAM) UDP-hez: datagram (SOCK_DGRAM)
Példaprogramok Socket programozási példák: http://irh.inf.unideb.hu/user/oroszp/node/98 tcpsrv.c: TCP példa - szerveroldali program fordítás: # gcc -o tcpsrv tcpsrv.c tcpclt.c: TCP példa - kliensoldali program fordítás: # gcc -o tcpclt tcpclt.c futtatás: #./tcpsrv #./tcpclt 127.0.0.1 # netstat -lp
A kapcsolat felépítése int socket(int domain, int type, int protocol): socket létrehozása domain: kommunikáció tartománya, protokoll család kiválasztása (ld. sys/socket.h) AF_INET: IPV4 végpont type: a kommunikáció szemantikájának kiválasztása SOCK_STREAM: szekvenciális, kétirányú, megbízható, kapcsolat alapú összeköttetés (TCP) SOCK_DGRAM: összeköttetés mentes kapcsolat (UDP) protocol: nem biztos, hogy implementálva van, most 0, a kommunikáció során alkalmazott szállítási réteg protokollt lehet meghatározni visszatérési érték: sikeres végrehajtáskor socket leíró (pozitív egész), hiba esetén -1, errno megfelelő hibakódra beállítva
Adatstruktúrák struct sockaddr { ( ) sa_family; // címcsalád, AF_xxx ( ) sa_data[14]; // a protocol cím 14 bájtja }; struct sockaddr_in { ( ) sin_family; // címcsalád in_port_t sin_port; // portszám struct in_addr sin_addr; // internet (IP) cím ( ) } struct in_addr { in_addr_t s_addr; // címet ír le, mérete architektúra függő };
A kapcsolat felépítése int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen): socket paraméterek beállítása sockfd: socket leíró level: SOL_SOCKET: socket API szintű konfiguráció optname: a beállítandó paraméter neve (ld. sys/socket.h) SO_REUSEADDR: kapcsolat bontása után a port újrahasznosítása (Port already in use üzenet elkerülése) SO_KEEPALIVE: kapcsolat életben tartása optval: a beállítandó érték optlen: a érték paraméter mérete visszatérési érték: 0: sikeres végrehajtás, egyébként -1, errno beállítva a megfelelő hibakódra
A kapcsolat felépítése int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen): socket hozzárendelése a névtérhez (jelen esetben IPcímhez, portszámhoz) sockfd: socket leíró addr: a hozzárendelendő névtér leírása (ld. netinet/in.h) addrlen: az előbbi adattípus hossz visszatérési érték: ld. mint előbb
A kapcsolat felépítése int listen(int sockfd, int backlog): "hallgatózás" a socketen, a sockfd passzív socketként várja a kapcsolódási kérelmeket. Passzív socket Aktív socket sockfd: socket leíró backlog: sorhossz - a várakozó kapcsolatok maximális száma visszatérési érték: mint előbb
A kapcsolat felépítése int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen): (szerverhez) kapcsolódás kezdeményezése a socketen keresztül sockfd: socket leíró addr: mutató a kapcsolat túloldali végpontját leíró adatstruktúrára addrlen: az előbbi adatstruktúra hossza visszatérési érték: siker esetén 0, egyébként -1, errno megfelelően beállítva
A kapcsolat felépítése int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen): (kliens) kapcsolódás elfogadása az adott socketen Új aktív socket Aktív socket sockfd: socket leíró addr: mutató az elfogadott kapcsolatot meghatározó adatstruktúrára addrlen: mutató az előbbi adatstruktúra hosszának eltárolásához visszatérési érték: nem neagatív egész, az elfogadott kapcsolat socket leírója, egyébként - 1, errno megfelelően beállítva Blokkolás: a végrehajtás áll, amíg nem jelentkezik kliens!
Üzenetváltás ssize_t send(int sockfd, const void *buf, size_t len, int flags): üzenet küldése a socketen keresztül sockfd: socket leíró buf: mutató az üzenetet tartalmazó memóriaterületre len: az üzenet hossza flags: most nem térünk ki rá, így 0 visszatérési érték: az átküldött karakterek száma, hiba esetén -1, errno megfelelően beállítva
Üzenetváltás ssize_t recv(int sockfd, void *buf, size_t len, int flags): üzenet beolvasása a socketen keresztül sockfd: socket leíró buf: mutató az üzenetet tartalmazó memóriaterületre len: az üzenet tárolására szolgáló memóriaterület hossza (max. ekkora üzenetet olvasunk be) flags: most nem térünk ki rá visszatérési érték: a beolvasott karakterek száma, hiba esetén -1, errno megfelelően beállítva Blokkolás!
Segédfüggvények endianness - milyen segédfüggvények állnak rendelkezésünkre az architektúrák közti konverzió kézben tartására? htons(): host to network short htonl(): host to network long ntohs(): network to host short ntohl(): network to host long
A kapcsolat bontása int close(int fd): megadott kezelőszámú fájl lezárása fd: socket leíró visszatérési érték: siker esetén 0, hiba esetén - 1, errno megfelelően beállítva Ki bontson előbb?
További hasznos tudnivalók Az utasítások részletes szintaxisa lekérhető a man parancs segítségével (pl. man connect) Blokkolás elkerülése: fcntl(sockfd, F_SETFL, O_NONBLOCK) nem jó ötlet (CPU-idő pazarlás) select() használata Parciális send: sendall() A távoli oldal lezárta a kommunikációt? recv()==0 Hálózati kapcsolatok listázása: ld. netstat parancs, LISTEN állapot