Szoftver laboratórium mérések
Hálózati és processzek közötti kommunikáció
UNIX környezetben I.
A socket interfész programozása
Mérési útmutató és feladatok
A mérést kidolgozta:
Haraszti Péter, 1995. augusztusában
Ajánlás
A Budapesti Műszaki Egyetem Távközlési és Telematikai Tanszéke új hallgatói laboratóriumi méréseket dolgoztatott ki elsôsorban műszaki informatikus hallgatók számára és a korszerű szoftverírás iránt érdeklôdô villamosmérnök hallgatók számára. A tanszék rendelkezik azokkal a hardver es szoftver eszközökkel, amelyek lehetôvé teszik UNIX operációs rendszer alatt a hálózati interfész socket interfészének programozásával elosztott-, a processzek közötti kommunációs interfész (a továbbiakban röviden IPC= Interprocess Communication) használatával pedig multiprocessz alkalmazások kifejlesztését hatékony módon. Napjainkban a projektmunka elôtérbe kerülésével fontossá válik ezen ismeretek gyakorlati alkalmazása. Ez, a sorozat első mérése a UNIX socket interfészének programozásába vezet be.
A mérés célja
A mérés célja, hogy megismertesse a hallgatókat a socket interfész programozásával Solaris 2.x (SunOS 5.x) UNIX operációs rendszer alatt, Sun SPARCstation 5, 10, 20 munkaállomásokon, vagy AIX 3.x operációs rendszer alatt, IBM RS/6000 munkaállomásokon. Mindkét operációs rendszer System V Release 4 (SVR4) kompatibilis, így az itt ismertetett programrészek lefordíthatók mindkét platformon. Továbbá a Linux SVR4 kompatibilitása miatt az itt megszerzett ismeretek IBM PC-s környezetben is alkalmazhatók.
A mérés sikeres elvégzéséhez UNIX felhasználói és C nyelvű programozási alap-ismeretek szükségesek. Előzetes ismeretek a hálózati interfész programozásában illetve az IPC programozásban jól jöhetnek, de nem szükségesek a mérés sikeres teljesítéséhez. A mérés során a hallgatóknak meglevô C nyelvű programvázakat kell kitölteniuk, kiegészíteniük a feladatban megkívánt működés eléréséhez, le kell fordítaniuk és futtatniuk az így kapott programokat, a mérési jegyzôkönyvben a tapasztalatok mellett ezek programlistáját, futtatás közbeni kimenetüket kell mellékelniük, a standard output file-ba irányításával.
A mérés során szerzett észrevételeket, kiegészítéseket és javaslatokat a következô címre lehet küldeni:
haraszti.p@ttt-atm.ttt.bme.hu
A mérés helye
A Távközlési és Telematikai Tanszék Sztoczek épület. földszint 6. laboratóriuma, IBM RS/6000 munkaállomásokon, IBM AIX 3.1 operációs rendszer alatt, vagy Sun SPARCstation 20 munka-állomáson, Solaris 2.4 (SunOS 5.4) operációs rendszer alatt.
Rendelkezésre álló információk
A mérési útmutató a következô betűtípusokat fogja használni:
• Ez a font fogja jelezni a rendszerhívásokat, programlistákat és file neveket,
• ez a font fogja jelezni a rendszer neveket,
• ez pedig a % jellel az elején a megfelelő UNIX parancssort fogja mutatni,
• végül a lényeg így lesz kiemelve.
A mérés során a hallgatók e mérési úmutató mellett a következô információs forrásokat használhatják:
• SunOS 5.3 Network Interfaces Programmer´s Guide (elérhető AnswerBook™ elektronikus formában, az answerbook OpenWindows programot használva
• SunOS 5.3 System Services Reference - Interprocess Communication fejezete (AnswerBook formátumban)
• B. Kernighan- B. Pike: A UNIX operációs rendszer című könyve
• a felhasznált rendszerhívásokról on-line részletes specifikáció man paranccsal, zárójelben feltüntetve a megfelelő szekció nevét, például semget(2) a szemaforok leolvasásához használt rendszerhívasról ad információt, s ez a tobbi rendszerhivassal egyutt a 2. szekcióban található. Elérése:
% man semget.
Ha ugyanarról a címről nem a megfelelő szekcióból kapunk információt, akkor meg kell adni a szekció azonosítóját is, ez a
% man -s 2 semget
paranccsal érhető el.
A socket interfész kialakulása
Az ISO (International Standards Organization) Open Systems Interconnect (OSI) referencia modellje logikailag hét rétegre bontja a hálózati hozzáférés felépítményét. A rétegeket az 1. ábra mutatja.

1. ábra: az OSI referencia modell
A negyedik réteg, a szállítási réteg vezérli a hálózati adatforgalmat, ez a legalacsonyabb réteg, amely end-to-end szolgáltatást tud nyújtani. UNIX rendszereknél ez a réteg támogatja például az ISO, Transmission Control Protocol (TCP), User Datagram Protocol (UDP), Xerox Network System (XNS), Systems Network Architecture (SNA) protokollokat.
A socket a programok (processzek) illetve rendszerek közötti kommunikáció alapeleme.
A UNIX socket interfész a szállítási réteghez nyújt hozzáférést. Socket-ek kommunikációs tar-tományokon (domain) belül létezhetnek, amelyek különbözô hálózati címzési módszereket és protokollokat támogathatnak. Csak ugyanazon a kommunikációs területen létrehozott és ugyanazt a protokollt használó socket-ek tudnak egymással kommunikálni. A két legfontosabb tartomány, amivel meg fogunk ismerkedni:
• UNIX domain socket-ek használhatók egy gépen belül processzek közötti adatcserére, egy megnevezett UNIX path névvel jelölve (pl. /dev/foo). A címzési család neve AF_UNIX.
• Internet domain socket-ek használhatók más-más gépen futó processzek közötti kommunikációra, TCP/IP Internet protokollt és Internet címzési rendszert használva. A címzési család neve AF_INET.
A socket interfészt az 1980-as évek elején, a University of California at Berkeley (UCB) egyik laborjában dolgozták ki TCP/IP kezelôi felületeként, UNIX operációs rendszerekhez, azóta UNIX szabványként terjedt el, implementálva van az SVR4-ben. A fejlesztôk úgy hozták létre a TCP/IP hálózati interfészt, hogy megtartották a UNIX addigi file I/O koncepcióját. Amikor egy program (processz) open() hívással sikeresen megnyit egy file-t, egy file leírót (file descriptor), vagyis egy integer értéket kap vissza, amellyel ezután a file-ra hivatkozhat, írhatja, olvashatja stb. Az operációs rendszer minden processzhez fenntart egy külön file leíró táblát (file description table), amelyben a processz által használt minden file-hoz tartozik egy az operációs rendszer által karbantartott adatstruktúrára mutató. A file leíró indexeli a file leíró táblát.
A socket a file-okhoz hasonló működéssel egy absztarkció a hálózati kommunikáció hasz-nálatához. Amikor a socket() rendszerhívással létrehozunk egy socket-et, ugyanúgy egy file leírót kapunk, mint az open() hívás esetében. A file-ok és socket-ek leíró táblája közös, így annak egy bejegyzése vagy egy megnyitott file, vagy egy megnyitott socket adatstruktúrájára mutat. Socket-ek esetében ez az adatstruktúra tartalmazza azok típusát, a használt protokollt, a Internet domain socket-ek esetén a kommunikációs végpontok IP címeit és port címeit.
Socket típusok
A /usr/include/sys/socket.h header file a használatos socket domain-ek mellet tartalmazza a támogatott socket típusokat is. Három lényeges socket típust definiáltak, más-más célú és tulajdonságú hálózati szolgálat eléréséhez:
• Stream socket-ek létrehozása kétirányú, megbízható, sorrendhelyes, és ismétlôdésmentes adatfolyamot tesz lehetôvé TCP protokoll használatával. Internet címzési rendszert (IP számok) használva (AF_INET), a socket típusa SOCK_STREAM. Az elküldhetô adatmennyiség nincs korlátozva.
• Datagram socket-ek használhatók kétirányú adatforgalom lebonyolításához, UDP protokoll használatával, de nem garantált a megbízhatóság: sorrendi hibák, ismétlôdô adatok elôfordulhatnak. Az elküldhetô adatmennyiség is korlátozva van. Internet címzési rendszert használva (AF_INET), a socket típusa SOCK_DGRAM.
• Raw socket-ek használatosak a szállítási rétegben illetve az alatt használatos protokollok kôzvetlen eléréséhez. Általában datagram alapúak, de ez az adott protokoll interfészétôl függ. Egy új kommunikációs protokoll kifejlesztéséhez és a meglevô protokollok kivételes (rejtett) eléréséhez használhatóak. Ezt a socket típust csak a rendszergazda használhatja, így nem fogunk vele foglalkozni.
A mérés során az elsô két socket típussal ismerkedünk meg mindkét kommunikációs területen. Az érdeklôdôknek ajánlott megnézni a fenti header file-t, milyen protokoll- és címzési családokat, socket típusokat támogat az adott operációs rendszer. A protokoll család általában azonos a címzési családdal.
Socket-ek használata
A kommunikáció felépítése a socket-ek mindkét oldali létrehozása után két szerepet követ, az egyik processz a szerver, a másik pedig a kliens funciót betöltve kommunikálhat egymással.
A szerver köti hozzá Internet domain esetén a socket-et egy definiált IP címhez és porthoz, ezzel létrehozva a hallgatozó (listening) végpontot, majd figyelni kezdi az ezen a végponton a kliens(ek) kapcsolatfelvételi kérelmét. Ha érkezik egy kapcsolatfelvételi kérelem, elfogadja azt, létrehozván ezzel a kommunikációs csatornát, amin most már lehet adatokat cserélni (írás és olvasás, üzenetek küldése és fogadása). A szerver bonthatja a kapcsolatot, illetve lezárhatja a listening végpontot. A 2. ábra mutatja a processzek közötti kommunikációhoz szükséges szerver oldali függvény-hívásokat.

2. ábra: szerver oldali függvényhívások
A kliens Internet domain használata esetén csatlakozik a szerverre, összerendelve a socket-jét annak IP címével és portjával, adatokat küldhet és fogadhat, majd lezárhatja az kommunikációt (3. ábra).

3. ábra: kliens oldali függvényhívások
UNIX socket rendszerhívások
A programozási technikák ismertetése elôtt tekintsük át a használt socket rendszerhívásokat. A zárójelben feltüntetett érték a man szekció neve, például a shutdown() rendszerhívást a 3N jelű szekcióban kell keresni, a szekció megadása nélkül a shutdown(2) parancsról kapunk információt...
% man -s 3N shutdown
A rendszerhívások:
Az itt felsorolt összes rendszerhívás multi-thread környezetben is megbízható (MT-safe).
• accept(3N) elfogadja a kapcsolatfelvételi kérelmet egy adott socket-en
• bind(3N) egy nevet rendel a socket-hez
• close(2) lezár egy megnyitott file leírót (file-t vagy socket-et)
• connect(3N) kapcsolatfelvételt kezdeményez egy adott socket-en
• fcntl(2) egy adott file leírót vezérel
• getpeername(3N) egy összekötött peer nevét kérdezi le
• getsockname(3N) egy adott socket aktuális nevét kérdezi le
• getsockopt(3N) egy adott socket tulajdonságait kérdezi le
• ioctl(2) eszközvezérlô funkció
• listen(3N) kapcsolatfelvételre várakozik
• poll(2) multiplexelt I/O beállításása és várakozás egy I/O eseményre
• read(2) byte-ok olvasása egy adott file leíróról
• recv(3N) egy adott socket-rôl vesz üzeneteket
• recvfrom(3N) egy adott socket-rôl vesz üzeneteket
• recvmsg(3N) egy adott socket-rôl vesz üzeneteket
• select(3C) szinkron multiplexelt I/O beállításása és várakozás egy I/O eseményre
• send(3N) egy adott socket-re ír üzeneteket
• sendmsg(3N) egy adott socket-re ír üzeneteket
• sendto(3N) egy adott socket-re ír üzeneteket
• setsockopt(3N) egy adott socket tulajdonságait állítja be
• shutdown(3N) letállítja egy kétirányú socket valamelyik irányú kommunikációját
• socket(3N) létrehoz egy névtelen socket-et
• socketpair(3N) létrehoz egy névtelen socket-párt
• write(2) byte-ok írása egy adott file leíróra
A példaprogramok fordítása és linkelése
A fent felsorolt rendszerhívásokhoz az alábbi C header file-okra van szükségünk, amelyek minden rendszeren az /usr/include alkönyvtárban találhatóak:
1. A 3N szekcióban levô függvényekhez:
<sys/socket.h>
<sys/types.h>
<sys/un.h>
csak UNIX domain socket-ekhez<netinet/in.h>
csak Internet domain socket-ekhez<netdb.h> hálózatot leíró adatbázisok eléréséhez
2. A 2 szekcióban levôkhöz:
<sys/time.h>
<unistd.h>
<fcntl.h>
<stropts.h>
<poll.h>
A programokat cc (az operációs rendszerhez szállított C fordítóval), vagy gcc (Gnu C fordítóval), két lépésben az alábbi módon lehet lefordítani; az objekt kódhoz hozzá kell linkelni a /usr/lib/libsocket.a (vagy libsocket.so.1) és a /usr/lib/libnsl.a (vagy libnsl.so.1) könyvtárakat:
% cc -c yourcfile.c
% cc -o yourexe yourfile.o -lsocket -lnsl
A mérés során több programot kell egymás után illetve párhuzamosan fejleszteni, ezért érdemes Makefile-t csinálni (make(1)), ennek egy mintája:
# Egyszerû makefile a rcvr és sndr programok elkészítéséhez
CCOMP= gcc
CFLAGS= -g -D_BSD
LFLAGS= -lsocket -lnsl
all: sndr rcvr
sndr: sender.o
${CCOMP} ${LFLAGS} sender.o -o sndr
sender.o: sender.c
${CCOMP} -c ${CFLAGS} sender.c
rcvr: receiver.o
${CCOMP} ${LFLAGS} rcvr.o -o rcvr
receiver.o: receiver.c
${CCOMP} -c ${CFLAGS} receiver.c
clean:
rm -f *.o rcvr sndr
A socket-ek programozása
Socket-ek létrehozása
A socket(3N) függvényhívással egy meghatározott típusú kommunikációs végpontot hozunk létre az adott tartományban (AF_UNIX vagy AF_INET), adott protokollt használva:
sock= socket(domain, type, protocol);
Ha a protokollt nem definiáljuk (0 érték a harmadik argumentum), akkor a rendszer kiválaszt egy a típushoz használható protokollt. Általában hagyatkozhatunk a rendszerre a protokoll megválasztásánál. SOCK_STREAM, SOCK_DGRAM, SOCK_RAW socket típusok használhatóak AF_UNIX és AF_INET tartományokon. A visszatérési érték a socket file leírója (int típusú). Ha sikertelen volt a socket létrehozása (a visszatérési érték -1), az errno rendszerszinten globális változóban nézhetjük meg a hiba okát (például nincsen elég buffer memória: errno az ENOBUFS értéket veszi fel). A perror(3C) a standard outputra írja ki a keletkezett hibát, az errno változó tartalmát kiértékelve. A következô programrészlet egy stream socket-et hoz létre, Internet kommunikációs tartományt használva:
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
extern int errno; /*csak akkor kell, ha mi erteleljuk ki a hibat*/
int sock;
...
if(sock= socket(AF_INET, SOCK_STREAM, 0)== -1)
{
perror(“Cannot create socket“);
exit(-1);
}
...
Természetesen ugyanígy létre kell hozni a másik programban is egy kommunikációs végpontot, ugyanilyen kommunikációs tartományon és típussal.
Socket-ekhez név rendelése:
A socket-ek létrehozásuk után névtelenül jönnek létre, addig nem használhatók, míg egy nevet (címet) nem rendelünk hozzájuk, illetve össze nem rendeljük azokat a távoli socket nevével, ezzel összekötve a két kommunikációs végpontot és kiépítve a csatornát a két program (processz) között. A kiépült kommunikációs csatotnának (összeköttetésnek) minden kommunikációs tartomány esetén egyedinek kell lennie. UNIX domain socket-ek esetén a
<local pathname,foreign pathname>
páros definiál egy összeköttetést, Internet domain socket-ek esetén pedig a
<protocol,local address,local port,foreign address,foreign port>
ötöst definiáljuk. A socket létrehozása (socket(3N)) után nevet kell adnunk a még névtelen socket-nek, ezt a bind(3N) rendszerhívással tehetjük meg. A hívás lehetôvé teszi a processznek a tényleges összerendelés helyi részének megnevezését (a local address, local port illetve local pathname kitöltését). Az összerendelés másik felét a connect(3N) kliens oldali és az accept(3N) szerveroldali rendszerhívás fogja kitölteni, ezzel befejezve az összeköttetés felépítését.
A következô kódrészlet a sock UNIX domain socket-hez (a kommunikációs tartomány AF_UNIX, a socket típus SOCK_DGRAM vagy SOCK_STREAM lehet) a /dev/meres path nevet rendeli hozzá. Ez az path név nem hivatkozhat már létezô file-ra, és a hívónak írási engedéllyel kell rendelkezni abban az alkönyvtárban, ahol a path név létre lesz hozva.
#include <sys/un.h>
struct sockaddr_un unaddrt;
...
strcpy(unaddr.sun_path, “/dev/meres“);
unaddr.sun_family= AF_UNIX;
bind(sock, (struct sockaddr *) &unaddr,
strlen(unaddr.sun_path)+ sizeof(unaddr.sun_family));
...
Ha már nem használatos a path név, az AF_UNIX socket-ek az unlink(1M) paranccsal törölhetôek.
Internet domain socket-ek esetén (a kommunikációs tartomány AF_INET), a socket típus SOCK_DGRAM vagy SOCK_STREAM lehet az IP Internet címek hozzárendelése jóval bonyolultabb, a socket nevesítéséhez szükség van névfeloldás végzô könyvtári rutinokra (name resolution routines). Ezt az IP cím hozzárendelés alfejezetében tárgyaljuk, ott vizsgáljuk meg, hogyan kell kitölteni az alábbi sockaddr_in struktúra egyes mezôit.
#include <sys/types.h>
#include <netinet/in.h>
struct sockaddr_in inaddr;
...
bind(sock, (struct sockaddr *) &inaddr, sizeof(inaddr));
...
Ha a hívás sikertelen (-1 a visszatérési érték), a perror(3C) hívással írathatjuk ki a hiba okát.
Kliens oldal: kapcsolatfelvételi kérés
A kapcsolatfelvételi kérés a connect(3N) hívással történik meg. Csak egy még nem kapcso-lódó processz tud kapcsolatfelvételi kérést kiadni a szerver felé.
1. UNIX domain esetén:
struct sockaddr_un server;
...
server.sun_family= AF_UNIX;
connect(sock, (struct sockaddr *) &server,
strlen(server.sun_path)+ sizeof(server.sun_family));
2. Internet domain-ek esetén:
struct sockaddr_in server;
...
connect(sock, (struct sockaddr *) &server, sizeof server);
Ha még nincs név rendelve a socket-hez, a kliens oldali nevesítés megtörténik, elkezdôdhet az adatok küldése a szervernek, illetve a szervertôl érkezô adatok olvasása. Ha a kapcsolatfelvétel sikertelen, az errno változóban megtalálható a hiba kódja.
A következô hibák fordulhatnak elô például:
ECONNREFUSED ha nem létezik a szerver processz (nem fut a szerver program)
ETIMEDOUT lejárt egy idôzítés, amely alatt nem jött létre az összeköttetés
EHOSTUNREACH nincs route a szerverhez
További információ az elôfordulható hibákról: connect(3N) és általában a rendszerhívások közben elôforduló hibákról intro(2).
Szerver oldal: a kapcsolatfelvételi kérések elfogadása
A szerver oldalán két lépés szükséges a hallgatózó (listening) socket létrehozása és névhozzá-rendelése után. Az elsô rendszerhívás, a listen(3N) állítja be, hogy a listening socket egy beérkezô kapcsolatfelvételi kérés teljesítése mellett idejűleg hány másik beérkezô kérést tud sorokkal kezelni. A második, az accept(3N) fogadja el a kapcsolatfelvételi kérést. Általában ez a hívás blokkolódik egy kapcsolatfelvételi kérés érkezéséig. Ha a kérés elfogadható, az accept() egy új file (socket) leíróval tér vissza, amelyik már összekötött állapotban van. Nincs lehetôség az accept() hívás elôtt a nem kívánatos kliensek kizárására, csak a hívás után lehet a hívó címét (a from struktúra az alábbi példában) ismerve az adatcsere megtagadására, a kapcsolat bontására. A következô kódrészlet Internet domain socket-ekre vonatkozik:
struct sockaddr_in from;
...
listen(sock, 2); /* maximum 5 queue */
fromlen= sizeof(from);
newsock= accept(sock, (struct sockaddr *) &from, &fromlen);
Unix domain socket-ek esetén sockaddr_un struktúrát kell használnunk.
Adatcsere
Adatcsere a kapcsolat felépítése után a wrtite(2) és read(2) illetve a send(3N) és recv(3N) rendszerhívásokkal lehetséges változatos módon:
ssize_t write(int filedes, const void *buffer, size_t nbyte);
ssize_t read(int filedes, const void *buffer, size_t nbyte);
int send(int sockdes, char *buffer, int length, int flags);
int recv(int sockdes, char *buffer, int length, int flags);
A két híváspár csak a flags megadásában tér el egymástól, lehetôség van az <sys/socket.h> file-ban szereplô opciók beállítására, például az MSG_PEEK opció beállítása lehetôvé a fogadó oldalon a recv() hívásnak, hogy a beérkezô adatokat olvasatlanul hagyja, a következô recv() ugyanazokat a byte-okat olvashatja. Bôvebb információ: man.
A socket-ek lezárása
A kapcsolat bontása bármelyik oldalon a close(2) rendszerhívással történik. Ha még érvényes adatok vára-koznak a sorban, lehetôség van azok feldolgozására egy bizonyos ideig a socket lezárása után és az adatok elvesztése elôtt. A shutdown(3N) hívás SOCK_STREAM socket-ek esetén a kétirányú (duplex) kommunikáció valamelyik, vagy mindkét irányának lezárását teszi lehetôvé.
IP cím hozzárendelés
Internet domain socket-ek (AF_INET) a kapcsolat felépitése a
<protocol,local address,local port,foreign address,foreign port>
ötös kitöltésével történik. Port számokat különbözô területeken allokálhatunk rendszertôl és szállítási protokolltól függôen. A bind(3N) rendszerhívás során a helyi adatok (host cím és port szám) kerülnek kitöltésre, a connect(3N) és accept(3N) hívások fejezik be az idegen (foreign) adatok kitöltését. Nehéz garantálni két socket-et összekapcsolásának egyediségét, mert az összeköttetés két lépésben és általában két külön host-on megy végbe. Nem várható el egy programtól, hogy ismerje az összes szabad portot, ezért definiálták a 0-ás wildcard portszámot: ebben az esetben a rendszer választ egy szabad portot és ehhez köti a socket-et. A saját IP cím kiválasztásánal is hagyatkozhatunk a rendszerre: az INADDR_ANY (0 érték) konstans segítségével, amelyik meg-határozatlanul hagyja a saját (helyi) IP címet. Több hálózati interfésszel rendelkezô gépeknél (például a gép rendelkezik egy Ethernet és egy ATM interfésszel) vigyáznunk kell, ha a szerver programunk megírásánál az INADDR_ANY tetszôleges (wildcard) IP címhez kötöttük a listening socket-ünket a bind(3N) hívásnál, akkor mindkét interfészrôl jelentkezhetnek kliensek! A speciális 127.0.0.1-es IP cím minden host-on a teszt interfész (loopback) IP száma. Az alábbi szerver oldali programrészlet a 3788-as porthoz rendeli hozzá a sock socket-et, a teszt hálózati interfészt használva:
#include <sys/types.h>
#include <netinet/in.h>
int sock;
struct sockaddr_in sin;
...
sock= socket(AF_INET, SOCK_STREAM, 0);
sin.sin_family= AF_INET;
sin.sin_addr.s_addr= inet_addr(“127.0.0.1“);
sin.sin_port= htons(3788);
if(bind(sock, (struct sockadd *) &sin, sizeof sin)< 0)
{
perror(“Bind error“);
exit(-1);
}
A helyi portszám kiválasztásánál két dolgot kell figyelembe vennünk: az IPPORT_RESERVED konstansnál (1024) kisebb port számok a rendszer privilegizált (root) szolgáltatásai számára foglaltak, az IPPORT_USERRESERVED konstansnál (5000) nagyobb portszámok az egyéb, nem-privilegizált szolgáltatások számára vannak fenntartva. A programozó a kettô közötti tartományból választhat a még szabad portok közül. Ha egy portot a programunkból ki- és belépve többször is fel szeretnénk használni egymás után, ezt a SO_REUSEADDR socket opció beállításával tehetjük meg, még a bind(3N) rendszerhívás elôtt, egyébként ehhez a porthoz kötéskor a EADDRINUSE kelle-metlen hibaüzenetet kapjuk.
A kliens oldal port számát és IP címét az accept(3N) hívás from argumentumából, vagy a getpeername(3N) és getsocketname(3N) rendszerhívás segítségével kaphatjuk meg.
Példa az Internet domain stream socket-ek használatára
A következô két egyszerű program egy szerver és egy kliens létrehozását mutatja be Internet domain stream socket-ek programozásával, vagyis megbízható összeköttetést hozva létre TCP protokoll használatával két IP host között (vagy ugyanazon host-on, ha mindkét program ugyanazon a host-on fut). A működési elvet a 4. ábra mutatja.

4. ábra: a kliens-szerver modell
A kliens és szerver példaprogram a mérés default könyvtárából indulva a pelda/pl1 alkönyv-tárban található. További információ a README file-okban.
A sender.c , a kliens program tartalma:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1
#define DATA "Egyszer volt"
main(argc, argv)
int argc;
char *argv[];
{
int sock, n;
struct sockaddr_in server;
char buf[1024];
if(argc< 2)
{
fprintf(stderr, "%s usage: %s <host-IP-address> <port>\n", argv[0], argv[0]);
exit(-1);
}
if((sock= socket(AF_INET, SOCK_STREAM, 0))< 0)
{
perror("opening");
exit(-1);
}
memset((void *) &server, 0, sizeof(server));
server.sin_family= AF_INET;
server.sin_addr.s_addr= htonl(inet_addr(argv[1]));
server.sin_port= htons(atoi(argv[2]));
if(connect(sock, (struct sockaddr *) &server, sizeof(server))< 0)
{
perror("connecting");
exit(1);
}
strcpy(buf, DATA);
n= strlen(buf);
if(write(sock, buf, n)!= n)
{
perror("writing");
exit(-1);
}
memset((void *) buf, 0, 1024);
if(read(sock, buf, 1024)< 0)
{
perror("reading");
exit(-1);
}
printf("sent :> %s\n", DATA);
printf("received :> %s\n", buf);
close(sock);
exit(0);
}
A receiver.c, a szerver program tartalma:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1
#define DATA "hol nem volt..."
main(argc, argv)
int argc;
char *argv[];
{
int sock, length, rval;
struct sockaddr_in server, client;
int msgsock;
char buf[1024];
if(argc< 2)
{
fprintf(stderr, "%s usage: %s <port>\n", argv[0], argv[0]);
exit(-1);
}
if((sock= socket(AF_INET, SOCK_STREAM, 0))< 0)
{
perror("opening");
exit(-1);
}
memset((void *) &server, 0, sizeof(server));
server.sin_family= AF_INET;
server.sin_addr.s_addr= htonl(INADDR_ANY);
server.sin_port= htons(atoi(argv[1]));
if(bind(sock, (struct sockaddr *) &server, sizeof(server))< 0)
{
perror("binding");
exit(-1);
}
printf("socket has port #%d\n", server.sin_port);
listen(sock, 5);
do
{
rval= sizeof(client);
memset((void *) &client, 0, rval);
if((msgsock= accept(sock, (struct sockaddr *) &client, &rval))< -1)
{
perror("accept");
exit(-1);
}
else
do
{
memset((void *) buf, 0, 1024);
if((rval= read(msgsock, buf, 1024))< 0)
perror("reading");
if(rval== 0)
printf("end connection\n");
else
printf("received> %s\n", buf);
strcpy(buf, DATA);
length= strlen(buf);
if(write(msgsock, buf, length)!= length)
perror("writing");
} while(rval!= 0);
close(msgsock);
} while (TRUE);
exit(0);
}
Példa az Internet domain datagram socket-ek használatára
A kliens és szerver példaprogram a mérés default könyvtárából indulva a pelda/pl2 alkönyv-tárban található. További információ a README file-okban.
A sender.c , a kliens program tartalma:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1
#define DATA "az operacios tengeren is tul..."
main(argc, argv)
int argc;
char *argv[];
{
int sock;
struct sockaddr_in name;
struct hostent *hp;
char buf[1024];
if(argc< 2)
{
fprintf(stderr, "%s usage: %s <hostname> <port>\n", argv[0], argv[0]);
exit(-1);
}
if((sock= socket(AF_INET, SOCK_DGRAM, 0))< 0)
{
perror("opening");
exit(-1);
}
if((hp= gethostbyname(argv[1]))== (struct hostent *) 0)
{
fprintf(stderr, "%s:unknown host name: %s\n", argv[0], argv[1]);
exit(-1);
}
memcpy((char *) &name.sin_addr, (char *) hp->h_addr, hp->h_length);
name.sin_family= AF_INET;
name.sin_port= htons(atoi(argv[2]));
if(sendto(sock, DATA, sizeof DATA, 0, (struct sockaddr *) &name, sizeof name)== -1)
perror("sending");
printf("sent> %s\n", DATA);
close(sock);
exit(0);
}
A receiver.c, a szerver program tartalma:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1
main()
{
int sock, length;
struct sockaddr_in server;
char buf[1024];
if((sock= socket(AF_INET, SOCK_DGRAM, 0))< 0)
{
perror("opening");
exit(-1);
}
memset((void *) &server, 0, sizeof(server));
server.sin_family= AF_INET;
server.sin_addr.s_addr= htonl(INADDR_ANY);
server.sin_port= 0;
if(bind(sock, (struct sockaddr *) &server, sizeof(server))< 0)
{
perror("binding");
exit(-1);
}
length= sizeof(server);
if(getsockname(sock, (struct sockaddr *)&server, & length))
{
perror("getting socket name");
exit(1);
}
printf("socket has port #%d\n", server.sin_port);
if(read(sock, buf, 1024)< 0)
{
perror("receiving");
exit(-1);
}
printf("received> %s\n", buf);
close(sock);
exit(0);
}
Példa az UNIX domain stream socket-ek használatára
A kliens és szerver példaprogram a mérés default könyvtárából indulva a pelda/pl3 alkönyv-tárban található. További információ a README file-okban.
A sender.c , a kliens program tartalma:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#define TRUE 1
#define DATA "Egyszer volt"
main(argc, argv)
int argc;
char *argv[];
{
int sock, n;
struct sockaddr_un server;
char buf[1024];
if(argc< 2)
{
fprintf(stderr, "%s usage: %s <pathname>\n", argv[0], argv[0]);
exit(-1);
}
if((sock= socket(AF_UNIX, SOCK_STREAM, 0))< 0)
{
perror("opening");
exit(-1);
}
strcpy(server.sun_path, argv[1]);
server.sun_family= AF_UNIX;
if(connect(sock, (struct sockaddr *) &server, sizeof(server.sun_family)+ strlen(server.sun_path))< 0)
{
perror("connecting");
exit(1);
}
strcpy(buf, DATA);
n= strlen(buf);
if(write(sock, buf, n)!= n)
{
perror("writing");
exit(-1);
}
memset((void *) buf, 0, 1024);
if(read(sock, buf, 1024)< 0)
{
perror("reading");
exit(-1);
}
printf("sent :> %s\n", DATA);
printf("received :> %s\n", buf);
close(sock);
exit(0);
}
A receiver.c, a szerver program tartalma:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <unistd.h>
#define TRUE 1
#define DATA "hol nem volt..."
main(argc, argv)
int argc;
char *argv[];
{
int sock, length, rval;
struct sockaddr_un server;
int msgsock;
char buf[1024];
if(argc< 2)
{
fprintf(stderr, "%s usage: %s <pathname>\n", argv[0], argv[0]);
exit(-1);
}
if((sock= socket(AF_UNIX, SOCK_STREAM, 0))< 0)
{
perror("opening");
exit(-1);
}
strcpy(server.sun_path, argv[1]);
server.sun_family= AF_UNIX;
if(bind(sock, (struct sockaddr *) &server, strlen(server.sun_path)+ sizeof(server.sun_family))== -1)
{
perror("binding");
exit(-1);
}
printf("socket created\n");
listen(sock, 5);
if((msgsock= accept(sock, (struct sockaddr *) 0, (int *) 0))== -1)
{
perror("accept");
exit(-1);
}
memset((void *) buf, 0, 1024);
if((rval= read(msgsock, buf, 1024))< 0)
{
perror("reading");
exit(-1);
}
printf("received> %s\n", buf);
strcpy(buf, DATA);
length= strlen(buf);
if(write(msgsock, buf, length)!= length)
{
perror("writing");
exit(-1);
}
close(msgsock);
unlink(argv[1]);
exit(0);
}
Példa az UNIX domain datagram socket-ek használatára
A kliens és szerver példaprogram a mérés default könyvtárából indulva a pelda/pl4 alkönyv-tárban található. További információ a README file-okban.
A sender.c , a kliens program tartalma:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#define TRUE 1
#define DATA "az operacios tengeren is tul..."
main(argc, argv)
int argc;
char *argv[];
{
int sock;
struct sockaddr_un name;
char buf[1024];
if(argc< 2)
{
fprintf(stderr, "%s usage: %s <pathname>\n", argv[0], argv[0]);
exit(-1);
}
if((sock= socket(AF_UNIX, SOCK_DGRAM, 0))< 0)
{
perror("opening");
exit(-1);
}
strcpy(name.sun_path, argv[1]);
name.sun_family= AF_UNIX;
if(sendto(sock, DATA, sizeof DATA, 0, (struct sockaddr *) &name, sizeof(name.sun_family)+ strlen(name.sun_path))== -1)
{
perror("sending");
exit(-1);
}
printf("sent> %s\n", DATA);
close(sock);
exit(0);
}
A receiver.c, a szerver program tartalma:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>
#define TRUE 1
main()
{
int sock, length;
struct sockaddr_un server;
char buf[1024];
if((sock= socket(AF_UNIX, SOCK_DGRAM, 0))< 0)
{
perror("opening");
exit(-1);
}
strcpy(server.sun_path, "/tmp/x1dhelloka");
server.sun_family= AF_UNIX;
if(bind(sock, (struct sockaddr *) &server, sizeof(server.sun_family)+ strlen(server.sun_path))< 0)
{
perror("binding");
exit(-1);
}
printf("socket created %s\n", server.sun_path);
if(read(sock, buf, 1024)< 0)
perror("receiving");
printf("received> %s\n", buf);
close(sock);
unlink("/tmp/x1dhelloka");
exit(0);
}
I/O multiplexelés
A file illetve socket írási-olvasási kérések együtt multiplexelhetôek szinkron módon a select(3C) rendszerhívással, amelynek formátuma a következô:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
A függvény használatához be kell szerkeszteni a sys/time.h és sys/select.h header file-okat is. A függvény nfds argumentum az utána következô három argumentumban elôforduló file leírók száma. A második, harmadik és negyedik argument három file leíró halmazra mutat: az elsôbe azok a file leírók vannak bemaszkolva, amelyekre olvasási feltétel bekövetkeztét várjuk, a másodikba az írási, a harmadikba a kivételes műveletre váró file leírók vannak bemaszkolva. Kivételes műveletek alatt Out-of-Band adatok érkezését kell érteni, errôl az Out-of-Band című alfejezetben lesz szó. Akármelyik maszk lehet NULL is. A maszkok long integer értékek, bennük a file leírók bitek. Ha az adott gépen a long integer 32 bites, akkor maximum 32 file leírót tudunk a select() hívással szinkron módon multiplexelni. A maszk maximális mérete (a long integer mérete) az FD_SETSIZE konstanssal van definiálva, megnézhetô a sys/select.h file-ban.
A file leírók maszkolására az alábbi makrók szolgálnak:
void FD_SET(int fd, fd_set &fdset); a file leíró beállítása a maszkban
void FD_CLR(int fd, fd_set &fdset); a file leíró törlésre a mszakból
int FD_ISSET(int fd, fd_set &fdset); a file leíró szerepel-e a maszkban
void FD_ZERO(fd_set &fdset); az egész maszk törlése
A timeout argumentum a hívásban az eseményekre való maximális várakozás idejét hivatott beállítani. Ha nincs definiálva (NULL), akkor a select() blokkolódik bármelyik bemaszkolt file leírón egy esemény bekövetkeztéig, vagy egy SIGIO illetve SIGURG signal érkezéséig. A SIGIO és SIGURG szignálok az aszinkron multiplexelést hivatottak biztosítani, errôl majd az Aszinkron socket-ek című fejezetben lesz szó. Ha a timeout 0, akkor a select() lekérdezi a bemaszkolt file-leírókra érkezett eseményeket és azonnal visszatér.
Segédfunkciók
Internet domain socket-ek esetén nem egyszerű feladat a hálózati konfigurációból eredô adatok programból lekérdezése (pl. host-nevek, hálózati címek és nevek, protokoll nevek lekérdezése). A szolgáltatásnak és a hosztnévnek a hálózati címekre leképezhetôknek kell lenniük. A hálózati címhez (a host eléréséhez) útnak (route) kell létezni. Ezen leképezések a hálózati architektúrától függenek. Az alábbi alfejezetekben a használható rendszerhívásokat fogjuk áttekinteni.
Host-név lekérdezés
Az Internet host-névbôl Internet host címre (IP számok) leképezés a gethostbyname(3N) rendszerhívás segítségével történik. A fordított irányú leképezésre a gethostbyaddr(3N) hívás használható. Mindkét hívás az alábbi hostent struktúrát használja, amely a netdb.h header file-ban van definiálva:
struct hostent
{
char *h_name; /* hivatalos host-név */
char **h_aliases; /* alias nevek */
int h_addrtype; /* cimzesi tipus: pl. AF_INET */
int h_length; /* a cimzes hossza */
char *h_addr_list; /* a halozati cimek listaja(IP szamok) */
};
Ezen hívások használatára mutat példát az elôzô fejezet példaprogramjai.
Hálózatnév lekérdezés
Az Internet hálózatnévbôl Internet hálózat címre leképezés a getnetbyname(3N) rendszerhívás segítségével történik. A fordított irányú leképezésre a getnetbyaddr(3N) hívás használható. Mindkét hívás az alábbi netent struktúrát használja, amely a netdb.h header file-ban van definiálva:
struct netent
{
char *n_name; /* hivatalos halozatnev */
char **n_aliases; /* alias nevek */
int n_addrtype; /* halozatcimzes tipus: pl. AF_INET */
int n_net; /* a halozatcim, host byte sorrendben */
};
A UNIX operációs rendszer hálózati byte-sorrendben várja mind a hálózati, mind a host címe-ket, de például a hálózat adatbázisban host byte sorrenben szerepel a hálózatcím. Sajnos elôfor-dulhat, hogy egy host byte sorrend különbözik a hálózati byte-sorrendtôl. Az alábbi konverziós rutinok léteznek:
#include <netinet/in.h>
u_long htonl(u_long hostlong);
u_long ntohl(u_long netlong);
Bizonyos gépeken nincs eltérés a host és a hálózat byte sorrend között, ekkor a fenti konverziós rutinok null makróként vannak definiálva.
Protokollnév lekérdezés
Internet domain-t használva lehetôség van a protokollnevekbôl azok sorszámára történô leképe-zésre a getprotobyname(3N) illetve fordított irányban a getprotobynumber(3N) hívásokkal. Unix domain socket-ek esetén nem létezik a protokollokról adatbázis, így nincs lehetôség a fenti konverziós rutinok használatára.
Szolgálatnév lekérdezés, definiált Internet TCP portok és UNIX domain nevek
Egy Internet domain szolgálat (service) (pl. IRC, FTP, Telnet) minden gépen egy mindenki által jól ismert, elôre meghatározott port számon van felépitve. A szolgálatokról, használt port számokról az a rendszergazda által felügyelt /etc/nsswitch.conf file tartalmaz információt. Az alábbi konverziós rutinokat használhatjuk a szolgálat név, protokoll és port szám leképezésre:
struct servent *getservbyname(const char *name, char *proto);
struct servent *getservbyport(int port, char *proto);
struct servent *getservent(void);
A servent struktúra a netdb.h header file-ban van definiálva:
struct servent {
char *s_name;
char *s_aliases;
int s_port;
char *s_proto;
};
A függvények használatáról bôvebb információt a man segítségével kaphatunk.
További programozási fogások, lehetôségek
Az eddig ismertett programozási fogások már elégségesek elosztott alkalmazások kifejlesz-téséhez. Ez a fejezet a további, még fejlettebb lehetôségeket mutatja be.
Socket opciók
Különféle socket-szintű opciók léteznek, ezeket a megfelelô típusú sockethez a setsockopt(3N) rendszerhívással lehet beállítani:
int setsockopt(int sock, int level, int optname, const char *optval, int optlen);
ahol a sock argumentum az adott socketl file leírója, a level argumentum SOL_SOCKET kell, hogy legyen. A lehetséges optname opciók a sys/socket.h header file-ban vannak felsorolva, a legfontosabbak:
SO_DEBUG a debug mezôk figyelmen kívűl hagyása
SO_REUSEADDR engedélyezi a socket ugyanazon címhez rendelését
SO_BROADCAST engedélyezi broadcast datagram üzenetek küldését egy socket-en
SO_LINGER lezárás után még meghatározott ideig élhet a socket, ha adatok érkeznek rajta
A linger típusú struktúrát kell kitölteni az idôzítés beállításához
SO_OOBINLINE visszahelyezi az OOB adatokat az összekötött csatornába
SO_SNDBUF a küldô üzenet bufferének mérete állítható
SO_RCVBUF a fogadó üzenet bufferének mérete állítható
SO_SNDTIMEO a küldô üzenet timeout-ja
SO_RCVTIMEO a fogadó üzenet timeout-ja
További információ: setsockopt(3N)!
A beállított opciók a getsockopt(3N) rendszerhívással lekérdezhetôek:
int getsockopt(int s, int level, int optname, char *optval, int *optlen);
Ebben az esetben az optname argumentum SO_TYPE kell, hogy legyen.
Az alábbi kódrészlet egy sock socket újra nevesítését és lezárása után még 2 másodpercig “életbenmaradását“ engedélyezi, majd lekérdezi a beállításokat:
#include <sys/types.h>
#include <sys/socket.h>
int type, on= 1;
struct linger li_opt;
...
li_opt.l_onoff= on;
li_opt.l_linger= 2;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
setsockopt(sock, SOL_SOCKET, SO_LINGER, &li_opt, sizeof(li_opt));
...
if(getsockopt(sock, SOL_SOCKET, SO_TYPE, (char *) &type, sizeof(type))< 0)
perror(“setsockopt hiba“);
else
...
Ha a sock socket stream típusú volt, a type a SOCK_STREAM értéket fogja felvenni.
Out-of-Band adatok
Stream socket-ek használata esetén lehetôség van két processz között a kétirányú (duplex) csatorna mellet egy logikailag külön kezelt jelzési csatornát használni, úgynevezett Out-of-Band (OOB) adatokat küldeni. Az OOB adatok a többi adattól függetlenül küldhetôek, send(3N) rendszer-hívás harmadik, flags argumentumának MSG_OOB flag beállításával.
Nem-blokkolódó socket-ek
Néhány alkalmazásnál alapkövetelmény, hogy a socket-ekre vonatkozó rendszerhívások ne blokkolódjanak. Egy olyan kérés, amely nem tud végrahajtódni azonnal, mert várakozni kell a processznek a kérés végrehajtására, nem mindig alkalmazható real-time körülmények között. Egy már létrehozott és összekötött socket-et az fcntl(2) rendszerhívással tehetünk nem-blokkolódóvá.
Az alábbi példa e hívás használatát mutatja be:
#include <fcntl.h>
#include <sys/file.h>
int fileflags;
int sock;
...
sock= socket(AF_INET, SOCK_STREAM, 0);
...
if(fileflags= fcntl(sock, F_GETFL, 0)== -1)
{
perror(“Get file flags“);
exit(-1);
}
if(fcntl(sock, F_SETFL, fileflags
| FNDELAY)== -1){
perror(“Set file to non-nlocking“);
exit(-1);
}
...
Ha egy nem-blokkolódó socket-en I/O történik, ellenôrizni kell az errno hibaváltozó tartalmát, EWOULDBLOCK hiba keletkezhet egy olyan hívásnál, amelyik rendesen (ezen opció beállítása nélkül) blokkolódna. Az accept(3N), connect(3N), send(3N), recv(3N), read(2) és write(2) hívásoknál fordulhat elô. Ha apéldául a write() hívás nem tud teljesen befejezôdni (nem tudja az adott socketre kiírni az összes byte-ot), a visszatérési érték a sikeresen elküldött byte-ok száma lesz.
Interrupt meghajtott socket-ek
A processz SIGIO szignált kap, ha a socket (vagy file) befejezte az adott I/O műveletet. E szignál elkapásához és feldolgozásához három dolgot kell megtenni:
1. Meg kell adni, hogy a processzben a SIGIO szignált melyik függvény fogja lekezelni. Ezt a signal(2), sigset(2), (sigvec(2B) a BSD kompatibilis UNIX rendszerek esetén) rend-szerhívásokkal tehetjük meg.
2. Hozzá kell rendelni az adott socket-hez a processz ID, vagy processz csoport ID az fcntl(2) hívással.
3. Át kell állítani a socket-et aszinkron üzemmódba, errôl részletesebben a következô alfejezetben lesz szó.
A következô példa egy sock socket-re állítja be, hogy a vétel pillanatában az alkalmazás a my_rcv_handler() függvénnyel kezelje a beérkezô SIGIO szignált:
#include <signal.h>
#include <fcntl.h>
#include <sys/file.h>
...
signal(SIGIO, my_rcv_handler);
sigset(SIGIO, my_rcv_handler);
if(fcntl(sock, F_SETOWN, getpid())< 0)
{
perror(“Set file to asynchronous“);
exit(-1);
}
Out-of-Band adatok aszinkron kezeléséhez hasonlóan be kell állítani a SIGURG szignál fogadását is programunkban.
Aszinkron socket-ek
Real-time alkalmazásoknál aszinron kommunikációra van szükség a processzek között. Csak stream socket-ek lehetnek ebben az üzemmódban. Hasonlóan a nem-blokkolódó socket-ekhez, a socket létrehozása, név hozzárendelése és a kapcsolat felvétele szinkron módon mehet, de a tényleges adatcsere elôtt kell átálítanunk a socket-et aszinkron üzemmódba, mint ahogy azt az alábbi példa mutatja:
#include <fcntl.h>
#include <sys/file.h>
int fileflags;
int sock;
...
sock= socket(AF_INET, SOCK_STREAM, 0);
...
if(fileflags= fcntl(sock, F_GETFL, 0)== -1)
{
perror(“Get file flags“);
exit(-1);
}
if(fcntl(sock, F_SETFL, fileflags
| FNDELAY | FASYNC)== -1){
perror(“Set file to non-nlocking and asynchronous“);
exit(-1);
}
Az aszinron üzemmódba helyezés után a send(3N), recv(3N), read(2) és write(2) hívásokat használhatjuk, ha beállítottuk a SIGIO szignál lekezelését programunkban, ahogy ezt az elôzô fejezetben bemutattuk.
Broadcast üzenetek küldése és a hálózati konfiguriáció meghatározása
Datagram típusú socket-ek Internet tartományban broadcast üzeneteket küldhetnek, hogy a kapcsolódó hálózat összes host-ját elérjék. A broadcast üzenetek igen leterhelhetik a hálózatot, hiszen minden hálózatban levô gépet az üzenetek feldolgozására kényszerítenek. Broadcast üzeneteket rendszerint két okból küldenek:
1. Megtalálni egy hálózati errôforrást címének ismerete nélkül,
2. Route-oláshoz, minden elérhetô szomszéd felkutatásához
Ha egy host több hálózatra is csatlakozik (például rendelkezik egy Ethernet és egy ATM adapter kártyával), akkor a send(3N) csak az egyik hálózatra tud broadcast üzeneteket kibocsátani, kivéve, ha a speciális INADDR_BROADCAST címhez rendeljük hozzá a socket-et (bind(3N)). Az INADDR_ANY és az INADDR_BROADCAST konstansok a netinet/in.h header file-ban vannak definiálva.
A UNIX ioctl(2) rendszerhívása SIOCGIFCONF, SIOCGIFBRDADDR és SIOCGIFFLAGS beállításai lehetôséget nyújtanak az összes kapcsolódó hálózat broadcast címének lekérdezésére, ezzel kideríthetjük, támogatja-e az adott hálózati interfész a broadcast üzenetek küldését, vagy sem, ha igen milyen broadcast címre küldhetünk broadcast üzeneteket a sendto(3N) rendszerhívás segítségével.
Mérési feladatok
Lásd a kiadott feladatjegyzéket (a mérés default könyvtárában a feladat/MERES.TODO szövegfile tartalma)
Jó munkát!