<?xml version='1.0' encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" 
	"/usr/share/sgml/docbook/xml-dtd-4.1.2/docbookx.dtd" [

<!ENTITY copydates "1995-2001">

<!ENTITY rfcbase "http://www.rfc-editor.org/">
<!ENTITY rfcpre "http://www.rfc-editor.org/rfc/rfc">
<!ENTITY rfcpost ".txt">

<!ENTITY manpre "http://linux.com.hk/man/showman.cgi?manpath=/man/man">
<!-- "2/close.2", for instance -->
<!ENTITY manpost ".inc">

<!ENTITY samplepre "">
<!ENTITY homepage "http://www.ecst.csuchico.edu/~beej/guide/net/">

<!ENTITY beejmail "beej@piratehaven.org">

<!ENTITY amazonref "http://www.amazon.com/exec/obidos/">
<!-- put an all-digits ISBN here: "ASIN/0123456789" -->
<!-- or home page reference: "redirect-home" -->
<!ENTITY amazonrefid "/beejsguides-20">

<!ENTITY winsockfaq "http://tangentsoft.net/wskfaq/">

<!ENTITY openssl "http://www.openssl.org/">

<!ENTITY esrsmartqs "http://www.tuxedo.org/~esr/faqs/smart-questions.html">

<!ENTITY karsmail "kars@kde.nl">
<!ENTITY karspage "http://analyser.oli.tudelft.nl/beej/">

<!ENTITY client_c 	SYSTEM "client.ent">
<!ENTITY getip_c 	SYSTEM "getip.ent">
<!ENTITY listener_c 	SYSTEM "listener.ent">
<!ENTITY select_c 	SYSTEM "select.ent">
<!ENTITY selectserver_c	SYSTEM "selectserver.ent">
<!ENTITY server_c	SYSTEM "server.ent">
<!ENTITY talker_c	SYSTEM "talker.ent">
]>

<!-- Nederlandse vertaling gestart op 5 april 2001 door Kars Meyboom. -->

<!-- TODO / NOG TE DOEN
  Getting error information using connected datagram sockets
  Man pages links for inet_addr, inet_ntoa, inet_aton, etc.
  
  Ophalen van fout informatie d.m.v. verbonden datagram sockets
  Verwijzingen naar man pages voor inet_addr, inet_ntoa, inet_aton, etc.
-->

<!-- CHANGES SINCE LAST RELEASE / VERANDERINGEN SINDS VORIGE UITGAVE :
--> 

<article id="index" lang="nl">

<articleinfo>
	<title>Beej's Handleiding voor Unix Netwerk Programmeren</title>
	<subtitle>Het Gebruik van Internet Sockets</subtitle>
	<titleabbrev>Beej's Handleiding voor Unix Netwerk Programmeren</titleabbrev>
	<date>8 oktober 2001</date>

	<revhistory>
		<revision>
			<revnumber>Versie 1.0.0</revnumber>
			<date>Augustus 1995</date>
			<authorinitials>beej</authorinitials>
			<revremark>Eerste versie.</revremark>
		</revision>

		<revision>
			<revnumber>Versie 1.5.5</revnumber>
			<date>13 Januari 1999</date>
			<authorinitials>beej</authorinitials>
			<revremark>Nieuwste HTML versie.</revremark>
		</revision>

		<revision>
			<revnumber>Versie 2.0.0</revnumber>
			<date>6 Maart 2001</date>
			<authorinitials>beej</authorinitials>
			<revremark>Geconverteerd naar DocBook XML, correcties, toevoegingen.</revremark>
		</revision>

		<!--
		<revision>
			<revnumber>Versie 2.0.1</revnumber>
			<date>7 Maart 2001</date>
			<authorinitials>beej</authorinitials>
			<revremark>Kleine correcties.</revremark>
		</revision>

		<revision>
			<revnumber>Versie 2.0.2</revnumber>
			<date>16 Maart 2001</date>
			<authorinitials>beej</authorinitials>
			<revremark>inet_ntoa() had zo hier en daar inet_aton() moeten zijn.</revremark>
		</revision>

		<revision>
			<revnumber>Versie 2.0.3</revnumber>
			<date>31 Maart 2001</date>
			<authorinitials>beej</authorinitials>
			<revremark>inet_aton() return waarden gecorrigeerd, selectserver
			aangepast, typefouten verbeterd.</revremark>
		</revision>

                <revision>
                        <revnumber>Versie 2.1.0</revnumber>
                        <date>3 Mei 2001</date>
                        <authorinitials>beej</authorinitials>
                        <revremark>Buffer overlopen in client.c en listener.c gefikst,
			server.c ruimt robuster zombies op, email beleid toegevoegd.</revremark>
                </revision>

                <revision>
                        <revnumber>Versie 2.2.0</revnumber>
                        <date>24 Juni 2001</date>
                        <authorinitials>beej</authorinitials>
                        <revremark>QnA (VnA) updates, Windows, talker.c, PF_INET info,
			accept() demo, geen bzero()s meer, Solaris setsockopt().</revremark>
                </revision>

                <revision>
                        <revnumber>Versie 2.2.1</revnumber>
                        <date>25 Juni 2001</date>
                        <authorinitials>beej</authorinitials>
                        <revremark>Kleine QnA (VnA) update, Amazon stuff.</revremark>
                </revision>

                <revision>
                        <revnumber>Versie 2.3.0</revnumber>
                        <date>25 Juli 2001</date>
                        <authorinitials>beej</authorinitials>
                        <revremark>QnA's (VnA's) toegevoegd, selectserver.c opgepoetst,
			WSACleanup() toegevoegd, verduidelijking van struct in_addr grootte. 
		</revremark>
                </revision>
                -->

                <revision>
                        <revnumber>Versie 2.3.1</revnumber>
                        <date>8 Oktober 2001</date>
                        <authorinitials>beej</authorinitials>
                        <revremark>Typefouten gecorrigeerd, syntax fout in client.c,
			een en ander aan V&amp;A onderdeel toegevoegd.</revremark>
                </revision>
	</revhistory>

	<author>
		<firstname>Brian</firstname><surname>Hall</surname>
		<othername>"Beej"</othername>
		<affiliation>
			<address>
				<email>&beejmail;</email>
			</address>
		</affiliation>
	</author>

	<copyright><year>&copydates;</year><holder>Brian "Beej" Hall</holder></copyright>

</articleinfo>

<!-- ======================================================= -->
<!-- Introduction -->
<!-- ======================================================= -->
<sect1 id="intro">
<title>Introductie</title>

<para>Hee! De moed verloren met socket programmeren? Is deze zooi net
iets te ingewikkeld om via de <command>man</command> pages uit te vogelen?
Wil je gaaf Internet programmeren, maar heb je geen tijd om door een
stapel <type>struct</type>s heen te waden om uit te zoeken of je nou
moet <function>bind()</function>en voor je <function>connect()</function>,
enz, enz.?</para>

<para>Nou, raad eens! Dat akelige karwei heb ik al gedaan, en ik sta te
springen om die kennis met iedereen te delen! Je bent op de goeie plek.
Dit dokument zou de gemiddelde bekwame C programmeur net het nodige duwtje
in de rug moeten geven die hij/zij nodig heeft om een beetje vat te
krijgen op dit hele netwerkgebeuren.</para>

<!-- ======================================================= -->
<!-- Audience -->
<!-- ======================================================= -->
<sect2>
<title>Publiek</title>

<para>Dit dokument is geschreven als een leerboek (tutorial), niet als een
referentiewerk. Het werkt waarschijnlijk het beste als het gelezen
wordt door personen die net beginnen met socket programmeren en
op zoek zijn naar een beetje houvast. Hoe dan ook, dit is zeker niet de
<emphasis>complete</emphasis> handleiding over socket programmeren.</para>

<para>Hopelijk, echter, is het net genoeg om al die man pages een
beetje te kunnen ontcijferen... <computeroutput>:-)</computeroutput></para>

</sect2>

<!-- ======================================================= -->
<!-- Platform and Compiler -->
<!-- ======================================================= -->
<sect2>
<title>Platform en Compiler</title>

<para>De code in dit dokument werd gecompileerd op een Linux PC met
GNU's <command>gcc</command> compiler. Het zou echter moeten compileren
op zo ongeveer elk ander platform dat <command>gcc</command> gebruikt.
Dit is uiteraard niet van toepassing als je in/voor Windows
programmeert--zie dan het <link linkend="windows">onderdeel
over Windows programmeren</link>, verderop.</para>

</sect2>

<!-- ======================================================= -->
<!-- Homepage -->
<!-- ======================================================= -->
<sect2>
<title>Offici&euml;le Homepage</title>

<para>De offici&euml;le locatie van dit dokument is op de Universiteit
van de staat Californi&euml;, Chico, op <computeroutput><ulink
url="&homepage;">&homepage;</ulink></computeroutput>.</para>

</sect2>

<!-- ======================================================= -->
<!-- Solaris and SunOS -->
<!-- ======================================================= -->
<sect2 id="solaris">
<title>Opmerking voor Solaris/SunOS Programmeurs</title>

<para>Wanneer je aan het compileren bent onder Solaris of SunOS, moet
je een aantal extra commandoregel parameters meegeven om de juiste
bibliotheken (libraries) te linken. Om dit voor elkaar te krijgen
voeg je simpelweg "<computeroutput>-lnsl -lsocket -lresolv</computeroutput>"
toe aan het einde van het compileercommando, zo dus:</para>

<screen>
    <prompt>$</prompt> <command>cc -o server server.c -lnsl -lsocket -lresolv</command>
</screen>

<para>Als je nog steeds fouten krijgt zou je nog kunnen proberen om
"<computeroutput>-lxnet</computeroutput>" aan het einde van die
commandoregel toe te voegen. Ik weet niet precies wat dat doet, maar
sommige mensen schijnen het nodig te hebben.</para>

<para>Een andere plek waar je problemen tegen zou kunnen komen is in de
aanroep naar <function>setsockopt()</function>. Het prototype verschilt
van die op m'n Linux doos, dus in plaats van:</para>

<programlisting>
        int yes=1;
</programlisting>

<para>moet je dit invoeren:</para>

<programlisting>
        char yes='1';
</programlisting>

<para>Aangezien ik geen Sun doos heb, heb ik van de bovenstaande
informatie niets kunnen testen--dit is alleen wat mensen me via email
verteld hebben.</para>

</sect2>

<!-- ======================================================= -->
<!-- Windows -->
<!-- ======================================================= -->
<sect2 id="windows">
<title>Opmerking voor Windows Programmeurs</title>

<para>Ik heb een bijzondere afkeer voor Windows, en raad je aan om in
plaats daarvan Linux, BSD of Unix te proberen. Na dat gezegd te hebben
kan ik je vertellen dat je deze zooi ook onder Windows kunt
gebruiken.</para>

<para>Ten eerste kun je zo'n beetje alle systeem header files die ik
in dit dokument opnoem vergeten. Het enige wat je moet invoegen is:</para>

<programlisting><![CDATA[
    #include <winsock.h> ]]>
</programlisting>

<para>Wacht! Je moet ook een aanroep maken naar
<function>WSAStartup()</function> voordat je ook maar iets met de
sockets bibliotheek doet. De code om dat te doen ziet er ongeveer zo
uit:</para>

<programlisting><![CDATA[
    #include <winsock.h>

    {
        WSADATA wsaData;   // als dit niet werkt,
	//WSAData wsaData; // probeer dan dit.

        if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
            fprintf(stderr, "WSAStartup is mislukt.\n");
            exit(1);
        } ]]>
</programlisting>

<para>Ook moet je je compiler vertellen om de Winsock library te
linken, meestal <filename>wsock32.lib</filename> genaamd of
<filename>winsock32.lib</filename> of iets dergelijks. Onder VC++
kan dit gedaan worden via het <computeroutput>Project</computeroutput>
menu, onder <computeroutput>Instellingen...</computeroutput>. Klik
op het <computeroutput>Link</computeroutput> tabblad, en zoek naar
de knop "Object/Bibliotheek modules". Voeg "wsock32.lib" toe
aan die lijst.</para>

<para>Zo is me ten minste verteld.</para>

<para>Tenslotte dien je <function>WSACleanup()</function> aan te
roepen wanneer je klaar bent met de sockets library. Zie je online help
voor details.</para>

<para>Als je dat eenmaal gedaan hebt zou de rest van de voorbeelden
in dit leerboek in het algemeen toepasbaar moeten zijn, met een paar
uitzonderingen. Om te beginnen,  je kunt niet de
<function>close()</function> functie gebruiken om een socket te
sluiten--daar moet je <function>closesocket()</function> voor gebruiken.
Verder werkt de functie <function>select()</function> alleen met socket
descriptors, niet met file descriptors (zoals <constant>0</constant> voor
<computeroutput>stdin</computeroutput>).</para>

<para>Er is ook een socket klasse die je kan gebruiken,
<type>CSocket</type>. Check je compiler's help pagina's voor meer
informatie.</para>

<para>Ga voor meer informatie over Winsock naar de <ulink
url="&winsockfaq;">Winsock FAQ</ulink>.</para>

<para>Tenslotte, het is me ter ore gekomen dat Windows geen
<function>fork()</function> systeemaanroep heeft welke, helaas,
in een aantal van m'n voorbeelden gebruikt wordt. Misschien moet je
iets van een POSIX library of zo gebruiken om het aan de praat te
krijgen, anders kun je misschien <function>CreateProcess()</function>
gebruiken. <function>fork()</function> heeft geen argumenten nodig,
terwijl <function>CreateProcess()</function> ongeveer 48 miljard
argumenten wil. Als je daar niet tegen opgewassen bent, de
<function>CreateThread()</function> is wat makkelijker te
verteren... Helaas valt een discussie over multithreading buiten het
bestek van dit document. Ik kan het maar over zoveel dingen hebben,
weet je!</para>

</sect2>

<!-- ======================================================= -->
<!-- Email policy -->
<!-- ======================================================= -->
<sect2 id="emailpolicy">
<title>Email Beleid</title>

<para>Over het algemeen ben ik gewoon bereikbaar om via email vragen
te beantwoorden, dus voel je vrij om te schrijven, maar ik kan geen
antwoord garanderen. Ik leid een vrij druk leven en er zijn tijden
wanneer ik gewoon geen antwoord op je vraag kan geven. Wanneer dat het
geval is verwijder ik het mailtje meestal gewoon. Da's niet persoonlijk
bedoeld; ik heb gewoon niet eeuwig de tijd om je het gedetailleerde
antwoord te geven dat je nodig hebt.</para>

<para>Als vuistregel, des te complexer je vraag, des te kleiner de kans
dat ik antwoord geef. Als je je vraag wat specifieker kan maken voor je
hem verstuurt (zoals platform, compiler, foutmeldingen, en al het andere
waarvan je denkt dat ik het probleem er sneller mee zou kunnen oplossen)
is de kans dat je antwoord krijgt een stuk groter. Voor meer tips zou
je ESR's document, <ulink url="&esrsmartqs;">How To Ask Questions The
Smart Way</ulink> (Hoe stel ik vragen op een slimme manier).</para>

<para>Als je geen antwoord krijgt, pruts er dan nog wat meer mee in de
hoop op een oplossing, en als het dan nog steeds niet lukt, mail me dan
nog een keer met de nieuwe informatie die je hebt gevonden en hopelijk
is het genoeg voor me om je te helpen.</para>

<para>Nou ik je eenmaal heb lastig gevallen over hoe je me moet mailen
en hoe niet, wil ik je even laten weten dat ik alle lof die m'n
handleiding in de afgelopen jaren heeft gekregen <emphasis>zeer</emphasis>
waardeer. Het geeft me een flinke oppepper, en het doet me plezier te
horen dat het voor het goede wordt gebruikt!
<computeroutput>:-)</computeroutput> Bedankt!</para>

</sect2>

<!-- ======================================================= -->
<!-- Mirroring -->
<!-- ======================================================= -->

<sect2>
<title>Spiegelen (mirroring)</title>

<para>Je bent van harte welkom om deze site te spiegelen, hetzij
publiek of priv&eacute;. Als je de site publiek spiegelt en wil dat ik
ernaar link vanaf de hoofdpagina, laat het me dan even weten op
<email>&beejmail;</email>.</para>

</sect2>

<!-- ======================================================= -->
<!-- Translators -->
<!-- ======================================================= -->

<sect2>
<title>Opmerking voor Vertalers</title>

<para>Als je de handleiding wil vertalen naar een andere taal, meld dat
dan even op <email>&beejmail;</email>, dan maak ik een link naar je vertaling
op de hoofdpagina.</para>

<para>Voeg gerust je naam en emailadres toe aan de vertaling.</para>

<para>Sorry, maar door ruimtegebrek kan ik de vertalingen niet
zelf hosten.</para>

</sect2>

<!-- ======================================================= -->
<!-- Dutch Translation -->
<!-- ======================================================= -->

<sect2>
<title>Opmerking bij de Nederlandse Vertaling</title>

<para>Deze handleiding is voor het eerst vertaald naar het Nederlands
op 5 april 2001, door Kars Meyboom. Mocht je opmerkingen of suggesties
hebben of typefouten tegenkomen, mail me dan even op
<email>&karsmail;</email>.</para>

<para>Zowel de Nederlandse vertaling als een mirror van de originele
handleiding zijn beschikbaar vanaf <computeroutput><ulink
url="&karspage;">&karspage;</ulink></computeroutput>.</para>

</sect2>

<!-- ======================================================= -->
<!-- Copyright -->
<!-- ======================================================= -->
<sect2>
<title>Copyright en Distributie</title>

<para>Beej's Handleiding voor Netwerk Programmeren is Copyright &copy; &copydates;
Brian "Beej" Hall.</para>

<para>Deze handleiding mag vrij herdrukt worden in elk medium mits de
inhoud niet is aangepast, het in zijn geheeld wordt aangeboden en deze
copyright mededeling intact blijft.</para>

<para>Docenten worden in het bijzonder aangemoedigd om deze handleiding
aan hun studenten aan te bevelen of hen kopi&euml;en aan te reiken.</para>

<para>Deze handleiding mag vrij worden vertaald naar elke taal, mits
de vertaling accuraat is, en de handleiding in zijn geheel wordt
aangeboden. De vertaling mag eveneens de naam en contactinformatie
van de vertaler bevatten.</para>

<para>De C broncode die in dit dokument gepresenteerd wordt is bij deze
vrijgegeven in het public domain.</para>

<para>Je kunt contact opnemen via <email>&beejmail;</email> voor meer
informatie.</para>

</sect2>

</sect1> <!-- /Introduction -->


<!-- ======================================================= -->
<!-- What is a socket? -->
<!-- ======================================================= -->

<sect1 id="theory">
<title>Wat is een socket?</title>

<para>Je hoort de hele tijd mensen praten over "sockets", en misschien
vraag je je af wat dat nou eigenlijk zijn. Nou, sockets zijn: een manier
om met andere programma's te praten d.m.v. standaard Unix file
descriptors.</para>

<para>Huh?</para>

<para>Ok--misschien heb je een Unix hacker wel eens horen zeggen,
"Tjemig, <emphasis>alles</emphasis> in Unix is een bestand!" Waar die
persoon het dan over had is het feit dat wanneer Unix programma's ook
maar enig soort van I/O doen, ze dat doen door te lezen van en te
schrijven naar een file descriptor. Een file descriptor is simpelweg
een integer, een geheel getal, dat geassoci&euml;erd wordt met een open
bestand. Maar (en hier zit 'm de kneep), dat bestand kan een netwerk
verbinding, een FIFO, een pipe, een terminal, een echt bestand op een
schijf zijn, of zo'n beetje wat dan ook. In Unix <emphasis>is</emphasis>
alles een bestand! Dus als je over het internet met een ander programma
wil communiceren, dan doe je dat door een file descriptor, geloof me
nou maar.</para>

<para>"Okee wijsneus, waar krijg ik deze file descriptor voor
netwerkcommunicatie dan?" is waarschijnlijk de laatste vraag die je
nou zou willen stellen, maar ik beantwoord hem toch maar: je roept
de <function>socket()</function> systeemroutine aan. Het geeft een
socket descriptor als waarde terug, en je praat erdoor door de
gespecialiseerde <function>send()</function> en
<function>recv()</function> (<command><ulink
url="&manpre;2/send.2&manpost;">man send</ulink></command>,
<command><ulink url="&manpre;2/recv.2&manpost;">man
recv</ulink></command>) functies aan te roepen.</para>

<para>"Ja maar, hee!" hoor ik je nou roepen, "Als het een file
descriptor is, waarom, in naam van Neptunus, kan ik dan niet gewoon
de normale <function>read()</function> en <function>write()</function>
systeemaanroepen gebruiken om door m'n socket te praten?" Het korte
antwoord is, "Dat kan!" Het lange antwoord is, "Het kan, maar
<function>send()</function> en <function>recv()</function> bieden je
veel meer controle over je data-overdracht.</para>

<para>Okee, en nu? Wat dacht je hiervan: er zijn allerlei soorten
sockets. Er zijn DARPA Internet adressen (Internet Sockets),
padnamen op een locale machine (Unix Sockets), CCITT X.25 adressen
(X.25 Sockets die je veilig kunt vergeten) en waarschijnlijk nog veel
meer, afhankelijk van het soort Unix dat je draait. Dit dokument heeft
alleen betrekking op de eerste: Internet Sockets.</para>

<!-- ======================================================= -->
<!-- Two Types of Internet Sockets -->
<!-- ======================================================= -->

<sect2 id="twotypes">
<title>Twee Soorten Internet Sockets</title>

<para>Wat krijgen we nou? Zijn er twee soorten Internet sockets? Ja.
Nou ja, eigenlijk niet. Ik lieg. Het zijn er meer, maar ik wil je
niet bang maken. Ik ga het maar over twee soorten hebben. Behalve
in deze zin, waarin ik je vertel dat "Kale Sockets" (Raw Sockets)
ook erg krachtig zijn en dat je er wat over zou moeten lezen.</para>

<para>Okee, Okee... Wat zijn die twee soorten? De ene soort is
de "Stream Socket", de andere de "Datagram Socket", naar welke
vanaf nu als "<constant>SOCK_STREAM</constant>", respectievelijk
"<constant>SOCK_DGRAM</constant>" gerefereerd zal worden. Datagram
sockets worden soms "verbindingloze (connectionless) sockets"
genoemd. (Hoewel je ze ook kunt <function>connect()</function>en
als je dat echt wilt. Zie <link linkend="connect"><function
>connect()</function></link>, verderop.)</para>

<para>Stream sockets zorgen voor betrouwbare twee-weg
communicatiestromen. Als je twee dingen in de socket stopt in de
volgorde "1, 2", dan komen ze aan de andere kant ook aan in de
volgorde "1, 2". Ze zullen ook vrij van fouten zijn. Fouten die
je wel tegenkomt zijn hersenspinsels van je eigen verwarde geest,
en daar zullen we het hier dus niet over hebben.</para>

<para>Wie of wat gebruikt stream sockets? Nou, misschien heb je
wel eens van het <command>telnet</command> programma gehoord. Dat
gebruikt stream sockets. Alle karakters die je typt moeten in
dezelfde volgorde aankomen zoals je ze typt, nietwaar? Verder
gebruiken web browsers het HTTP protocol, welke stream sockets
gebruikt om pagina's op te halen. Inderdaad, als je telnet naar
een web site op poort 80, en "<computeroutput>GET
/</computeroutput>" intypt, rolt de HTML pagina over je scherm!</para>

<para>Hoe realiseren stream sockets zo'n hoog kwaliteitsniveau wat
de datatransmissie betreft? Ze gebruiken een protocol genaamd
"Het Transmissie Controle Protocol (The Transmission Control
Protocol)", ook wel "TCP" genaamd (zie <ulink
url="&rfcpre;793&rfcpost;">RFC-793</ulink> voor extreem gedetailleerde
informatie over TCP). TCP zorgt ervoor dat je data sequenti&euml;el (dus in
volgorde) en foutvrij aankomt. Misschien heb je "TCP" wel eens eerder
gehoord in de term "TCP/IP" waarin "IP" staat voor "Internet Protocol"
(zie <ulink url="&rfcpre;791&rfcpost;">RFC-791</ulink>). IP houdt
zich voornamelijk bezig met internet routing (het zoeken van een
weg van een computer naar een andere over een internet) en is over
het algemeen niet verantwoordelijk voor data-integriteit.</para>

<para>Gaaf. En hoe zit het met datagram sockets? Waarom worden ze
verbindingloos genoemd? Hoe zit dat nou eigenlijk? Waarom zijn ze
onbetrouwbaar? Nou, hier zijn wat feiten: als je een datagram stuurt,
zou het aan kunnen komen. Het zou in een andere volgorde aan kunnen
komen. Als het aankomt, dan is de data in het pakket vrij van
fouten.</para>

<para>Datagram sockets gebruiken ook IP voor hun routing, maar ze
gebruiken geen TCP; ze gebruiken het "Gebruiker Datagram Protocol
(User Datagram Protocol)", of "UDP" (zie <ulink
url="&rfcpre;768&rfcpost;">RFC-768</ulink>.)</para>

<para>Waarom zijn ze verbindingloos? Nou, simpelweg omdat je geen
verbinding open hoeft te houden zoals je dat met stream sockets
doet. Je bouwt gewoon een pakketje, mept er een IP header op
met een bestemmingsadres, en stuurt 't de deur uit. Geen verbinding
nodig. Ze worden over het algemeen gebruikt voor per-pakket
verzendingen van informatie. Voorbeeldtoepassingen:
<command>ftp</command>, <command>bootp</command>, etc.</para>

<para>"Genoeg!" hoor ik je roepen. "Hoe werken deze programma's
nou eigenlijk als datagrammen verloren kunnen gaan?!" Nou, m'n
menselijke vriend, elk van die programma's heeft zijn eigen
protocol boven UDP. Bijvoorbeeld, het tftp protocol zegt dat voor
elk pakket dat verstuurd wordt, de ontvanger een pakketje moet terug
sturen dat zegt, "Ik heb 'em!" (een "ACK" pakket). Als de verzender
van het originele pakket geen antwoord krijgt in, zeg, vijf seconden,
dan stuurt hij het pakket opnieuw tot hij een ACK krijgt. Deze
bevestigingsprocedure (ACKnowledgement procedure) is erg belangrijk
bij het implementeren van <constant>SOCK_DGRAM</constant>
programma's.</para>

</sect2>

<!-- ======================================================= -->
<!-- Two Types of Internet Sockets -->
<!-- ======================================================= -->

<sect2 id="lowlevel">
<title>Low level Gedoe en Netwerk Theorie</title>

<para>Aangezien ik het net heb gehad over het stapelen (layering) van
protocollen, is het tijd om het te hebben over hoe netwerken echt
werken, en wat voorbeelden te laten zien over hoe
<constant>SOCK_DGRAM</constant> pakketten worden gebouwd. Praktisch
gezien zou je dit hoofdstuk over kunnen slaan, maar het bevat
wel goede achtergrondinformatie.</para>

<figure float="0" id="figure1">
<title>Data Inkapsulatie.</title>
	<mediaobject>
		<imageobject><imagedata fileref="dataencap.eps"></imagedata></imageobject>
		<imageobject><imagedata fileref="dataencap.pdf"></imagedata></imageobject>
		<imageobject><imagedata fileref="dataencap.gif"></imagedata></imageobject>
		<textobject>
			<phrase>[Ingekapselde Protocollen Diagram]</phrase>
		</textobject>
	</mediaobject>
</figure>

<para>Hey jongens, het is tijd om wat te leren over <emphasis><link
linkend="figure1">Data Inkapsulatie (Data Encapsulatiion)</link></emphasis>!
Dit is heel erg belangrijk. Zelfs zo belangrijk dat je het misschien
wel zou leren als je de netwerkcursus doet, hier in Chico State
<computeroutput>;-)</computeroutput>. Waar het om draait is het volgende:
een pakketje wordt geboren, wordt in een kop (header) verpakt
("ingekapseld") (en zelden in een staart (footer)) door het eerste
protocol (zeg, het TFTP protocol). Daarna wordt het hele zaakje
(inclusief de TFTP kop) opnieuw ingepakt door het volgende protocol
(zeg, UDP), daarna nog een keer door het volgende protocol (IP),
dan nog een keer door het laatste protocol van de hardware (fysieke)
laag (zeg, ethernet).</para>

<para>Wanneer een andere computer het pakket ontvangt, haalt de
hardware de ethernet kop eraf, de kernel verwijdert de IP en UDP
koppen, het TFTP verwijdert de TFTP kop, en verkrijgt zo de
data.</para>

<para>Nou kan ik het eindelijk hebben over het notoire
<emphasis>Gelaagde Netwerk Model (Layered Network Model)</emphasis>.
Dit netwerk model beschrijft een systeem van netwerkfunctionaliteit
dat vele voordelen heeft over andere modellen. Bijvoorbeeld, je
kunt socket programma's schrijven die exact hetzelfde zijn zonder
je zorgen te maken over hoe de data fysiek wordt verzonden
(seri&euml;le kabel, ethernet, AUI, maakt niet uit) omdat programma's
op lagere niveaus dat voor je doen. De feitelijke netwerk hardware
en topologie is transparant voor de socket programmeur.</para>

<para>Zonder verder gedraal presenteer ik de lagen van het hele
model. Onthou dit voor je netwerkcursus examens:</para>

<para>
<itemizedlist>

<listitem><para>Applicatie</para></listitem>

<listitem><para>Presentatie</para></listitem>

<listitem><para>Sessie</para></listitem>

<listitem><para>Transport</para></listitem>

<listitem><para>Netwerk</para></listitem>

<listitem><para>Data Link</para></listitem>

<listitem><para>Fysiek</para></listitem>

</itemizedlist>
</para>

<para>De Fysieke Laag is de hardware (serieel, ethernet, enz.). De
Applicatie Laag is zo'n beetje het verst van de fysieke laag als je
je maar kan voorstellen--dat is waar gebruikers de interactie met
het netwerk aangaan.</para>

<para>Nou, dit model is zo algemeen dat je het waarschijnlijk zou
kunnen gebruiken als een autoreparatiehandleiding als je het echt
zou willen. Een gelaagd model dat meer overeenkomst vertoont met
Unix zou kunnen zijn:</para>

<para>
<itemizedlist>

<listitem><para>Applicatielaag (<emphasis>telnet, ftp,
etc.</emphasis>)</para></listitem>

<listitem><para>Host-naar-Host Transportlaag (<emphasis>TCP,
UDP</emphasis>)</para></listitem>

<listitem><para>Internetlaag (<emphasis>IP en
routing</emphasis>)</para></listitem>

<listitem><para>Netwerk Toegangslaag (<emphasis>Ethernet, ATM, of
wat dan ook</emphasis>)</para></listitem>

</itemizedlist>
</para>

<para>Vanaf dit moment zie je waarschijnlijk wel in hoe deze lagen
corresponderen met de inkapsulatie van de originele data.</para>

<para>Zie je hoeveel werk er nodig is om een simpel pakketje te
bouwen? Jemig! En je moet de koppen van de pakketten zelf intypen
met "<command>cat</command>"! Geintje. Het enige wat je moet doen voor
stream sockets, is de data <function>send()</function>en. Het enige
wat je moet doen voor datagram sockets, is je pakketje naar eigen
inzicht inpakken, en met <function>sendto()</function> naar buiten
sturen. De kernel bouwt de Transportlaag en Internetlaag voor je
en de hardware doet de Netwerktoegangslaag. Ahh, moderne
technologie.</para>

<para>Hiermee eindigt ons korte uitstapje in de netwerk theorie. O
ja, ik vergat je alles wat ik over routing te zeggen heb te vertellen:
niks! Inderdaad, daar ga ik het niet over hebben. De router rukt de
IP kop van het pakketje, kijkt in z'n routing tabel, bla bla bla.
Kijk maar in de <ulink url="&rfcpre;791&rfcpost;">IP RFC</ulink>
als je het echt wilt weten. Als je er nooit wat over leert, ach,
dat overleef je wel.</para>

</sect2>

</sect1> <!-- What is a socket? -->

<!-- ======================================================= -->
<!-- structs -->
<!-- ======================================================= -->
<sect1 id="structs">
<title><type>struct</type>s en Data Handling</title>

<para>Zo, we zijn er eindelijk. Het is tijd om het over programmeren
te hebben. In dit hoofdstuk ga ik het hebben over verscheidene
datatypes die gebruikt worden door de sockets interface, omdat
het uitpluizen van sommige ervan een flinke kluif is.</para>

<para>Eerst de makkelijkste: een socket descriptor. Een socket
descriptor is het volgende type:</para>

<programlisting><![CDATA[
    int ]]>
</programlisting>

<para>Gewoon een doorsnee <type>int</type>.</para>

<para>Vanaf hier wordt het wat vaag, dus lees maar gewoon door en heb
een beetje vertrouwen in me. Onthoud dit: er zijn twee byte volgorden:
meest significante byte (most significant byte, soms een "octet"
genoemd) eerst, of minst significante byte eerst. De eerste wordt
de "Netwerk Byte Volgorde" (Network Byte Order) genoemd. Sommige
machines slaan hun data intern in netwerk byte volgorde op, andere
niet. Wanneer ik zeg dat iets in netwerk byte volgorde moet staan,
moet je een functie (zoals <function>htons()</function>) aanroepen om
het te veranderen van "Host Byte Volgorde" naar "Netwerk Byte
Volgorde". Als ik niet zeg dat het in "Netwerk Byte Volgorde" moet
staan, dan moet het gewoon in Host Byte Volgorde staan.</para>

<para>Voor de nieuwsgierigen, "Netwerk Byte Volgorde" staat ook
bekend als "Big-Endian Byte Order", wat zoveel betekent als
"Grootste Byte Eerst".</para>

<para>Mijn Eerste Struct<superscript>TM</superscript>--<type>struct
sockaddr</type>. Deze structuur bevat socket adresinformatie voor
vele sockettypen:</para>

<programlisting><![CDATA[
    struct sockaddr {
        unsigned short    sa_family;    // adres familie, AF_xxx
        char              sa_data[14];  // 14 bytes voor protocol adres
    }; ]]>
</programlisting>

<para><parameter>sa_family</parameter> kan een aantal dingen betekenen,
maar in dit dokument zal het altijd de waarde <constant>AF_INET</constant>
hebben. <parameter>sa_data</parameter> bevat een bestemmingsadres en
poortnummer voor de socket. Dit is nogal onpraktisch aangezien je
niet tot in den treure het adres met de hand in
<parameter>sa_data</parameter> wil verpakken.</para>

<para>Om met <type>struct sockaddr</type> overweg te kunnen hebben
programmeurs een parallelle structuur gemaakt: <type>struct
sockaddr_in</type> ("in" als in "internet").</para>

<programlisting><![CDATA[
    struct sockaddr_in {
        short int          sin_family;  // Adres familie
        unsigned short int sin_port;    // Poort nummer
        struct in_addr     sin_addr;    // Internet adres
        unsigned char      sin_zero[8]; // Zelfde grootte als struct sockaddr
    }; ]]>
</programlisting>

<para>Deze structuur maakt het makkelijk om te refereren naar elementen
van het socket adres. Merk op dat <parameter>sin_zero</parameter>
(welke is ingevoegd om de structuur op te vullen tot dezelfde grootte
als een <type>struct sockaddr</type>) met <function>memset()</function>
met allemaal nullen gevuld moet worden. Ook, en dit is het
<emphasis>belangrijke</emphasis> stuk, een pointer naar een <type>struct
sockaddr_in</type> kan gecast worden naar een pointer naar een
<type>struct sockaddr</type> en vice versa. Dus hoewel
<function>socket()</function> een <type>struct sockaddr*</type> wil,
kun je gewoon en <type>struct sockaddr_in</type> gebruiken en op het
laatste moment een cast uitvoeren! Merk verder op dat
<parameter>sin_family</parameter> correspondeert met
<parameter>sa_family</parameter> in een <type>struct sockaddr</type>
en dat deze op "<constant>AF_INET</constant>" gezet moet worden.
Tenslotte moeten <parameter>sin_port</parameter> en
<parameter>sin_addr</parameter> in Netwerk Byte Volgorde staan!</para>

<para>"Maar", sputter je tegen, "hoe kan die hele structuur,
<type>struct in_addr sin_addr</type>, nou in Netwerk Byte Volgorde
staan?" Deze vraag vereist een nauwkeurig onderzoek van de
<type>struct in_addr</type> structuur, een van de ergste
verenigingen (unions) in de wereld:</para>

<programlisting><![CDATA[
    // Internet adres (een structuur om historische redenen)
    struct in_addr {
        unsigned long s_addr; // da's een 32-bit long, oftwel 4 bytes
    }; ]]>
</programlisting>

<para>Nou ja, <emphasis>vroeger</emphasis> was het een union, maar
tegenwoordig lijkt het erop dat 'ie weg is. De groeten. Dus als
je <parameter>ina</parameter> hebt declareerd als van het type
<type>struct sockaddr_in</type>, dan refereert
<parameter>ina.sin_addr.s_addr</parameter> naar het 4-bytes lange
IP adres (in Netwerk Byte Volgorde). Let er wel op dat zelfs als
je systeem nog steeds die afgrijselijke union gebruikt voor
<type>struct in_addr</type>, je nog steeds naar het 4-bytes
IP adres kunt refereren op precies dezelfde manier als ik hierboven
heb gedaan (dit dankzij <computeroutput>#define</computeroutput>s.)</para>

<!-- ======================================================= -->
<!-- Convert The Natives -->
<!-- ======================================================= -->
<sect2 id="convert">
<title>De Inboorlingen Converteren!</title>

<para>En zo zijn we netjes bij het volgende hoofdstuk aangeland. We
hebben het al te lang over deze Netwerk naar Host Byte Volgorde
conversie gehad--nou is het tijd voor actie!</para>

<para>Okie dokie. Er zijn twee typen die je kan converteren:
<type>short</type> (twee bytes) en <type>long</type> (vier bytes).
Deze functies werken ook voor de <type>unsigned</type>
varianten. Stel dat je een <type>short</type> wil converteren
van Host Byte Volgorde naar Network Byte Volgorde. Begin met
"h" voor "host", plak er "to" (als in "naar") aanvast, daarna
"n" voor "netwerk" en "s" voor "short": h-to-n-s, oftewel
<function>htons()</function> (lees: "Host to Network Short").</para>

<para>Dit is bijna te makkelijk...</para>

<para>Je kunt elke combinatie van "n", "h", "s" en "l" gebruiken die
je maar wil, de echt stomme niet meegerekend. Zo is er bijvoorbeeld
GEEN <function>stolh()</function> ("Short to Long Host") functie--niet
op dit feestje, in elk geval. Wel zijn er:</para>

<para>
<itemizedlist>
<listitem><para><function>htons()</function> -- "Host to Network Short"</para></listitem>
<listitem><para><function>htonl()</function> -- "Host to Network Long"</para></listitem>
<listitem><para><function>ntohs()</function> -- "Network to Host Short"</para></listitem>
<listitem><para><function>ntohl()</function> -- "Network to Host Long"</para></listitem>
</itemizedlist>
</para>

<para>Nou denk je misschien bij de hand te zijn door te vragen,
"Wat moet ik doen om de byte volgorde van een <type>char</type>
te veranderen?" Daarna denk je hopelijk, "Uh, laat maar." Je
denkt misschien ook dat aangezien je machine een 68000 toch
al netwerk byte volgorde gebruikt, je <function>htonl()</function>
niet nodig hebt voor je IP adressen. Je zou gelijk hebben,
<emphasis>MAAR</emphasis>, als je zou proberen je programma
te porteren naar een andere machine die omgekeerde netwerk byte
volgorde gebruikt, doet je programma het niet. Wees porteerbaar! Dit
is een Unix wereld! (Hoewel Bill Gates je graag anders zou doen geloven.)
Denk erom: zet je bytes in Netwerk Byte Volgorde voor je ze op
het netwerk zet.</para>

<para>Nog een laatste puntje; waarom moeten <parameter>sin_addr</parameter>
en <parameter>sin_port</parameter> in Netwerk Byte Volgorde staan in
een <type>struct sockaddr_in</type>, maar <parameter>sin_family</parameter>
niet? Het antwoord: <parameter>sin_addr</parameter> en
<parameter>sin_port</parameter> worden ingekapseld in het pakket in
de IP en UDP lagen, respectievelijk. Dus moeten ze in Netwerk Byte
Volgorde staan. Het <parameter>sin_family</parameter> veld daarentegen
wordt alleen door de kernel gebruikt om te bepalen wat voor type
adres de structuur bevat, dus moet het in Host Byte Volgorde staan.
Tevens, aangezien <parameter>sin_family</parameter> <emphasis>niet</emphasis>
over het netwerk wordt verstuurd, kan het gewoon in Host Byte
Volgorde staan.</para>

</sect2>

<!-- ======================================================= -->
<!-- IP Addresses -->
<!-- ======================================================= -->
<sect2 id="ipaddr">
<title>IP Adressen en Hoe Ermee Om te Gaan</title>

<para>Gelukkig voor jou zijn er een aantal functies die je in staat
stellen om IP adressen te manipuleren. Nergens voor nodig om
ze zelf te ontleden en met de hand in een <type>long</type> te
proppen met de <computeroutput><![CDATA[<<]]></computeroutput>
operator.</para>

<para>Ten eerste, stel dat je een <type>struct sockaddr_in ina</type>
hebt, en een IP adres "<computeroutput>10.12.110.57</computeroutput>"
die je erin wil opslaan. De functie die je dan wil gebruiken,
<function>inet_addr()</function>, converteert een IP adres in
getallen-en-punten notatie naar een unsigned long. De toewijzing
kan als volgt worden gedaan:</para>

<programlisting><![CDATA[
    ina.sin_addr.s_addr = inet_addr("10.12.110.57"); ]]>
</programlisting>

<para>Merk op dat <function>inet_addr()</function> het adres al in
Netwerk Byte Volgorde teruggeeft--je hoeft niet zelf
<function>htonl()</function> aan te roepen. Vet!</para>

<para>Nou, het bovenstaande stukje code is niet erg robust omdat
er geen foutencontrole plaats vindt. Zie je, <function>inet_addr()</function>
geeft <constant>-1</constant> terug bij een fout. Ken je de binaire
getallen nog een beetje? <constant>(unsigned)-1</constant> komt toevallig
overeen met het IP adres <computeroutput>255.255.255.255</computeroutput>!
Dat is het broadcastadres! Oepsie. Denk erom dat je wel je
foutafhandeling goed voor elkaar hebt.</para>

<para>Eigenlijk is er een nettere interface die je kunt gebruiken in
plaats van <function>inet_addr()</function>: het heet
<function>inet_aton()</function> ("aton" betekent "ascii to network"):</para>

<programlisting><![CDATA[
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>

    int inet_aton(const char *cp, struct in_addr *inp); ]]>
</programlisting>

<para>En hier is een gebruiksvoorbeeld, tijdens het inpakken
van een <type>struct sockaddr_in</type> (dit voorbeeld is beter
te begrijpen wanneer je bij de hoofdstukken over <link linkend="bind">
<function>bind()</function></link> en <link
linkend="connect"><function>connect()</function></link> bent
beland).</para>

<programlisting><![CDATA[
    struct sockaddr_in mijn_addr;

    mijn_addr.sin_family = AF_INET;         // host byte volgorde
    mijn_addr.sin_port = htons(MYPORT);     // short, network byte volgorde
    inet_aton("10.12.110.57", &(mijn_addr.sin_addr));
    memset(&(mijn_addr.sin_zero), '\0', 8); // de rest van de struct op nul ]]>
</programlisting>

<para><function>inet_aton()</function>, <emphasis>in tegenstelling
tot vrijwel elke andere socket-gerelateerde functie</emphasis>,
geeft een niet-nul waarde terug bij succes, en nul bij een fout.
En het adres wordt teruggegeven in <parameter>inp</parameter>.</para>

<para>Helaas implementeren niet alle platforms <function>inet_aton()</function>,
dus, hoewel het gebruik ervan de voorkeur heeft, wordt de oudere,
meer bekende <function>inet_addr()</function> in deze handleiding
gebruikt.</para>

<para>Okee, nou kun je een reeks IP adressen converteren naar
hun binaire representaties. Maar andersom? Stel dat je een
<type>struct in_addr</type> hebt en je wilt het in getallen-en-punten
notatie weergeven? In dat geval heb je de functie
<function>inet_ntoa()</function> nodig ("ntoa" betekent "network to
ascii") zoals hier:</para>

<programlisting><![CDATA[
    printf("%s", inet_ntoa(ina.sin_addr)); ]]>
</programlisting>

<para>Dat drukt het IP adres af. Merk op dat <function>inet_ntoa()</function>
een <type>struct in_addr</type> als argument wil, en niet een
<type>long</type>. Merk ook op dat het een pointer naar char teruggeeft.
Deze wijst naar een statisch opgeslagen char array binnen
<function>inet_ntoa()</function> waardoor bij iedere aanroep
naar <function>inet_ntoa()</function> het vorige IP adres wordt
overschreven. Bijvoorbeeld:</para>

<programlisting><![CDATA[
    char *a1, *a2;
    .
    .
    a1 = inet_ntoa(ina1.sin_addr);  // dit is 192.168.4.14
    a2 = inet_ntoa(ina2.sin_addr);  // dit is 10.12.110.57
    printf("adres 1: %s\n",a1);
    printf("adres 2: %s\n",a2); ]]>
</programlisting>

<para>drukt af:</para>

<programlisting><![CDATA[
    adres 1: 10.12.110.57
    adres 2: 10.12.110.57 ]]>
</programlisting>

<para>Als je het adres wil bewaren, <function>strcpy()</function> het
dan naar je eigen char array.</para>

<para>Dat was het voorlopig wat betreft dit onderwerp. Later leer
je hoe je een string zoals "www.overheid.nl" kunt converteren naar
het corresponderende IP adres (zie <link linkend="dns">DNS</link>,
verderop).</para>

</sect2>
</sect1> <!-- /structs -->

<!-- ======================================================= -->
<!-- System Calls -->
<!-- ======================================================= -->
<sect1 id="syscalls">
<title>Systeemaanroepen of Barsten</title>

<para>Dit is het hoofdstuk waar we in de systeemaanroepen duiken die
je in staat stellen de netwerkfunctionaliteit van een Unix doos aan
te spreken. Wanneer je &eacute;&eacute;n van deze functies aanroept
neemt de kernel het over en doet al het werk automagisch voor je.</para>

<para>De plek waar de meeste mensen vast komen te zitten is de volgorde
waarin ze deze dingen moeten aanroepen. In dat geval zijn de
<command>man</command> pages nutteloos, zoals je waarschijnlijk al
hebt ontdekt. Nou, om je uit die afgrijselijke situatie te helpen,
heb ik geprobeerd om alle aanroepen in de volgende onderdelen
<emphasis>precies</emphasis> zo uit te leggen (ongeveer) in dezelfde
volgorde als je ze in je eigen programma's zou gebruiken.</para>

<para>Dat, samen met wat stukjes voorbeeldcode zo hier en daar,
wat melk en koekjes (waar je toch echt zelf voor zult moeten zorgen),
een stevig stel kloten en wat moed, en je zult data over het
internet stralen als ware je de Zoon van Jon Postel!</para>

<!-- ======================================================= -->
<!-- socket -->
<!-- ======================================================= -->
<sect2 id="socket">
<title><function>socket()</function>--Grijp Die File Descriptor!</title>

<para>Ik vrees dat ik het niet langer uit kan stellen--ik moet het
gaan hebben over de <function>socket()</function> systeemaanroep. Hier
gaat het om:</para>

<programlisting><![CDATA[
    #include <sys/types.h>
    #include <sys/socket.h>

    int socket(int domain, int type, int protocol); ]]>
</programlisting>

<para>Maar wat zijn dat voor argumenten? Ten eerste,
<parameter>domain</parameter> moet op "<constant>AF_INET</constant>"
gezet worden, net zoals in de <type>struct sockaddr_in</type>
(zie terug). Verder, het <parameter>type</parameter> argument
vertelt de kernel wat voor soort socket dit is:
<constant>SOCK_STREAM</constant> of <constant>SOCK_DGRAM</constant>.
Tenslotte kun je <parameter>protocol</parameter> gewoon op
"<constant>0</constant>" zetten om <function>socket()</function>
op basis van het <parameter>type</parameter> zelf uit te laten
zoeken welk protocol het moet gebruiken. (Opmerkingen: er zijn veel
meer <parameter>domain</parameter>s dan ik heb beschreven.
Zie de <function>socket()</function> man page. Verder is er een
"betere" manier om het <parameter>protocol</parameter> te achterhalen.
Zie de <function>getprotobyname()</function> man page.)</para>

<para><function>socket()</function> geeft je simpelweg een socket
descriptor terug die je kan gebruiken in latere systeemaanroepen,
of <constant>-1</constant> bij een fout. De globale variabele
<parameter>errno</parameter> krijgt de waarde van de fout (zie de
<function>perror()</function> man page.)</para>

<para>In bepaalde documentatie is er sprake van een mystieke
"<constant>PF_INET</constant>". Dit is een vreemd etherisch wezen
dat zelden in de natuur wordt gesignaleerd, maar ik kan hier wel een
beetje opheldering geven. Eens, lang geleden, werd er gedacht dat
een adres familie (waar de "AF" in "<constant>AF_INET</constant>" voor
staat) misschien meerdere protocollen zou ondersteunen, aan welke
gerefereerd zou worden aan door hun protocol familie (waar "PF" in
"<constant>PF_INET</constant>" voor staat). Dat gebeurde niet. Ook
best. Dus de juiste manier is om <constant>AF_INET</constant>
in je <type>struct sockaddr_in</type> te gebruiken en
<constant>PF_INET</constant> in je aanroep naar
<function>socket()</function>. Maar praktisch gezien kun je overal
<constant>AF_INET</constant> gebruiken. En, aangezien W. Richard
Stevens dat in zijn boek ook doet, doe ik dat hier ook.</para>

<para>Ja, ja, dat zal wel, maar wat heb ik eraan? Het antwoord is
dat je er op zich eigenlijk niks aan hebt, en je verder moet lezen
en meer systeemaanroepen moet doen om er wat zinnigs uit te
krijgen.</para>

</sect2>

<!-- ======================================================= -->
<!-- bind -->
<!-- ======================================================= -->
<sect2 id="bind">
<title><function>bind()</function>--Op Welke Poort Zit Ik?</title>

<para>Als je eenmaal een socket hebt, moet je die misschien nog
associ&euml;ren met een poort op je locale machine. (Dit is gebruikelijk
als je gaat <function>listen()</function>en (luisteren) naar inkomende
verbindingen op een specifieke poort--MUDs doen dit als ze je vertellen
dat je moet "telnetten naar w.x.y.z op poort 6969".) Het poortnummer
wordt door de kernel gebruikt om een inkomend pakketje met een
bepaald proces z'n file descriptor te associ&euml;ren. Als je alleen
maar gaat <function>connect()</function>en, is dit misschien onnodig.
Lees het toch maar, gewoon voor de kick.</para>

<para>Hier is de beschrijving door de <function>bind()</function>
systeemaanroep:</para>

<programlisting><![CDATA[
    #include <sys/types.h>
    #include <sys/socket.h>

    int bind(int sockfd, struct sockaddr *my_addr, int addrlen); ]]>
</programlisting>

<para><parameter>sockfd</parameter> is de socket file descriptor
die door <function>socket()</function> wordt teruggegeven.
<parameter>my_addr</parameter> is een pointer naar een
<type>struct sockaddr</type> die informatie bevat over je
adres, namelijk poort en IP adres. <parameter>addrlen</parameter>
kun je instellen op <computeroutput>sizeof(struct
sockaddr)</computeroutput>.</para>

<para>Pfoeh. Dat was een flinke kluif. Tijd voor een voorbeeld:</para>

<programlisting><![CDATA[
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>

    #define MIJNPOORT 3490

    main()
    {
        int sockfd;
        struct sockaddr_in mijn_addr;

        sockfd = socket(AF_INET, SOCK_STREAM, 0); // doe wat foutafhandeling!

        mijn_addr.sin_family = AF_INET;         // host byte volgorde
        mijn_addr.sin_port = htons(MYPORT);     // short, netwerk byte volgorde
        mijn_addr.sin_addr.s_addr = inet_addr("10.12.110.57");
        memset(&(mijn_addr.sin_zero), '\0', 8); // de rest van de struct op nul

        // vergeet de foutafhandeling voor bind() niet:
        bind(sockfd, (struct sockaddr *)&mijn_addr, sizeof(struct sockaddr));
        .
        .
        . ]]>
</programlisting>

<para>Eer zijn een paar dingen die moeten worden opgemerkt:
<parameter>mijn_addr.sin_port</parameter> staat in Netwerk Byte Volgorde.
<parameter>mijn_addr.sin_addr.s_addr</parameter> ook. Iets anders om op
te letten is dat de header files kunnen verschillen van systeeem tot
systeem. Als je zekerheid wil, zul je in je locale <command>man</command>
pages moeten kijken.</para>

<para>Tenslotte, nou we het over <function>bind()</function> hebben,
moet ik je vertellen dat een deel van het proces om je eigen IP
adres en/of poort te verkrijgen kan worden geautomatiseerd:</para>

<programlisting><![CDATA[
        mijn_addr.sin_port = 0; // kies willekeurig een ongebruikte poort
        mijn_addr.sin_addr.s_addr = INADDR_ANY;  // gebruik mijn IP adres ]]>
</programlisting>

<para>Zie je, door <parameter>mijn_addr.sin_port</parameter> op nul te zetten,
geef je <function>bind()</function> de opdracht om een poort voor je te
kiezen. Vergelijkbaar kun je door <parameter>mijn_addr.sin_addr.s_addr</parameter>
op <constant>INADDR_ANY</constant> te zetten, hem automatisch het IP adres
in laten vullen van de machine waar je proces op draait.</para>

<para>Als je op kleine dingetjes let, heb je misschien gezien dat ik
<constant>INADDR_ANY</constant> niet in Netwerk Byte Volgorde heb gezet!
Stout van me. Maar, ik heb zo m'n voorkennis:
<constant>INADDR_ANY</constant> is feitelijk nul! nul blijft nul zelfs
als je de bits door elkaar gooit. Maar, puristen zullen vast zeggen
dat er een parallelle dimensie zou kunnen zijn waar <constant>INADDR_ANY</constant>
misschien wel, zeg, 12 is en dat m'n code daar niet werkt. Dat vind ik
wel best:</para>

<programlisting><![CDATA[
        mijn_addr.sin_port = htons(0); // kies willekeurig een ongebruikte poort
        mijn_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // gebruik mijn IP adres ]]>
</programlisting>

<para>Nou zijn we zo porteerbaar dat het bijna niet meer te geloven is.
Ik wou het je maar even zeggen, aangezien de meeste code die je tegenkomt
niet de moeite neemt om <constant>INADDR_ANY</constant> door
<function>htonl()</function> te halen.</para>

<para><function>bind()</function> geeft ook <constant>-1</constant>
terug bij een fout en geeft <parameter>errno</parameter> de foutwaarde.</para>

<para>Nog iets om voor uit te kijken wanneer je <function>bind()</function>
aanroept: ga niet te laag met je poort nummers. Alle poorten onder 1024
zijn GERESERVEERD (tenzij je de superuser bent)! Je kunt elk poortnummer
daarboven krijgen, tot aan 65535 (vooropgesteld dat ze niet al door een
ander programma worden gebruikt).</para>

<para>Soms zal je opvallen dat wanneer je een server herstart dat
<function>bind()</function> faalt met de kreet "Address already in use"
(adres is reeds in gebruik). Wat betekent dat? Nou, een deel van een
socket dat verbonden was hangt nog rond in de kernel, en houdt de poort
bezet. Je kan &oacute;f wachten tot 'ie weg is (duurt een minuutje of zo),
of je kunt een stukje code aan je programma toevoegen waardoor het
de poort opnieuw kan gebruiken, zo:</para>

<programlisting><![CDATA[
    int yes=1;
    // char='1'; // Solaris mensen gebruiken dit

    // Het bericht "Address already in use" voorkomen
    if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
        perror("setsockopt");
        exit(1);
    } ]]>
</programlisting>

<para>Nog een kleine, echt allerlaatste opmerking over
<function>bind()</function>: er zijn tijden dat je hem niet absoluut
noodzakelijk hoeft aan te roepen. Als je aan het
<function>connect()</function>en bent naar een andere machine en
het zal je worst wezen wat je locale poort is (zoals met
<command>telnet</command>, waar het je alleen maar om de bestemmingspoort
gaat)kun je simpelweg <function>connect()</function>en. Dan zal er
gecontroleerd worden of de socket ongebonden is, en het zonodig
<function>bind()</function>en aan een ongebruikte locale poort.</para>

</sect2>

<!-- ======================================================= -->
<!-- connect -->
<!-- ======================================================= -->
<sect2 id="connect">
<title><function>connect()</function>--H&eacute; jij daar!</title>

<para>Laten we eens even doen alsof je een telnet applicatie bent. Je
gebruiker geeft je de opdracht (net zoals in de film
<emphasis>TRON</emphasis>) een file descriptor te halen. You doet
braaf wat je gezegd wordt en roept <function>socket()</function> aan.
Vervolgens vertelt de gebruiker je om een verbinding te maken met
"<computeroutput>10.12.110.57</computeroutput>" op poort
"<computeroutput>23</computeroutput>" (de standaard telnet poort).
Jeetje! Wat doe je nu?</para>

<para>Gelukkig voor jou, programma, lees je nou net het hoofdstuk over
<function>connect()</function>--hoe een verbinding te maken met een
andere host. Dus lees onverschrokken verder! Geen tijd te verliezen!</para>

<para>De <function>connect()</function> aanroep luidt als volgt:</para>

<programlisting><![CDATA[
    #include <sys/types.h>
    #include <sys/socket.h>

    int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); ]]>
</programlisting>

<para><parameter>sockfd</parameter> is onze goede vriend, de socket
descriptor, zoals teruggegeven door de <function>socket()</function>
aanroep, <parameter>serv_addr</parameter> is een <type>struct
sockaddr</type> die de bestemmingspoort en IP adres bevat, en
<parameter>addrlen</parameter> kan gesteld worden op
<computeroutput>sizeof(struct sockaddr)</computeroutput>.</para>

<para>Begint het al een beetje te dagen? Een voorbeeld:</para>

<programlisting><![CDATA[
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>

    #define DEST_IP   "10.12.110.57"
    #define DEST_PORT 23

    main()
    {
        int sockfd;
        struct sockaddr_in dest_addr;   // voor het bestemmingsadres

        sockfd = socket(AF_INET, SOCK_STREAM, 0); // foutafhandeling!

        dest_addr.sin_family = AF_INET;          // host byte volgorde
        dest_addr.sin_port = htons(DEST_PORT);   // short, netwerk byte volgorde
        dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
        memset(&(dest_addr.sin_zero), '\0', 8);  // de rest van de struct op nul

        // niet vergeten om connect() op fouten te controleren!
        connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr));
        .
        .
        . ]]>
</programlisting>

<para>Wederom, denk erom de return waarde van <function>connect()</function>
te controleren--die geeft <constant>-1</constant> terug bij een fout en
wijzigt de <parameter>errno</parameter> variabele.</para>

<para>Merk ook op dat we <function>bind()</function> niet hebben
aangeroepen. Eigenlijk maakt ons lokale poortnummer ons geen barst uit;
we willen alleen weten waar we naar toe gaan (de bestemmingspoort). De kernel
kiest een lokale poort voor ons, en de site waarnaar we verbinding maken
krijgt deze informatie allemaal automatisch van ons. Geen zorgen.</para>

</sect2>

<!-- ======================================================= -->
<!-- listen -->
<!-- ======================================================= -->
<sect2 id="listen">
<title><function>listen()</function>--Wil iemand me alsjeblieft bellen?</title>

<para>Ok, even wat anders. Wat nou als je niet geen verbinding met een
andere machine wil maken. Zeg maar dat je op inkomende verbindingen
wil wachten en er dan wat mee wil doen. Het proces bestaat uit twee delen:
eerst <function>listen()</function> (luister) je, daarna
<function>accept()</function>eer je (zie verderop).</para>

<para>De <function>listen()</function> aanroep is vrij simpel, maar
vereist wat uitleg:</para>

<programlisting><![CDATA[
    int listen(int sockfd, int backlog); ]]>
</programlisting>

<para><parameter>sockfd</parameter> is de gebruikelijke socket file
descriptor van de <function>socket()</function> systeemaanroep.
<parameter>backlog</parameter> is het aantal toegestane verbindingen
op de inkomende wachtrij. Wat betekent dat? Nou, inkomende
verbindingen zullen in deze rij moeten wachten totdat je
ze <function>accept()</function>eert, en dit is het maximale aantal
dat in de wachtrij mag. De meeste systemen stellen dit stilletjes op
ongeveer 20; je komt ervast wel mee weg als je het op
<constant>5</constant> of <constant>10</constant> instelt.</para>

<para>Wederom, zoals gewoonlijk, geeft <function>listen()</function>
de waarde <constant>-1</constant> terug en wijzigt
<parameter>errno</parameter> bij een fout.</para>

<para>Okee, zoals je je waarschijnlijk wel kunt voorstellen, moeten we
<function>bind()</function> aanroepen voordat we
<function>listen()</function> aanroepen, anders zorgt de kernel ervoor
dat we op een willekeurige poort komen te luisteren. Bleh! Dus als
je van plan bent om naar inkomende verbindingen te luisteren, is de
volgorde om de systeemaanroepen uit te voeren als volgt:</para>

<programlisting><![CDATA[
    socket();
    bind();
    listen();
    /* accept() komt hier */ ]]>
</programlisting>

<para>Ik laat het hierbij, aangezien het redelijk makkelijk te
volgen is. (De code in het <function>accept()</function> hoofdstuk
hieronder is meer compleet.) Het lastigste deel van deze hele
constructie is de aanroep naar <function>accept()</function>.</para>

</sect2>

<!-- ======================================================= -->
<!-- accept -->
<!-- ======================================================= -->
<sect2 id="accept">
<title><function>accept()</function>--"Bedankt voor het bellen met poort
3490."</title>

<para>Hou je vast--de <function>accept()</function> aanroep is wat
eigenaardig! Wat er gaat gebeuren is het volgende: iemand ver weg zal
proberen om te <function>connect()</function>en met je machine op een
poort waarop je aan het <function>listen()</function>en bent. Hun
verbinding zal in de wachtrij worden geplaatst in de hoop
ge<function>accept()</function>eerd te worden. Je roept
<function>accept()</function> aan en geeft opdracht de wachtende
verbinding op te halen. Het geeft je vervolgens een
<emphasis>gloednieuwe socket file descriptor</emphasis> terug voor
het gebruik van deze ene verbinding! Dat klopt, opeens heb je
<emphasis>twee socket file descriptors</emphasis> voor de prijs
van &eacute;&eacute;n! De originele luistert nog steeds op je poort
en de nieuw aangemaakte is eindelijk klaar om te gaan
<function>send()</function>en en <function>recv()</function>.
We zijn er!</para>

<para>De aanroep is als volgt:</para>

<programlisting><![CDATA[
     #include <sys/socket.h>

     int accept(int sockfd, void *addr, int *addrlen); ]]>
</programlisting>

<para><parameter>sockfd</parameter> is de socket descriptor waarop
ge<function>listen()</function>d wordt. Simpel. <parameter>addr</parameter>
is meestal een pointer naar een locale <type>struct sockaddr_in</type>.
Dit is waar de informatie over de inkomende verbinding zal worden
opgeslagen (en waarmee je dus kunt bepalen van welke machine en van welke
poort de inkomende verbinding komt). <parameter>addrlen</parameter> is
een locale integer variabele die op <computeroutput>sizeof(struct
sockaddr_in)</computeroutput> gezet dient te worden voordat z'n adres
wordt doorgegeven aan <function>accept()</function>.
<function>accept()</function> zal niet meer dan zoveel bytes in
<parameter>addr</parameter> proberen op te slaan. Als het er minder
instopt, zal het de waarde van <parameter>addrlen</parameter> aanpassen
om dat weer te geven.</para>

<para>Raad eens? <function>accept()</function> geeft <constant>-1</constant>
terug en past <parameter>errno</parameter> aan als er een fout optreedt.
Dat had je vast nooit zelf verzonnen.</para>

<para>Zoals hiervoor is dit een flinke kluif om &eacute;&eacute;n keer te
verwerken, dus hier is een stukje voorbeeldcode om eens te bestuderen:</para>

<programlisting><![CDATA[
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>

    #define MIJNPOORT 3490    // de poort waar gebruikers verbinding mee maken

    #define BACKLOG 10     // hoeveel openstaande verbindingen de wachtrij mag bevatten

    main()
    {
        int sockfd, new_fd;  // listen()en op sock_fd, nieuwe verbinding op new_fd
        struct sockaddr_in mijn_addr;    // mijn adresinformatie
        struct sockaddr_in hun_addr; // adresinformatie van de 'beller'
        int sin_size;

        sockfd = socket(AF_INET, SOCK_STREAM, 0); // foutafhandeling!

        mijn_addr.sin_family = AF_INET;         // host byte volgorde
        mijn_addr.sin_port = htons(MIJNPOORT);  // short, network byte volgorde
        mijn_addr.sin_addr.s_addr = INADDR_ANY; // automatisch mijn IP invullen
        memset(&(mijn_addr.sin_zero), '\0', 8); // de rest van de struct op nul

        // vergeet je foutafhandeling voor de volgende aanroepen niet:
        bind(sockfd, (struct sockaddr *)&mijn_addr, sizeof(struct sockaddr));

        listen(sockfd, BACKLOG);

        sin_size = sizeof(struct sockaddr_in);
        new_fd = accept(sockfd, (struct sockaddr *)&hun_addr, &sin_size);
        .
        .
        . ]]>
</programlisting>

<para>Nogmaals, let erop dat we de socket descriptor
<parameter>new_fd</parameter> gebruiken voor alle <function>send()</function>
en <function>recv()</function> aanroepen. Als je in de loop van je programma
maar &eacute;&eacute;n hele verbinding krijgt, kan je de luisterende
<parameter>sockfd</parameter> <function>close()</function>n om te voorkomen
dat er meer verbindingen op dezelfde poort komen te staan, als je dat
zou willen.</para>

</sect2>

<!-- ======================================================= -->
<!-- sendrecv -->
<!-- ======================================================= -->
<sect2 id="sendrecv">
<title><function>send()</function> en <function>recv()</function>--Praat
tegen me, schatje!</title>

<para>Deze twee functies zijn bedoeld om mee te communiceren over
stream sockets of verbonden datagram sockets. Als je normale
onverbonden (unconnected) datagram sockets wil gebruiken, moet je
het hoofdstuk over <link linkend="sendtorecv"><function>sendto()</function>
en <function>recvfrom()</function></link>, verderop lezen.</para>

<para>De <function>send()</function> aanroep:</para>

<programlisting><![CDATA[
    int send(int sockfd, const void *msg, int len, int flags); ]]>
</programlisting>

<para><parameter>sockfd</parameter> is de socket descriptor waar je
data naartoe wil sturen (hetzij degene door <function>socket()</function>
teruggegeven, of degene die je van <function>accept()</function> kreeg).
<parameter>msg</parameter> is een pointer naar de data die je wil
versturen, en <parameter>len</parameter> is de lengte van die data
in bytes. Zet <parameter>flags</parameter> maar gewoon op <constant>0</constant>.
(Zie de <function>send()</function> man page voor meer informatie over
de vlaggen (flags).)</para>

<para>Wat voorbeeldcode is bijvoorbeeld:</para>

<programlisting><![CDATA[
    char *msg = "Beej was hier!";
    int len, bytes_verzonden;
    .
    .
    len = strlen(msg);
    bytes_verzonden = send(sockfd, msg, len, 0);
    .
    .
    . ]]>
</programlisting>

<para><function>send()</function> geeft het aantal bytes terug dat
daadwerkelijk werd verstuurd--<emphasis>dit kan minder zijn dat het aantal
dat je verteld had dat verstuurd moest worden!</emphasis> Zie je, soms
geef je opdracht om een hele berg data te versturen terwijl 'ie het
gewoon niet aankan. In dat geval zal er zoveel mogelijk van de data
verstuurd worden, en erop vertrouwd worden dat je de rest later
verstuurd. Onthoud, als de door <function>send()</function> teruggegeven
waarde niet gelijk is aan de waarde van <parameter>len</parameter>, ben
jij verantwoordelijk om de rest van de data te versturen. Het goede nieuws
is dit: Als het pakketje klein is (minder dan 1K of zo), zal het
<emphasis>waarschijnlijk</emphasis> wel lukken om het in z'n geheel
te versturen. Wederom wordt bij een fout <constant>-1</constant> teruggegeven,
en <parameter>errno</parameter> gewijzigd naar het foutnummer.</para>

<para>De <function>recv()</function> aanroep is in veel opzichten
vergelijkbaar:</para>

<programlisting><![CDATA[
    int recv(int sockfd, void *buf, int len, unsigned int flags); ]]>
</programlisting>

<para><parameter>sockfd</parameter> is de socket descriptor waarvan
gelezen wordt, <parameter>buf</parameter> is de buffer waar de data
in wordt opgeslagen, <parameter>len</parameter> is de maximum lengte
van de buffer, en <parameter>flags</parameter> kan wederom op
<constant>0</constant> gezet worden. (Zie de <function>recv()</function>
man page voor vlag (flag) informatie.)</para>

<para><function>recv()</function> geeft het aantal bytes dat daadwerkelijk
in de buffer werd opgeslagen terug, of <constant>-1</constant> bij een
fout (met <parameter>errno</parameter> overeenkomstig gewijzigd).</para>

<para>Wacht! <function>recv()</function> kan <constant>0</constant> teruggeven.
Dit betekent slechts &eacute;&eacute;n ding: de andere kant heeft de verbinding
verbroken! Een return-waarde van <constant>0</constant> is
<function>recv()</function>'s manier om je te laten weten dat dit gebeurd
is.</para>

<para>Nou, kijk eens aan, dat was makkelijk, nietwaar? Nou kan je data
heen en weer sturen over stream sockets! Jippie! Nou ben je een Unix
Netwerk Programmeur!</para>

</sect2>

<!-- ======================================================= -->
<!-- sendtorecv -->
<!-- ======================================================= -->
<sect2 id="sendtorecv">
<title><function>sendto()</function> and
<function>recvfrom()</function>--Zeg wat tegen me, op z'n DGRAM's</title>

<para>"Dit is allemaal wel leuk en aardig", hoor ik je zeggen, "maar
wat moet ik nou aan met onverbonden datagram sockets?" Geen probleem.
Ik weet precies wat je nodig hebt.</para>

<para>Aangezien datagram sockets niet verbonden worden aan een andere
machine, raad eens wat we op moeten geven voordat we een pakketje verzenden?
Precies! Het bestemmingsadres! Zo zit 't:</para>

<programlisting><![CDATA[
    int sendto(int sockfd, const void *msg, int len, unsigned int flags,
               const struct sockaddr *to, int tolen); ]]>
</programlisting>

<para>Zoals je kunt zien is deze aanroep in principe hetzelfde als
de <function>send()</function> aanroep met toevoeging van twee
stukjes informatie. <parameter>to</parameter> is een pointer naar
een <type>struct sockaddr</type> (welke je waarschijnlijk als een
<type>struct sockaddr_in</type> hebt declareert en op het laatste
moment gecast) welke het bestemmings-IP en -poort bevat.
<parameter>tolen</parameter> kan simpelweg op
<computeroutput>sizeof(struct sockaddr)</computeroutput> gezet
worden.</para>

<para>Net zoals met <function>send()</function> geeft
<function>sendto()</function> het aantal bytes terug dat daadwerkelijk
werd verstuurd (welke, wederom, minder kan zijn dan het aantal bytes
dat je had opgegeven om te versturen!), of <constant>-1</constant>
bij een fout.</para>

<para>Ook <function>recv()</function> en <function>recvfrom()</function>
lijken op elkaar. De beschrijving van <function>recvfrom()</function>
is:</para>

<programlisting><![CDATA[
    int recvfrom(int sockfd, void *buf, int len, unsigned int flags,
                 struct sockaddr *from, int *fromlen); ]]>
</programlisting>

<para>Ook hier zijn er overeenkomsten met <function>recv()</function>
met de toevoeging van een aantal extra velden. <parameter>from</parameter>
is een pointer naar een lokale <type>struct sockaddr</type> wat opgevuld
zal worden met het IP adres en poortnummer van de oorspronkelijke machine.

<parameter>fromlen</parameter> is een pointer naar een lokale
<type>int</type> die ge&iuml;nitialiseerd moet worden op
<type>sizeof(struct sockaddr)</type>. Wanneer de functie terugkeert
zal <parameter>fromlen</parameter> de lengte van het adres in
<parameter>from</parameter> bevatten.</para>

<para><function>recvfrom()</function> geeft het aantal ontvangen
bytes terug, of <constant>-1</constant> bij een fout (met
<parameter>errno</parameter> overeenkomstig gewijzigd).</para>

<para>Onthoud, als je een datagram socket <function>connect()</function>,
kun je simpelweg <function>send()</function> en <function>recv()</function>
gebruiken voor je transacties. De socket zelf is nog steeds een
datagram socket en de pakketjes zullen nog steeds UDP gebruiken,
maar de socket interface zal automatisch het bestemmingsadres en
broninformatie voor je toevoegen.</para>

</sect2>

<!-- ======================================================= -->
<!-- closedown -->
<!-- ======================================================= -->

<sect2 id="closedown">

<title><function>close()</function> en
<function>shutdown()</function>--Oprotten!</title>

<para>Pfoe! De hele dag lang hebben je zitten <function>send()</function>en
en <function>recv()</function>en, en nou heb je er genoeg van. Je bent
klaar om de verbinding op je socket descriptor te sluiten. Da's makkelijk.
Je kunt de gewone Unix file descriptor functie <function>close()</function>
gebruiken:</para>

<programlisting><![CDATA[
    close(sockfd); ]]>
</programlisting>

<para>Dit voorkomt verdere lees- en schrijfacties van en naar de socket.
Iedereen die probeert om te lezen van of te schrijven naar de socket
vanaf de andere kant van de verbinding krijgt een foutmelding voor
z'n kiezen.</para>

<para>Mocht je nou wat meer controle willen hebben over hoe de socket
afsluit, dan kun je de <function>shutdown()</function> functie gebruiken.
Hiermee kun je de communicatie in een bepaalde richting afbreken,
of in allebei de richtingen (zoals <function>close()</function> doet).
Beschrijving:</para>

<programlisting><![CDATA[
    int shutdown(int sockfd, int how); ]]>
</programlisting>

<para><parameter>sockfd</parameter> is de socket file descriptor die
je wilt afsluiten, en <parameter>how</parameter> is een van de volgende:</para>

<itemizedlist>

<listitem><para><constant>0</constant> -- Verdere ontvangsten worden
geweigerd</para></listitem>

<listitem><para><constant>1</constant> -- Verdere verzendingen worden
geweigerd</para></listitem>

<listitem><para><constant>2</constant> -- Verdere verzendingen en
ontvangsten worden geweigerd (zoals
<function>close()</function>)</para></listitem>

</itemizedlist>

<para><function>shutdown()</function> geeft <constant>0</constant> terug
bij succes, en <constant>-1</constant> bij een fout (met
<parameter>errno</parameter> onvereenkomstig gewijzigd).</para>

<para>Als je je niet te goed acht om <function>shutdown()</function> op
onverbonden sockets te gebruiken, zal het de socket simpelweg
onbeschikbaar maken voor verdere <function>send()</function> en
<function>recv()</function> aanroepen (denk eraan dat je deze kunt
gebruiken als je je datagram socket maar <function>connect()</function>).</para>

<para>Het is belangrijk om op te merken dat <function>shutdown()</function>
de file descriptor niet feitelijk afsluit--het verandert alleen z'n
bruikbaarheid. Om een socket descriptor vrij te geven moet je
<function>close()</function> gebruiken.</para>

<para>Makkelijk zat.</para>

</sect2>

<!-- ======================================================= -->
<!-- getpeername -->
<!-- ======================================================= -->

<sect2 id="getpeername">
<title><function>getpeername()</function>--Wie ben jij?</title>

<para>Deze functie is zo makkelijk.</para>

<para>Zo makkelijk, dat ik hem bijna z'n eigen hoofdstuk niet heb gegeven.
Maar hier is 'ie toch maar.</para>

<para>De functie <function>getpeername()</function> vertelt je wie er aan
de andere kant van een verbonden socket hangt. De beschrijving:</para>

<programlisting><![CDATA[
    #include <sys/socket.h>

    int getpeername(int sockfd, struct sockaddr *addr, int *addrlen); ]]>
</programlisting>

<para><parameter>sockfd</parameter> is de descriptor van de verbonden
socket, <parameter>addr</parameter> is een pointer naar een
<type>struct sockaddr</type> (of een <type>struct sockaddr_in</type>) waar
de informatie over de andere kant van de verbinding wordt opgeslagen,
en <parameter>addrlen</parameter> is een pointer naar een
<type>int</type>, die ge&iuml;nitialiseerd wordt op
<computeroutput>sizeof(struct sockaddr)</computeroutput>.</para>

<para>Bij een fout geeft de functie <constant>-1</constant> terug en
wijzigt <parameter>errno</parameter> overeenkomstig.</para>

<para>Als je eenmaal hun adres hebt, kun je <function>inet_ntoa()</function>
of <function>gethostbyaddr()</function> gebruiken om het af te drukken
of meer informatie te krijgen. Nee, hun login naam krijg je niet.
(Ok, ok. Als de andere machine een ident daemon heeft draaien, is
dit mogelijk. Maar dat licht buiten het bestek van dit dokument. Kijk
op <ulink url="&rfcpre;1413&rfcpost;">RFC-1413</ulink> voor meer
informatie.)</para>

</sect2>

<!-- ======================================================= -->
<!-- gethostname -->
<!-- ======================================================= -->

<sect2 id="gethostname">
<title><function>gethostname()</function>--Wie ben ik?</title>

<para>Nog makkelijker dan <function>getpeername()</function> is de
<function>gethostname()</function> functie. Dit geeft de naam van
de computer waar je programma op draait terug. De naam kan dan
gebruikt worden met <function>gethostbyname()</function>, verderop,
om het IP adres van je lokale machine te bepalen.</para>

<para>Wat is er nou leuker? Ik zou wel een paar dingen kunnen
bedenken, maar dat heeft niks met socket programmeren te maken.
Hoe dan ook, hier is de beschrijving:</para>

<programlisting><![CDATA[
    #include <unistd.h>

    int gethostname(char *hostname, size_t size); ]]>
</programlisting>

<para>De argumenten zijn eenvoudig: <parameter>hostname</parameter> is
een pointer naar een array van chars die de hostnaam bevatten na
terugkeer van de functie, en <parameter>size</parameter> is de
lengte van de <parameter>hostname</parameter> array in bytes.</para>

<para>De functie geeft <constant>0</constant> terug bij succes,
en <constant>-1</constant> bij een fout, waarbij
<parameter>errno</parameter> zoals gewoonlijk wordt gewijzigd.</para>

</sect2>

<!-- ======================================================= -->
<!-- dns -->
<!-- ======================================================= -->

<sect2 id="dns">
<title>DNS--Jij zegt "www.overheid.nl", ik zeg "193.78.30.5"</title>

<para>Mocht je niet weten wat DNS is, het staat voor "Domain Name
Service (Domein Naam Service)". In een notendop, je vertelt het wat
het leesbare adres is voor een site, en vervolgens geeft het je
het IP adres terug (zodat je het kunt gebruiken met
<function>bind()</function>, <function>connect()</function>,
<function>sendto()</function> of waar jet het ook maar voor nodig hebt).
Op deze manier, wanneer iemand intypt:</para>

<screen>
    <prompt>$</prompt> <command>telnet www.overheid.nl</command>
</screen>

<para>dan zoekt <command>telnet</command> uit dat het moet
<function>connect()</function>en met "193.78.30.5".</para>

<para>Maar hoe werkt het? Je zult de functie
<function>gethostbyname()</function> nodig hebben:</para>

<programlisting><![CDATA[
    #include <netdb.h>
    
    struct hostent *gethostbyname(const char *name); ]]>
</programlisting>

<para>Zoals je ziet geeft het een pointer naar een
<type>struct hostent</type> terug, waarvan de layout als
volgt is:</para>

<programlisting><![CDATA[
    struct hostent {
        char    *h_name;
        char    **h_aliases;
        int     h_addrtype;
        int     h_length;
        char    **h_addr_list;
    };
    #define h_addr h_addr_list[0] ]]>
</programlisting>

<para>En hier volgend de beschrijvingen van de velden in de
<type>struct hostent</type>:</para>

<itemizedlist>
<listitem><para><parameter>h_name</parameter> -- Offici&euml;le naam
van de machine.</para></listitem>

<listitem><para><parameter>h_aliases</parameter> -- Een NULL-geterminateerde
array van alternatieve namen voor de machine.</para></listitem>

<listitem><para><parameter>h_addrtype</parameter> -- Het adrestype
dat teruggegeven wordt; meestal
<parameter>AF_INET</parameter>.</para></listitem>

<listitem><para><parameter>h_length</parameter> -- De lengte van het adres
in bytes.</para></listitem>

<listitem><para><parameter>h_addr_list</parameter> -- Een nulgetermineerde
array van netwerkadressen voor de machine. Machine-adressen staan in
Netwerk Byte Volgorde.</para></listitem>

<listitem><para><parameter>h_addr</parameter> -- Het eerste adres in
<parameter>h_addr_list</parameter>.</para></listitem>

</itemizedlist>

<para><function>gethostbyname()</function> geeft als return-waarde
een pointer naar de gevulde <type>struct hostent</type>, of NULL
bij een fout. (Maar <parameter>errno</parameter> wordt 
<emphasis>niet</emphasis> gewijzigd--in plaats daarvan wordt
<emphasis><parameter>h_errno</parameter></emphasis> gewijzigd. Zie
<function>herror()</function>, verderop.)</para>

<para>Maar hoe wordt het gebruikt? Soms (merken we op als we
computerhandleidingen lezen) is informatie naar het hoofd van de
gebruiker slingeren niet genoeg. Deze functie is zeker
makkelijker te gebruiken dan het lijkt.</para>

<para><ulink url="&samplepre;getip.c">Hier is een
voorbeeldprogramma</ulink>:</para>

<programlisting>
&getip_c;
<!--
/*
** getip.c - een hostnaam 'lookup' demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    struct hostent *h;

    if (argc != 2) {  /* commandoregel op fouten checken */
        fprintf(stderr,"gebruik: getip adres\n");
        exit(1);
    }

    if ((h=gethostbyname(argv[1])) == NULL) {  /* host info ophalen */
        herror("gethostbyname");
        exit(1);
    }

    printf("Hostnaam : %s\n", h->h_name);
    printf("IP Adres : %s\n", inet_ntoa(*((struct in_addr *)h->h_addr)));
   
   return 0;
}
-->
</programlisting>

<para>Met <function>gethostbyname()</function> kun je
<function>perror()</function> niet gebruiken (aangezien
<parameter>errno</parameter> niet gebruikt wordt). Gebruik in plaats
daarvan <function>herror()</function>.</para>

<para>Het is redelijk voordehandliggend. Je geeft de string die de
machinenaam ("www.overheid.nl") bevat door aan <function>gethostbyname()</function>
en grijpt vervolgens de informatie uit de teruggeven
<type>struct hostent</type>.</para>

<para>De enige mogelijke afwijking zou in het afdrukken van het IP
adres kunnen zitten. <parameter>h-&gt;h_addr</parameter> is een
<type>char *</type>, maar <function>inet_ntoa()</function> verwacht
een <type>struct in_addr</type>. Dus cast ik
<parameter>h-&gt;h_addr</parameter> naar een <type>struct in_addr *</type>,
en vervolgens derefereren (dereference) om bij de data te kunnen.</para>

</sect2>

</sect1>  <!-- syscalls -->

<!-- ======================================================= -->
<!-- clientserver -->
<!-- ======================================================= -->

<sect1 id="clientserver">
<title>Client-Server Achtergrond</title>

<para>Het is een client-server wereld, schat. Zo'n beetje alles op
het netwerk gaat over client processen die praten met server processen
en vice-versa. Neem bijvoorbeeld <command>telnet</command>. Wanneer je
verbinding maakt met een andere host op poort 23 met telnet (de client),
wordt een programma op die host (de <command>telnet</command> genoemd,
de server) tot leven geroepen. Het handelt de inkomende telnet verbinding
af, zet je voor een login prompt, etc.</para>

<figure float="0" id="figure2">
<title>Client-Server Interactie.</title>
	<mediaobject>
		<imageobject><imagedata fileref="cs.eps"></imagedata></imageobject>
		<imageobject><imagedata fileref="cs.pdf"></imagedata></imageobject>
		<imageobject><imagedata fileref="cs.gif"></imagedata></imageobject>
		<textobject>
			<phrase>[Client-Server Interactie Diagram]</phrase>
		</textobject>
	</mediaobject>
</figure>

<para>De uitwisseling van informatie tussen client en server is
samengevat in <link linkend="figure2">Figuur 2</link>.</para>

<para>Merk op dat het client-server paar <constant>SOCK_STREAM</constant>
kan praten, of <constant>SOCK_DGRAM</constant>, of wat dan ook (zolang
ze maar het zelfde spreken). Een paar goede voorbeelden van client-server
paren zijn
<command>telnet</command>/<command>telnetd</command>,
<command>ftp</command>/<command>ftpd</command>, of
<command>bootp</command>/<command>bootpd</command>. Iedere keer dat je
<command>ftp</command> gebruikt, is er een programma aan de andere kant,
<command>ftpd</command>, dat je tot dienst staat.</para>

<para>Vaak draait er maar &eacute;&eacute;n server op een machine, en die
server handelt meerdere clients af m.b.v. <function>fork()</function>.
De basisroutine is: de server wacht op een verbinding, <function>accept()</function>
deze, en <function>fork()</function>t een 'child process' (kind proces)
om het af te laten handelen. Dit is wat onze voorbeeldserver doet in
het volgende onderdeel.</para>

<!-- ======================================================= -->
<!-- simpleserver -->
<!-- ======================================================= -->

<sect2 id="simpleserver">
<title>Een eenvoudige Stream Server</title>

<para>Het enige wat deze server doet is het versturen van de string
"<computeroutput>Hallo, Wereld!\n</computeroutput>" over een stream
verbinding. Het enige wat je moet doen om deze server te testen is
het in het een venster te draaien, en ernaartoe telnetten via een
ander met:</para>

<screen>
    <prompt>$</prompt> <command>telnet anderehostnaam 3490</command>
</screen>

<para>Waarbij <computeroutput>anderehostnaam</computeroutput> de naam
is van de machine waarop je de server draait.</para>

<para><ulink url="&samplepre;server.c">De servercode</ulink>:</para>

<programlisting><![CDATA[
/*
** server.c -- een stream socket server demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>


/* de poort waar gebruikers verbinding mee maken */
#define MIJNPOORT 3490

/* hoeveel nog af te handelen verbindingen er in de wachtrij passen */
#define BACKLOG 10


void sigchld_handler(int s)
{
    while (wait(NULL) > 0);
}

int main(void)
{
    int sockfd, new_fd;  /* luisteren op sock_fd, nieuwe verbinding op new_fd */
    struct sockaddr_in mijn_adres;    /* mijn adresinformatie */
    struct sockaddr_in hun_adres; /* connector's adresinformatie */
    int sin_size;
    struct sigaction sa;
    int yes=1;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
        perror("setsockopt");
        exit(1);
    }
    
    mijn_adres.sin_family = AF_INET;         /* host byte volgorde */
    mijn_adres.sin_port = htons(MIJNPOORT);  /* short, netwerk byte volgorde */
    mijn_adres.sin_addr.s_addr = INADDR_ANY; /* automatisch vullen met mijn IP */
    memset(&(mijn_adres.sin_zero), '\0', 8); /* rest van de struct op nul */

    if (bind(sockfd, (struct sockaddr *)&mijn_adres, sizeof(struct sockaddr)) == -1) {
        perror("bind");
        exit(1);
    }

    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }

    sa.sa_handler = sigchld_handler; /* alle dode processen opvegen */
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    while(1) {  // hoofd accept() lus
        sin_size = sizeof(struct sockaddr_in);
        if ((new_fd = accept(sockfd, (struct sockaddr *)&hun_adres, &sin_size)) == -1) {
            perror("accept");
            continue;
        }
        printf("server: verbinding gekregen van %s\n",inet_ntoa(hun_adres.sin_addr));
        if (!fork()) { /* dit is het kind proces */
            close(sockfd); /* kind heeft de listener niet nodig */
            if (send(new_fd, "Hallo, wereld!\n", 15, 0) == -1)
                perror("send");
            close(new_fd);
            exit(0);
        }
        close(new_fd);  /* ouder heeft deze niet nodig */
    }

    return 0;
}
]]>
</programlisting>

<para>Mocht je nieuwsgierig zijn, ik heb de code in &eacute;&eacute;n grote
<function>main()</function> functie gestopt om de syntaxis een beetje
helder te houden (hoop ik). Voel je vrij om het in kleinere functies te
verdelen als je dat wil.</para>

<para>Het hele <function>sigaction()</function> gebeuren is misschien ook
nieuw voor je--da's niet erg. De code die er staat is verantwoordelijk voor
het oogsten van zombie processen die tevoorschijn komen als de 
ge<function>fork()</function>te kind processen afsluiten. Als je heel veel
zombies genereert en ze niet opruimt, krijg je je systeembeheerder op je
dak.</para>

<para>Je kunt de data van deze server krijgen door de client te gebruiken die
in het volgende onderdeeel staat.</para>

</sect2>

<!-- ======================================================= -->
<!-- simpleclient -->
<!-- ======================================================= -->

<sect2 id="simpleclient">
<title>Een Eenvoudige Stream Client</title>

<para>Dit geval is nog makkelijker dan de server. Alles wat 'ie doet
is verbinding maken met de host die je aangeeft op de commandoregel, op
poort 3490. Het haalt de string op die de server verstuurt</para>

<para><ulink url="&samplepre;client.c">De source voor de client</ulink>:</para>

<programlisting><![CDATA[
/*
** client.c -- een stream socket client demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>


/* de poort waar de client verbinding mee maakt */
#define POORT 3490

/* max. aantal bytes dat we per keer kunnen ophalen */ 
#define MAXDATASIZE 100


int main(int argc, char *argv[])
{
    int sockfd, aant_bytes;  
    char buf[MAXDATASIZE];
    struct hostent *he;
    struct sockaddr_in hun_adres; /* connector's adresinformatie */

    if (argc != 2) {
        fprintf(stderr,"gebruik: client hostnaam\n");
        exit(1);
    }

    if ((he = gethostbyname(argv[1])) == NULL) {  /* host info ophalen */
        perror("gethostbyname");
        exit(1);
    }

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    hun_adres.sin_family = AF_INET;    /* host byte volgorde */
    hun_adres.sin_port = htons(POORT); /* short (16 bit), netwerk byte volgorde */ 
    hun_adres.sin_addr = *((struct in_addr *)he->h_addr);
    memset(&(hun_adres.sin_zero), '\0', 8);  /* rest van de struct op nul */

    if (connect(sockfd, (struct sockaddr *)&hun_adres, sizeof(struct sockaddr)) == -1) {
        perror("connect");
        exit(1);
    }

    if ((aant_bytes = recv(sockfd, buf, MAXDATASIZE - 1, 0)) == -1) {
        perror("recv");
        exit(1);
    }

    buf[aant_bytes] = '\0';

    printf("Ontvangen: %s",buf);

    close(sockfd);

    return 0;
}
]]>
</programlisting>

<para>Merk op dat als je de server niet start voordat je de client draait,
<function>connect()</function> de melding "Connection refused"
(Verbinging geweigerd) teruggeeft. Erg handig.</para>

</sect2>

<!-- ======================================================= -->
<!-- datagram -->
<!-- ======================================================= -->

<sect2 id="datagram">
<title>Datagram Sockets</title>

<para>Eigenlijk heb ik hier weinig over te zeggen, dus laat ik maar
gewoon een paar voorbeelprogramma's zien:
<filename>talker.c</filename> en <filename>listener.c</filename>.</para>

<para><command>listener</command> zit op een machine te wachten op
binnenkomende pakketjes op poort 4950. <command>talker</command> verstuurt
een pakketje naar de poort op de aangegeven machine, met daarin wat de
gebruiker ook maar op de commandoregel intikt.</para>

<para>Hier is de <ulink url="&samplepre;listener.c">source voor
<filename>listener.c</filename></ulink>:</para>

<programlisting><![CDATA[
/*
** listener.c -- een datagram socket "server" demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/* de poort waar gebruikers verbinging mee maken */
#define MIJNPOORT 4950

#define MAXBUFLEN 100

int main(void)
{
    int sockfd;
    struct sockaddr_in mijn_adres; /* mijn adresinformatie */
    struct sockaddr_in hun_adres;  /* connector's adresinformatie */
    int adres_len, aant_bytes;
    char buf[MAXBUFLEN];

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    mijn_adres.sin_family = AF_INET;         /* host byte volgorde */
    mijn_adres.sin_port = htons(MIJNPOORT);  /* short, netwerk byte volgorde */
    mijn_adres.sin_addr.s_addr = INADDR_ANY; /* automatisch vullen met mijn IP */
    memset(&(mijn_adres.sin_zero), '\0', 8); /* rest van de struct op nul */

    if (bind(sockfd, (struct sockaddr *)&mijn_adres,
		sizeof(struct sockaddr)) == -1) {
        perror("bind");
        exit(1);
    }

    adres_len = sizeof(struct sockaddr);
    if ((aant_bytes = recvfrom(sockfd, buf, MAXBUFLEN - 1 , 0,
		(struct sockaddr *)&hun_adres, &adres_len)) == -1) {
        perror("recvfrom");
        exit(1);
    }

    printf("pakket gekregen van %s\n",inet_ntoa(hun_adres.sin_addr));
    printf("pakket is %d bytes lang\n",aant_bytes);
    buf[aant_bytes] = '\0';
    printf("pakket bevat \"%s\"\n",buf);

    close(sockfd);

    return 0;
}
]]>
</programlisting>

<para>Merk op dat we in de aanroep naar <function>socket()</function>
eindelijk <constant>SOCK_DGRAM</constant> gebruiken. Merk ook op dat
het niet nodig is om te <function>listen()</function>en of te
<function>accept()</function>en. Dit is een van die extra's van het
gebruik van ongebonden datagram sockets!</para>

<para>Vervolgens komt de <ulink url="&samplepre;talker.c">source voor
<filename>talker.c</filename></ulink>:</para>

<programlisting><![CDATA[
/*
** talker.c -- een datagram "client" demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

/* de poort waar gebruikers verbinding mee maken */
#define MIJNPOORT 4950

int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in hun_adres; /* connector's adresinformatie */
    struct hostent *he;
    int aant_bytes;

    if (argc != 3) {
        fprintf(stderr,"gebruik: talker hostnaam bericht\n");
        exit(1);
    }

    if ((he=gethostbyname(argv[1])) == NULL) {  /* host info ophalen */
        perror("gethostbyname");
        exit(1);
    }

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    hun_adres.sin_family = AF_INET;        /* host byte volgorde */
    hun_adres.sin_port = htons(MIJNPOORT); /* short, netwerk byte volgorde */
    hun_adres.sin_addr = *((struct in_addr *)he->h_addr);
    memset(&(hun_adres.sin_zero), '\0', 8);  /* rest van de struct op nul */

    if ((aant_bytes = sendto(sockfd, argv[2], strlen(argv[2]), 0,
		     (struct sockaddr *)&hun_adres, sizeof(struct sockaddr))) == -1) {
        perror("sendto");
        exit(1);
    }

    printf("%d bytes verzonden naar %s\n", aant_bytes, inet_ntoa(hun_adres.sin_addr));

    close(sockfd);

    return 0;
}
]]>
</programlisting>

<para>En da's alles! Draai <command>listener</command> op een of andere
machine, en <command>talker</command> op een andere. Kijk ze eens
communiceren! Pret en vermaak voor de hele nucleaire familie!</para>

<para>Op een piepklein stukje detail na dan, dat ik al vele malen
eerder heb verteld: verbonden datagram sockets. Ik moet het er hier
over hebben, aangezien we in het datagram onderdeel van het document
zijn. Laten we eens zeggen dat <command>talker</command> de
functie <function>connect()</function> aanroept en het adres van de
<command>listener</command> specificeert. Vanaf dat moment mag de
<command>talker</command> alleen verzenden naar en ontvangen van
het bij de <function>connect()</function> aanroep opgegeven adres.
Om deze reden hoef je <function>sendto()</function> en
<function>recvfrom()</function> niet te gebruiken; je gebruikt
simpelweg <function>send()</function> en <function>recv()</function>.</para>

</sect2>

</sect1> <!-- clientserver -->

<!-- ======================================================= -->
<!-- advanced -->
<!-- ======================================================= -->

<sect1 id="advanced">
<title>Enigszins Geavanceerde Technieken</title>

<para>Deze zijn niet <emphasis>echt</emphasis> geavanceerd, maar
ze liggen voorbij de reeds behandelde basisstof. Eigenlijk, als
je tot hier bent gekomen, mag je jezelf als redelijk bedreven
beschouwen in de basics van Unix netwerk programmeren!
Gefeliciteerd!</para>

<para>Dus hier gaan we dapper de nieuwe wereld binnen van de wat
esoterischer zaken die je zou willen leren over sockets. Flink
zijn!</para>

<!-- ======================================================= -->
<!-- blocking -->
<!-- ======================================================= -->

<sect2 id="blocking">
<title>Blocking</title>

<para>Blokkering ("Blocking"). Je hebt erover gehoord--maar wat is het
nou eigenlijk? In een notendop, "block" is techie jargon voor "slaap".
Je hebt vast al wel opgemerkt dat wanneer je <command>listener</command>
draait, het daar maar een beetje zit te wachten tot er een pakketje komt.
Wat er gebeurt is dat het <function>recvfrom()</function> aanroept terwijl
er nog geen data was, en zodoende wordt er van <function>recvfrom()</function>
gezegd dat 'ie "blockt" (slapen dus), totdat er data arriveert.</para>

<para>Veel functies blokkeren. <function>accept()</function> blokkeert.
Alle <function>recv()</function> functies blokkeren. De reden waarom
ze dit doen is omdat ze dat mogen. Wanneer je de socket descriptor aanmaakt
met <function>socket()</function>, zet de kernel deze op "blocking".
Als je niet wilt dat een socket blokkeert, moet je een aanroep maken
naar <function>fcntl()</function>:</para>

<programlisting><![CDATA[
    #include <unistd.h>
    #include <fcntl.h>
    .
    .
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    fcntl(sockfd, F_SETFL, O_NONBLOCK);
    .
    . ]]>
</programlisting>

<para>Door een socket op "non-blocking" te zetten kun je de socket
peilen ("poll") voor data. Als je probeert te lezen van een
non-blocking socket en er staat geen data te wachten, mag de socket
niet blokkeren--het geeft dan direct <constant>-1</constant> terug
en zet <parameter>errno</parameter> op
<constant>EWOULDBLOCK</constant>.</para>

<para>Algemeen gezegd, echter, is deze manier van "polling" een slecht
idee. Als je je programma in een bezig-wacht (busy-wait) zet om data
van de socket te halen, vreet het CPU-tijd op als een gek. Een meer
elegante oplossing voor het controleren of er ergens data te lezen valt
komt in het volgende onderdeel aan bod bij <function>select()</function>.</para>

</sect2>

<!-- ======================================================= -->
<!-- select -->
<!-- ======================================================= -->

<sect2 id="select">
<title><function>select()</function>--Synchrone I/O Multiplexing</title>

<para>Deze functie is ietwat eigenaardig, maar erg nuttig. Neem de volgende
situatie: je bent een server en je wil luisteren naar inkomende connecties
maar ook blijven lezen van de connecties die je al hebt.</para>

<para>Geen probleem, zeg je, gewoon een <function>accept()</function>
en een paar <function>recv()</function>s. Niet zo snel, knul! Wat nou
als je aan het blocken bent op een <function>accept()</function>
aanroep? Hoe ga je tegelijkertijd data <function>recv()</function>en?
"Non-blocking sockets gebruiken!" Echt niet! Je wil de CPU niet de
hele tijd bezig houden. Maar wat dan?</para>

<para><function>select()</function> geeft je de kracht om verscheidene
sockets tegelijkertijd in de gaten te houden. Het vertelt je welke
klaar zijn om van gelezen te worden, welke klaar zijn om naar te schrijven,
en welke sockets uitzonderingen ("exceptions") hebben gegenereerd, als je
dat echt wil weten.</para>

<para>Zonder verder gedraal geef ik je hier de beschrijving van
<function>select()</function>:</para>

<programlisting><![CDATA[
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int numfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout); ]]>
</programlisting>

<para>De functie houdt verzamelingen ("sets") van file descriptors
in de gaten; in het bijzonder <parameter>readfds</parameter>,
<parameter>writefds</parameter> en <parameter>exceptfds</parameter>.
Als je wil weten of je van standaard invoer (standard input, "stdin")
en een of andere socket descriptor <parameter>sockfd</parameter>
kunt lezen, voeg dan de file descriptors <constant>0</constant> en
<parameter>sockfd</parameter> toe aan de verzameling
<parameter>readfds</parameter>. De parameter <parameter>numfds</parameter>
moet de waarde van de hoogste file descriptor plus &eacute;&eacute;n
krijgen. In dit voorbeeld moet het dus op <parameter>sockfd+1</parameter>
ingesteld worden, aangezien dit gegarandeerd hoger is dan stdin
(<constant>0</constant>).</para>

<para>Wanneer <function>select()</function> terugkeert, wordt
<parameter>readfds</parameter> aangepast om te reflecteren welke
van de file descriptors die je hebt geselecteerd, klaar is om
van te lezen. Je kunt het testen met de macro
<function>FD_ISSET()</function>, zoals verderop.</para>

<para>Voordat we echt verder gaan wil ik het even hebben over hoe
je deze verzamelingen kunt manipuleren. Elke verzameling is van het
type <type>fd_set</type>. De volgende macro's opereren over dit
type:</para>

<itemizedlist>

<listitem><para><function>FD_ZERO(fd_set *set)</function> -- wist een
file descriptor set</para></listitem>

<listitem><para><function>FD_SET(int fd, fd_set *set)</function> -- voegt
<parameter>fd</parameter> toe aan de set</para></listitem>

<listitem><para><function>FD_CLR(int fd, fd_set *set)</function> --
verwijdert <parameter>fd</parameter> uit de set</para></listitem>

<listitem><para><function>FD_ISSET(int fd, fd_set *set)</function> --
Test om te bepalen of <parameter>fd</parameter> in de verzameling
zit</para></listitem>

</itemizedlist>

<para>Tenslotte, wat is dit vage type <type>struct timeval</type>? Nou,
soms wil je geen eeuwigheid wachten tot iemand je data stuurt. Misschien
wil je eens in de 96 seconden een "Nog bezig..." berichtje afdrukken
naar de terminal terwijl er nog niks gebeurd is. Deze tijdsstructuur
stelt je in staat om een "timeout" periode aan te geven. Als de tijd
verstreken is en <function>select()</function> heeft nog steeds geen
file descriptors die klaar zijn, keert hij terug zodat je verder
kunt.</para>

<para>De <type>struct timeval</type> heeft de volgende velden:</para>

<programlisting><![CDATA[
    struct timeval {
        int tv_sec;     // seconden
        int tv_usec;    // microseconden
    }; ]]>
</programlisting>

<para>Zet simpelweg <parameter>tv_sec</parameter> op het aantal te wachten
seconden, en zet <parameter>tv_usec</parameter> op het aantal te wachten
microseconden. Ja, <emphasis>micro</emphasis>seconden dus, niet
milliseconden. Er zitten 1.000 microseconden in een milliseconde, en
1.000 milliseconden in een seconde. Dus zitten er 1.000.000 microseconden
in een seconde. Maar waarom "usec"? De "u" moet een beetje op de griekse
letter &mu; (Mu) lijken die we gebruiken voor "micro". Verder, wanneer de
functie terugkeert, <emphasis>zou</emphasis> <parameter>timeout</parameter>
bijgewerkt kunnen zijn om de resterende tijd weer te geven. Dit hangt af
van de Unix-smaak die je draait.</para>

<para>Joepie! We hebben een microseconde resolutie timer! Nou, reken er
maar niet op. Standaard Unix "timeslices" zijn zo rond de 100
milliseconden, dus dikke kans dat je minstens zo lang moet wachten,
hoe klein je je <type>struct timeval</type> ook instelt.</para>

<para>Andere zaken van belang: Als je de velden in je <type>struct
timeval</type> instelt op <constant>0</constant>, genereert
<function>select()</function> meteen een time-out, daarmee effectief
alle file descriptors in je verzamelingen peilend. Als je de
parameter <parameter>timeout</parameter> op NULL instelt, zal er nooit
een timeout gegenereert worden, waardoor er gewacht zal worden tot er
een file descriptor vrijkomt. Tenslotte, als het je niet uitmaakt of
je moet wachten op een bepaalde verzameling, kun je deze ook op NULL
zetten in de aanroep naar <function>select()</function>.</para>

<para><ulink url="&samplepre;select.c">Het volgende stukje code</ulink> wacht 2.5 seconden
tot er iets op de standaard invoer verschijnt:</para>

<programlisting><![CDATA[
/*
** select.c -- een select() demo
*/

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

/* file descriptor voor standaard invoer */
#define STDIN 0

int main(void)
{
    struct timeval tv;
    fd_set readfds;

    tv.tv_sec = 2;
    tv.tv_usec = 500000;

    FD_ZERO(&readfds);
    FD_SET(STDIN, &readfds);

    /* writefds en exceptfds zijn niet van belang */
    select(STDIN+1, &readfds, NULL, NULL, &tv);

    if (FD_ISSET(STDIN, &readfds))
        printf("Een toets werd ingedrukt!\n");
    else
        printf("Timeout.\n");

    return 0;
}
]]>
</programlisting>

<para>Als je op een "line-buffered terminal" zit, zul je op RETURN moeten
drukken, anders krijg je alsnog een timeout voor je kiezen.</para>

<para>Misschien denken sommigen van jullie nu dat dit een geweldige manier
is om op data te wachten op een datagram socket--en je hebt gelijk: het
zou <emphasis>kunnen</emphasis>. Sommige Unices kunnen
<function>select()</function> op deze manier gebruiken, en sommige niet.
Je zou moeten kijken wat je locale man page hier over te zeggen heeft
als je het wil proberen.</para>

<para>Sommige Unices updaten de tijd in je <type>struct timeval</type>
om de nog te resterende tijd tot een timeout weer te geven. Maar andere
weer niet. Vertrouw daar maar niet op als je portable wil zijn.
(Gebruik <function>gettimeofday()</function> als je de verlopen tijd in
de gaten wil houden. 't Zuigt, ik weet het, maar zo is het nou
eenmaal.)</para>

<para>Wat gebeurt er als een socket in de leesverzameling de verbinding
sluit? In dat geval keert <function>select()</function> terug met die
socket descriptor ingesteld als "klaar om te lezen". Wanneer je er dan
eenmaal van <function>recv()</function>t, geeft <function>recv()</function>
de waarde <constant>0</constant> terug. Op die manier weet je dat de
client de verbinding heeft verbroken.</para>

<para>Nog een laatste opmerking over <function>select()</function>: als
je een socket hebt dat aan het <function>listen()</function>en is,
kun je controleren of er een nieuwe verbinding is door de file descriptor
van die socket in de <parameter>readfds</parameter>-verzameling te
zetten.</para>

<para>En dat, vrienden, is een snelle blik op de almachtige
<function>select()</function> functie.</para>

<para>Maar, op verzoek volgt hier een uitgebreid voorbeeld.
Helaas is het verschil tussen het uiterst simpele voorbeeld hierboven en
het voorbeeld hieronder significant. Maar kijk maar eens, en lees dan
de beschrijving die er op volgt.</para>

<para><ulink url="&samplepre;selectserver.c">Dit programma</ulink>
gedraagt zich als een simpele multi-user chat server. Start het in
een venster, en <command>telnet</command> er naartoe
(<command>telnet hostname 9034</command>) vanuit meerdere andere
vensters.. Wanneer je iets typt in een <command>telnet</command>-sessie,
zou het moeten verschijnen alle andere vensters.</para>

<programlisting><![CDATA[
/*
** selectserver.c -- een afgezaagde multi-user chat server
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define POORT 9034   /* de poort waar we op luisteren */

int main(void)
{
    fd_set master;   /* hoofd file descriptor lijst */
    fd_set read_fds; /* tijdelijke file descriptor lijst voor select() */
    struct sockaddr_in mijn_adres;  /* server adres */
    struct sockaddr_in hun_adres;   /* client adres */
    int fdmax;        /* maximum file descriptor nummer */
    int listener;     /* luisterende socket descriptor */
    int newfd;        /* nieuwe ge-accept()eerde socket descriptor */
    char buf[256];    /* buffer voor client data */
    int aant_bytes;
    int yes=1;        /* voor setsockopt() SO_REUSEADDR, verderop */
    int adres_len;
    int i, j;

    FD_ZERO(&master);    /* de hoofd en tijdelijke sets op nul */
    FD_ZERO(&read_fds);

    /* luistersocket ophalen */
    if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    /* hinderlijke "address already in use" foutmelding verhelpen */
    if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes,
                                                        sizeof(int)) == -1) {
        perror("setsockopt");
        exit(1);
    }

    /* bind */
    mijn_adres.sin_family = AF_INET;
    mijn_adres.sin_addr.s_addr = INADDR_ANY;
    mijn_adres.sin_port = htons(POORT);
    memset(&(mijn_adres.sin_zero), '\0', 8);
    if (bind(listener, (struct sockaddr *)&mijn_adres, 
			    sizeof(mijn_adres)) == -1) {
        perror("bind");
        exit(1);
    }

    /* listen */
    if (listen(listener, 10) == -1) {
        perror("listen");
        exit(1);
    }

    /* luistersocket aan hoofdset toevoegen */
    FD_SET(listener, &master);

    /* grootste file descriptor bijhouden */
    fdmax = listener; /* tot nu toe is het deze */

    // hoofdlus
    for(;;) {
        read_fds = master; /* kopieren */
        if (select(fdmax + 1, &read_fds, NULL, NULL, NULL) == -1) {
            perror("select");
            exit(1);
        }

        /* bestaande verbindingen doorzoeken voor te lezen data */
        for(i = 0; i <= fdmax; i++) {
            if (FD_ISSET(i, &read_fds)) { /* we hebben er een! */
                if (i == listener) {
                    /* nieuwe verbindingen verwerken */
                    adres_len = sizeof(hun_adres);
                    if ((newfd = accept(listener, (struct sockaddr *)&hun_adres,
                                                               &adres_len)) == -1) { 
                        perror("accept");
                    } else {
                        FD_SET(newfd, &master); /* aan hoofdset toevoegen */
                        if (newfd > fdmax) {    /* maximum in de gaten houden */
                            fdmax = newfd;
                        }
                        printf("selectserver: nieuwe verbinding van %s op "
                            "socket %d\n", inet_ntoa(hun_adres.sin_addr), newfd);
                    }
                } else {
                    /* data van client verwerken */
                    if ((aant_bytes = recv(i, buf, sizeof(buf), 0)) <= 0) {
						/* fout, of client sloot verbinding */
			if (aant_bytes == 0) {
                            /* verbinding gesloten */
                            printf("selectserver: socket %d hing op\n", i);
                        } else {
                            perror("recv");
                        }
                        close(i); /* doei! */
                        FD_CLR(i, &master); /* uit hoofdset verwijderen */
                    } else {
						/* we kregen data van een client */
                        for(j = 0; j <= fdmax; j++) {
                            /* versturen naar iedereen! */
                            if (FD_ISSET(j, &master)) {
                                /* behalve de 'listener' en onszelf */
                                if (j != listener && j != i) {
                                    if (send(j, buf, aant_bytes, 0) == -1) {
                                        perror("send");
                                    }
                                }
                            }
                        }
                    }
                } /* dit is ZO LELIJK! */
            }
        }
    }
    
    return 0;
}
]]>
</programlisting>

<para>Merk op dat ik twee file descriptor sets in de code heb:
<parameter>master</parameter> en <parameter>read_fds</parameter>. De
eerste, <parameter>master</parameter>, bevat alle socket descriptors die
momenteel verbonden zijn, evenals de socket descriptor die luistert naar
nieuwe verbindingen.</para>

<para>De reden waarom ik de <parameter>master</parameter> set gebruik is
omdat <function>select()</function> <emphasis>wijzigingen</emphasis>
aanbrengt in de set die je aan hem meegeeft om aan te kunnen geven van
welke sockets gelezen kan worden. Aangezien ik de verbindingen in de
gaten moet houden van de ene <function>select()</function> aanroep op
de andere, moet ik ze ergens veilig opslaan. Op het laatste moment
copi&euml;er ik de <parameter>master</parameter> set naar de
<parameter>read_fds</parameter> set, en roep dan
<function>select()</function> aan.</para>

<para>Maar betekent dat niet dat, telkens als ik een nieuwe verbinding
krijg, ik die toe moet voegen aan de <parameter>master</parameter> set?
Juist! En telkens als een verbinding sluit, moet ik hem verwijderen
uit de <parameter>master</parameter> set? Ja, ook dan.</para>

<para>Merk op dat ik controleer of de <parameter>listener</parameter>
klaar is om van gelezen te worden.Wanneer dat het geval is, betekent dat
dat er een nieuwe verbinding staat te wachten, en ik
<function>accept()</function>eer deze en voeg hem toe aan de
<parameter>master</parameter> set. Vergelijkbaar, wanneer een client
verbinding klaar is om van gelezen te worden, en <function>recv()</function>
geeft <constant>0</constant> terug, dan weet ik dat de client de verbinding
heeft gesloten, en moet ik deze van de <parameter>master</parameter> set
verwijderen.</para>

<para>Als de client <function>recv()</function> een niet-nul teruggeeft,
echter, weet ik dat enige data is ontvangen. Dus haal ik het op, en loop
dan door de <parameter>master</parameter> lijst en verstuur die data naar
de rest van alle clients.</para>

<para>En dat, vrienden, is een minder-dan-simpele beschouwing van de
almachtige <function>select()</function> functie.</para>

</sect2>

<!-- ======================================================= -->
<!-- sendall -->
<!-- ======================================================= -->

<sect2 id="sendall">
<title>Omgaan met parti&euml;le <function>send()</function>s</title>

<para>Herinner je terug in de <link linkend="sendrecv">sectie over
<function>send()</function></link> waar ik zei dat <function>send()</function>
mogelijk niet alle bytes verstuurt die je had aangegeven? Dat wil zeggen,
dat je wil dat er 512 bytes verstuurd worden, maar hij geeft 412 terug.
Wat is er met de laatste 100 bytes gebeurd?</para>

<para>Nou, die staan nog steeds in je buffer te wachten om verstuurd te
worden. Door omstandigheden buiten jouw controle heeft de kernel besloten
om niet alle data in &eacute;&eacute;n hap te versturen. En nu is het aan jou
om het laatste beetje de deur uit te helpen.</para>


<para>Je zou ook een functie kunnen schrijven die het voor je doet:</para>

<programlisting><![CDATA[
    #include <sys/types.h>
    #include <sys/socket.h>

    int sendall(int s, char *buf, int *len)
    {
        int totaal = 0;        // aantal verzonden bytes
        int bytesover = *len; // hoeveel we nog moeten
        int n;

        while(totaal < *len) {
            n = send(s, buf+totaal, bytesover, 0);
            if (n == -1) { break; }
            totaal += n;
            bytesover -= n;
        }

        *len = totaal; // werkelijk verzonden aantal teruggeven

        return n==-1?-1:0; // return -1 bij fout, 0 bij succes
    } ]]>
</programlisting>

<para>In dit voorbeeld is <parameter>s</parameter> de socket waarnaar
je de data wilt versturen, <parameter>buf</parameter> is de buffer
met de data, en <parameter>len</parameter> is een pointer naar een
<type>int</type> welke het aantal bytes dat in de buffer zit bevat.</para>

<para>De functie geeft <constant>-1</constant> terug bij een fout (en
<parameter>errno</parameter> bevat nog steeds de waarde die door
<function>send()</function> werd gezet). Ook wordt in
<parameter>len</parameter> het aantal verzonden bytes teruggegeven. Dit zal
hetzelfde zijn als het aantal bytes wat je had aangegeven, tenzij er een
fout is opgetreden. <function>sendall()</function> zal z'n best doen,
hijgend en puffend, om de data de deur uit te doen, maar als er een fout
optreedt, komt die alsnog terug.</para>

<para>Ten overvloede is hier een voorbeeld van de functieaanroep:</para>

<programlisting><![CDATA[
    char buf[10] = "Beej!";
    int len;

    len = strlen(buf);
    if (sendall(s, buf, &len) == -1) {
        perror("sendall");
        printf("We hebben slechts %d bytes verstuurd door de fout!\n", len);
    } ]]>
</programlisting>

<para>Wat gebeurt er aan de kant van de ontvanger als er een deel van een
pakketje binnenkomt? Als de pakketjes van variabele lengte zijn, hoe weet
de ontvanger dan waar het ene pakketje begint en de andere eindigt? Yep,
de werkelijkheid doet pijn. Je zult de boel waarschijnlijk moeten
<emphasis>inkapselen</emphasis> (weet je nog, in de sectie over
<link linkend="lowlevel">data inkapsulatie</link> helemaal in het begin?).
Lees verder voor de details!</para>

</sect2>

<!-- ======================================================= -->
<!-- sonofdataencap -->
<!-- ======================================================= -->

<sect2 id="sonofdataencap">
<title>Data Inkapsulatie</title>

<para>Wat betekent het inkapselen van data nou eigenlijk? In het eenvoudigste
geval betekent het dat je een kop (<emphasis>header</emphasis>) op de data
plakt met of wat indentificerende informatie, of een pakket lengte, of
beide.</para>

<para>Hoe zou je header eruit moeten zien? Nou, het is eigenlijk alleen maar
wat binaire data die representeert wat jij noodzakelijk acht voor je
project.</para>

<para>Wow. Da's vaag.</para>

<para>Okee. Stel bijvoorbeeld dat je een multi-user chat programma hebt dat
<constant>SOCK_STREAM</constant>s gebruikt. Wanneer een gebruiker iets typt
("zegt"), zijn er twee stukjes informatie die naar de server verzonden moeten
worden: wat er gezegd werd, en door wie.</para>

<para>Tot zover alles nog in orde? "Wat is het probleem?" hoor ik je
vragen.</para>

<para>Het probleem is dat de berichten van variabele lengte kunnen zijn.
Iemand die "tom" heet zou "Hoi" kunnen zeggen, en iemand anders die "Benjamin"
heet zou "Hee jongens, hoe is 't?" kunnen zeggen.</para>

<para>Dus je <function>send()</function> dit alles naar de clients zoals het
binnenkomt. Je uitgaande datastroom ziet er dan zo uit:</para>

<screen>
    t o m H o i B e n j a m i n H e e j o n g e n s , h o e i s ' t ?
</screen>

<para>Enzovoorts. Hoe weet de client waar het ene bericht begint en de andere
stopt? Je zou alle berichten even lang kunnen maken en
<function>sendall()</function> zoals we deze hebben
<link linkend="sendall">hierboven</link> hebben ge&iuml;mplementeerd aan kunnen
roepen. Maar dan verspil je bandbreedte! We willen geen 1024 bytes 
<function>send()</function>en alleen maar zodat "tom" "Hoi" kan
zeggen.</para>

<para>Dus <emphasis>inkapsuleren</emphasis> we de data in een kleine header
en pakket structuur. Zowel de client als de server weten hoe ze deze data
kunnen in- en uitpakken (soms ook wel "marshal" en "unmarshal" genoemd).
Geloof het of niet, maar we staan op het punt om een
<emphasis>protocol</emphasis> te defini&euml;ren dat beschrijft hoe een client
en server communiceren!</para>

<para>Laten we in dit geval eens aannemen dat de gebruikersnaam een vaste
lengte heeft van 8 karakters, opgevuld met <constant>'\0'</constant>. En
laten we aannemen dat de data van variabele lengte is, met een maximum lengte
van 128 karakters. Laten we eens kijken naar een voorbeeld pakketstructuur die
we in deze situatie zouden kunnen gebruiken:</para>

<orderedlist>

<listitem><para><computeroutput>len</computeroutput> (1 byte, unsigned)
-- De totale lengte van het pakket, inclusief de 8-byte naam en de chat data.
</para></listitem>

<listitem><para><computeroutput>name</computeroutput> (8 bytes) -- De
gebruikersnaam, met NULlen opgevuld indiend nodig.</para></listitem>

<listitem><para><computeroutput>chatdata</computeroutput>
(<emphasis>n</emphasis>-bytes) -- De data zelf, niet meer dan 128
bytes. De lengte van het pakket zou dan de lengte van deze data zijn, plus
8 (de lengte van het naamveld).</para></listitem>

</orderedlist>

<para>Waarom ik heb gekozen voor de 8-byte en 128-byte limieten voor de
velden? Die heb ik uit de lucht gegrepen, aangenomen dat ze lang genoeg
zijn. Misschien is 8 bytes te krap voor je behoeften, en neem je een
30-byte veld voor de naam, maakt niet uit. De keuze is aan jou.</para>

<para>Met bovenstaande pakketdefinitie zou het eerste pakket de volgende
informatie bevatten (in hex en ASCII):</para>


<screen>
      0B     74 6F 6D 00 00 00 00 00      48 6F 69
   (lengte)  T  o  m    (vulling)         H  o  i
</screen>

<para>Het tweede is vergelijkbaar:</para>

<screen>
      14     42 65 6E 6A 61 6D 69 6E      48 65 65 20 6A 6F 6E 67 65 6E ...
   (lengte)  B  e  n  j  a  m  i  n       H  e  e     j  o  n  g  e  n  ...
</screen>

<para>(De lengte wordt in Netwerk Byte Volgorde opgeslagen, uiteraard.
In dit geval is het slechts &eacute;&eacute;n byte dus maakt het niet uit,
maar in het algemeen zul je al je binaire integers in je pakketten op
willen slaan in Netwerk Byte Volgorde.)</para>

<para>Wanneer je deze data verstuurt, kun je beter het zekere voor het
het onzekere nemen en een commando gebruiken dat vergelijkbaar is met
<link linkend="sendall"><function>sendall()</function></link>, zodat je
zeker weet dat alle data is verstuurd, zelfs als het meerdere aanroepen
naar <function>send()</function> vereist om het allemaal de deur uit te
krijgen.</para>

<para>Op vergelijkbare wijze, wanneer je deze data ontvangt, zul je wat
extra werk moeten doen. Om zeker te zijn zou je moeten aannemen dat je
slechts een deel van een pakketje ontvangt (misschien ontvangen we
bijvoorbeeld wel <computeroutput>00 14 42 65 6E</computeroutput> van
Benjamin, zie boven, als we <function>recv()</function> aanroepen).
We moeten <function>recv()</function> blijven aanroepen tot we het pakketje
volledig hebben ontvangen.</para>

<para>Maar hoe? Nou, we weten het aantal bytes dat we in totaal moeten
ontvangen om een compleet pakketje te ontvangen, aangezien dat getal aan
het begin van het pakketje zit geplakt. We weten ook dat de maximum
grootte van het pakket 1+8+128 is, oftewel 137 bytes (want zo hebben we
het pakketje gedefinieerd).</para>

<para>Wat je kunt doen is een array declareren die groot genoeg is voor
twee pakketjes. In deze werk array reconstrueer je pakketjes zoals ze
arriveren.</para>

<para>Elke keer dat je data <function>recv()</function>t stop je het in de
werk array en controleer je of het pakket compleet is. Dat wil zeggen,
het aantal bytes in de buffer is groter dan of gelijk aan de lengte
gespecificeerd in de header (+1, want de lengte van de header is exclusief
de byte voor de lengte zelf). Als het aantal bytes in de buffer minder
is dan 1, dan is het pakket duidelijk niet compleet. Hier moet je echter
een speciaal geval van maken, aangezien het eerste byte troep is en je er
niet op kunt vertrouwen dat het de juiste pakketlengte bevat.</para>

<para>Wanneer het pakket compleet is kun je ermee doen wat je wil. Gebruik
het, en verwijder het van je werk buffer.</para>

<para>Whew! Heb je dat in je hoofd zitten? Nou, hier is het tweede punt:
het kan zijn dat je het over het einde van het ene pakket en dus een
deel van het volgende pakket hebt gelezen in een enkele aanroep van
<function>recv()</function>. Oftewel, je hebt een werk buffer met een
compleet pakket, en een incompleet deel van het volgende pakket! Sodeju.
(Maar dit is waarom je je werk buffer groot genoeg hebt gemaakt voor
<emphasis>twee</emphasis>pakketjes -- speciaal voor dit geval!)</para>

<para>Aangezien je de lengte van het eerste pakket kunt vinden in de header,
en je het aantal bytes in de werk buffer bijhoudt, kun je door ze van elkaar
af te trekken uitrekenen hoeveel bytes in de werk buffer van het tweede
(involledige) pakket zijn. Als je het eerste pakket verwerkt hebt, kun je
het uit de werk buffer verwijderen het het incomplete pakketje doorschuiven
naar het begin van de werk buffer, zodat alles klaar is voor de volgende
<function>recv()</function> aanroep.</para>

<para>(Een aantal lezers zullen opmerken dat het verplaatsen van het
incomplete pakketje naar het begin van de werk buffer tijd kost, en het
programma zo geschreven kan worden dat dat niet nodig is door een circulaire
buffer te gebruiken. Helaas voor de rest van jullie valt een discussie over
circulaire buffers buiten het bestek van dit boek. Als je nog steeds
nieuwsgierig bent, pak dan eens een boek over data structuren.)</para>

<para>Ik heb nooit gezegd dat het makkelijk was. Okee, dat heb ik wel gezegd.
En dat is het ook; je moet alleen oefenen en voor je het weet gaat het
vanzelf. Bij Excalibur, ik zweer het!</para>

</sect2>

</sect1> <!-- advanced -->

<!-- ======================================================= -->
<!-- reference -->
<!-- ======================================================= -->

<sect1 id="reference">
<title>Meer Referenties</title>

<para>Je bent tot hier gekomen, en nou schreeuw je om meer! Waar kun je nog
meer naartoe om meer over dit soort dingen te leren?</para>

<!-- ======================================================= -->
<!-- manpages -->
<!-- ======================================================= -->

<sect2 id="manpages">
<title><command>man</command> Pagina's</title>

<para>Probeer om te beginnen eens de volgende man pages:</para>

<itemizedlist>

<listitem><para><function><ulink url="&manpre;2/socket.2&manpost;">socket()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;7/socket.7&manpost;">socket options</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/bind.2&manpost;">bind()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/connect.2&manpost;">connect()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/listen.2&manpost;">listen()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/accept.2&manpost;">accept()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/send.2&manpost;">send()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/recv.2&manpost;">recv()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/sendto.2&manpost;">sendto()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/recvfrom.2&manpost;">recvfrom()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/close.2&manpost;">close()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/shutdown.2&manpost;">shutdown()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/getpeername.2&manpost;">getpeername()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/getsockname.2&manpost;">getsockname()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;3/gethostbyname.3&manpost;">gethostbyname()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;3/gethostbyaddr.3&manpost;">gethostbyaddr()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;3/getprotobyname.3&manpost;">getprotobyname()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/fcntl.2&manpost;">fcntl()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/select.2&manpost;">select()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;3/perror.3&manpost;">perror()</ulink></function></para></listitem>
<listitem><para><function><ulink url="&manpre;2/gettimeofday.2&manpost;">gettimeofday()</ulink></function></para></listitem>

</itemizedlist>

</sect2>

<!-- ======================================================= -->
<!-- books -->
<!-- ======================================================= -->

<sect2 id="books">
<title>Boeken</title>

<para>Voor old-school, echte hou-em-in-je-hand pulp papier boeken zou
je de volgende uitstekende handleidingen kunnen proberen. Let op het
prominente Amazon.com logo. Wat al dit schaamteloze commercialisme
betekent is dat ik wat terug krijg (Amazon.com winkelcrediet,
welteverstaan) voor het verkopen van deze boeken via deze handleiding.
Dus als je toch al een van de volgende boeken wilde bestellen, waarom
zou je me dan geen speciaal bedankje sturen door je geldsmijterij te
beginnen via een van de onderstaande links.</para>

<para>Bovendien, meer boeken voor mij zou misschien kunnen leiden tot
meer handleidingen voor jou <computeroutput>;-)</computeroutput></para>

<para>
<ulink url="&amazonref;redirect-home&amazonrefid;">
<inlinemediaobject>
<imageobject><imagedata fileref="home-logo-130x60w.eps"></imagedata></imageobject>
<imageobject><imagedata fileref="home-logo-130x60w.pdf"></imagedata></imageobject>
<imageobject><imagedata fileref="home-logo-130x60w.gif"></imagedata></imageobject>
<textobject><phrase>In Samenwerking met Amazon.com</phrase></textobject>
</inlinemediaobject>
</ulink>
</para>

<para><emphasis>Unix Network Programming, volumes 1-2</emphasis> by W.
Richard Stevens.  Published by Prentice Hall.
ISBNs for volumes 1-2:
<ulink url="&amazonref;ASIN/013490012X&amazonrefid;">013490012X</ulink>,
<ulink url="&amazonref;ASIN/0130810819&amazonrefid;">0130810819</ulink>.
</para>

<para><emphasis>Internetworking with TCP/IP, volumes I-III</emphasis> by
Douglas E. Comer and David L. Stevens.  Published by Prentice Hall.
ISBN nummers voor delen I, II en III:
<ulink url="&amazonref;ASIN/0130183806&amazonrefid;">0130183806</ulink>,
<ulink url="&amazonref;ASIN/0139738436&amazonrefid;">0139738436</ulink>,
<ulink url="&amazonref;ASIN/0138487146&amazonrefid;">0138487146</ulink>.
</para>

<para><emphasis>TCP/IP Illustrated, volumes 1-3</emphasis> by W. Richard
Stevens and Gary R. Wright.  Published by Addison Wesley.
ISBN nummers voor delen 1, 2 en 3:
<ulink url="&amazonref;ASIN/0201633469&amazonrefid;">0201633469</ulink>,
<ulink url="&amazonref;ASIN/020163354X&amazonrefid;">020163354X</ulink>,
<ulink url="&amazonref;ASIN/0201634953&amazonrefid;">0201634953</ulink>.
</para>

<para><emphasis>TCP/IP Network Administration</emphasis> by Craig Hunt.
Published by O'Reilly &amp; Associates, Inc.
ISBN
<ulink url="&amazonref;ASIN/1565923227&amazonrefid;">1565923227</ulink>.
</para>

<para><emphasis>Advanced Programming in the UNIX Environment</emphasis>
by W. Richard Stevens.  Published by Addison Wesley.
ISBN
<ulink url="&amazonref;ASIN/0201563177&amazonrefid;">0201563177</ulink>.
</para>

<para><emphasis>Using C on the UNIX System</emphasis> by David A. Curry.
Published by O'Reilly &amp; Associates, Inc.  ISBN 0937175234.
<emphasis>Wordt niet meer gedrukt.</emphasis></para>

</sect2>

<!-- ======================================================= -->
<!-- webref -->
<!-- ======================================================= -->

<sect2 id="webref">
<title>Web Referenties</title>

<para>Op het web:</para>

<para><emphasis><ulink url="http://www.cs.umn.edu/~bentlema/unix/"> BSD
Sockets: A Quick And Dirty Primer</ulink></emphasis> (heeft ook andere
geweldige info over UNIX systeemprogrammeren!)</para>

<para><emphasis><ulink url="http://www.ibrado.com/sock-faq/">The Unix
Socket FAQ</ulink></emphasis></para>

<para><emphasis><ulink
url="http://pandonia.canberra.edu.au/ClientServer/">Client-Server
Computing</ulink></emphasis></para>

<para><emphasis><ulink
url="gopher://gopher-chem.ucdavis.edu/11/Index/Internet_aw/Intro_the_Internet/intro.to.ip/">
Intro to TCP/IP</ulink></emphasis> (gopher)</para>

<para><emphasis><ulink
url="http://www-iso8859-5.stack.net/pages/faqs/tcpip/tcpipfaq.html">Internet
Protocol Frequently Asked Questions</ulink></emphasis></para>

<para><emphasis><ulink url="&winsockfaq">The Winsock
FAQ</ulink></emphasis></para>

</sect2>

<!-- ======================================================= -->
<!-- rfcs -->
<!-- ======================================================= -->

<sect2 id="rfcs">
<title>RFCs</title>

<para><ulink url="&rfcbase;">RFCs</ulink>--the real dirt:</para>

<para><emphasis><ulink
url="&rfcpre;768&rfcpost;">RFC-768</ulink></emphasis>--The User
Datagram Protocol (UDP)</para>

<para><emphasis><ulink
url="&rfcpre;791&rfcpost;">RFC-791</ulink></emphasis>--The Internet
Protocol (IP)</para>

<para><emphasis><ulink
url="&rfcpre;793&rfcpost;">RFC-793</ulink></emphasis>--The Transmission
Control Protocol (TCP)</para>

<para><emphasis><ulink
url="&rfcpre;854&rfcpost;">RFC-854</ulink></emphasis>--The Telnet
Protocol</para>

<para><emphasis><ulink
url="&rfcpre;951&rfcpost;">RFC-951</ulink></emphasis>--The Bootstrap
Protocol (BOOTP)</para>

<para><emphasis><ulink
url="&rfcpre;1350&rfcpost;">RFC-1350</ulink></emphasis>--The Trivial
File Transfer Protocol (TFTP)</para>

</sect2>

</sect1> <!-- reference -->

<!-- ======================================================= -->
<!-- faq -->
<!-- ======================================================= -->

<sect1 id="faq">
<title>Gebruikelijke Vragen</title>

<qandaset defaultlabel='qanda'>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Waar kan ik die header files krijgen?</para></question>

<answer><para>Als je ze niet al op je systeem hebt, dan heb je ze
waarschijnlijk ook niet nodig. Sla de handleiding voor jouw specifieke
platform er even op na. Als je voor Windows aan het schrijven bent, heb je
alleen <computeroutput>#include &lt;winsock.h&gt;</computeroutput>
nodig.</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Wat moet ik doen wanneer <function>bind()</function>
de melding "Address already in use" geeft?</para></question>

<answer><para>Je moet <function>setsockopt()</function> gebruiken met de
<constant>SO_REUSEADDR</constant> optie op de luisterende socket. Zie
ook de <link linkend="bind">sectie over <function>bind()</function></link>
en de <link linkend="select">sectie over <function>select()</function></link>
voor een voorbeeld.</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Hoe krijg ik een lijst van open sockets op het 
systeem?</para></question>

<answer><para>Gebruik het <command>netstat</command> commando. Kijk even in
de <command>man</command> pagina voor volledige details, maar je zou enige
nuttige uitvoer moeten krijgen met:</para>

<screen>
<prompt>$</prompt> <command>netstat</command>
</screen>

<para>De truuk is alleen om uit te vogelen welke socket met welk programma
is geassocieerd. <computeroutput>:-)</computeroutput></para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Hoe kan ik de routing table bekijken?</para></question>

<answer><para>Voer het <command>route</command> commando uit (in
<filename>/sbin</filename> op de meeste Linux systemen) of het
commando <command>netstat -r</command>.</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Hoe kan ik de client en server programma's uitvoeren
als ik maar &eacute;&eacute;n computer heb? Moet ik geen netwerk
hebben om netwerkprogramma's te schrijven?</para></question>

<answer><para>Gelukkig voor jou hebben vrijwel alle machines een
loopback netwerk "apparaat" dat in de kernel zit en doet alsof het
een netwerkkaart is. (Dit is de interface die als
<computeroutput>lo</computeroutput> wordt vermeldt in de routing
table).</para>

<para>Doe eens even alsof je op een machine ingelogd bent die
"<computeroutput>geit</computeroutput>" heet. Draai de client in een
venster en de server in een ander. Of start de server in de achtergrond
en draai de client in hetzelfde venster. Het leuke van het loopback
device is dat je zowel <command>client goat</command> als
<command>client localhost</command> kunt gebruiken (aangezien
"<computeroutput>localhost</computeroutput>" waarschijnlijk is
gedefini&euml;erd in je <filename>/etc/hosts</filename> bestand)
en je client en server babbelen vervolgens lekker met elkaar
zonder een netwerk!</para>

<para>Simpel gezegd zijn er geen wijzigingen nodig om de code aan
de praat te krijgen op een enkele stand-alone machine!
Jippie!</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Hoe kom ik erachter of de andere kant de verbinding heeft
gesloten?</para></question>

<answer><para>Daar kom je achter doordat <function>recv()</function>
de waarde <constant>0</constant> teruggeeft.</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Hoe implementeer ik een "ping" utility? Wat is ICMP?
Waar kan ik meer te weten komen over kale sockets (raw sockets) en
<constant>SOCK_RAW</constant>?</para></question>

<answer><para>Al je vragen over kale sockets worden beantwoord in
de UNIX Network Programming boeken van W. Richard Stevens. Zie het
<link linkend="books">boeken</link> onderdeel van deze
handleiding.</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Hoe schrijf ik voor Windows?</para></question>

<answer><para>Ten eerste, verwijder Windows en installeer Linux of BSD.
<computeroutput>};-)</computeroutput>. Nee, je hoeft alleen maar naar de <link
linkend="windows">sectie over schrijven voor Windows</link> in de
introductie te gaan.</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Hoe compileer ik voor Solaris/SunOS? Ik blijf maar linker
errors krijgen wanneer ik probeer te compileren!</para></question>

<answer><para>De linker errors komen omdat Sun dozen niet automatisch de
socket libraries mee compileren. Zie de <link
linkend="solaris">sectie over compileren voor Solaris/SunOS</link> in de
introductie voor een voorbeeld.</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Waarom kapt <function>select()</function> er steeds mee bij
ontvangst van een signaal (<emphasis>signal</emphasis>)?</para></question>

<answer><para>Signals hebben de neiging om geblokkeerde systeemaanroepen
<constant>-1</constant> terug te laten geven, met <parameter>errno</parameter>
ingesteld op <constant>EINTR</constant>. Wanneer je een signal handler
instelt met de <function>sigaction()</function> kun je de vlag op
<constant>SA_RESTART</constant> zetten, wat ervoor zou moeten zorgen dat
de systeemaanroep wordt herstart nadat het werd onderbroken.</para>

<para>Natuurlijk werkt dit niet altijd.</para>

<para>Mijn favoriete oplossing hiervoor maakt gebruik van een
<computeroutput>goto</computeroutput> statement. Je weet dat dit je
leraren mateloos irriteert, dus ga ervoor!</para>

<programlisting><![CDATA[
select_restart:
    if ((err = select(fdmax+1, &readfds, NULL, NULL, NULL)) == -1) {
        if (errno == EINTR) {
            // een of ander signaal heeft ons onderbroken, dus herstarten
            goto select_restart;
        }
        // echte errors hier afhandelen:
        perror("select");
    } ]]>
</programlisting>

<para>Tuurlijk, je <emphasis>hoeft</emphasis>
<computeroutput>goto</computeroutput> in dit geval niet te gebruiken;
je kunt ook andere structuren gebruiken. Maar ik vind dat het gebruik van
<computeroutput>goto</computeroutput> netter is.</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Hoe implementeer ik een timeout op een aanroep naar
<function>recv()</function>?</para></question>

<answer><para>Gebruik <link linkend="select"><function>select()</function></link>!
Het stelt je in staat een timeout parameter aan te geven voor socket
descriptors waarvan je wilt lezen. Je zou al die functionaliteit ook
in een enkele functie kunnen stoppen, zo:</para>

<programlisting><![CDATA[
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>

int recvtimeout(int s, char *buf, int len, int timeout)
{
    fd_set fds;
    int n;
    struct timeval tv;

    // file descriptor set opstellen
    FD_ZERO(&fds);
    FD_SET(s, &fds);

    // de struct timeval voor de timeout instellen
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    // wacht op timeout of ontvangst data
    n = select(s+1, &fds, NULL, NULL, &tv);
    if (n == 0) return -2; // timeout!
    if (n == -1) return -1; // error

    // er moet data zijn, dus voer een normale recv() uit
    return recv(s, buf, len, 0);
}

// Voorbeelaanroep van recvtimeout():
    .
    .
    n = recvtimeout(s, buf, sizeof(buf), 10); // 10 seconden timeout

    if (n == -1) {
        // fout opgetreden
        perror("recvtimeout");
    }
    else if (n == -2) {
        // timeout opgetreden
    } else {
        // wat data in buf ontvangen
    }
    .
    . ]]>
</programlisting>

<para>Merk op dat <function>recvtimeout()</function> in geval
van een timeout <constant>-2</constant> teruggeeft. Waarom geven
we niet <constant>0</constant> terug? Nou, zoals je je misschien
herinnert, een return waarde van <constant>0</constant> bij een
aanroep naar <function>recv()</function> geeft aan dat de andere
kant de verbinding heeft gesloten. Dus die return waarde heeft
al een betekenis, en <constant>-1</constant> betekent "fout", dus
heb ik <constant>-2</constant> als timeout indicator gekozen.</para>

</answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<!-- al eerder beantwoord, zie terug
<qandaentry>

<question><para>Wat als ik maar een computer en geen netwerk heb? Hoe
is het mogelijk om socket programma's te schrijven en te
testen?</para></question>

<answer><para>Het werkt op een enkele computer op precies dezelfde manier
als het op een netwerkcomputer doet.</para>

<para>Dat wil zeggen, het draaien van de client en de server op dezelfde
machine werkt net zo goed als op verschillende machines, en je hoeft
niet eens rekening te houden met de verschillen.</para>

<para>Waarom dit zo is kun je zien als je "<command>route</command>"
intypt:</para>

<screen>
<prompt>$</prompt> <command>route</command>
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.0.0.0        *               255.0.0.0       U     0      0        0 eth0
127.0.0.0       *               255.0.0.0       U     0      0        0 lo
default         mygateway       0.0.0.0         UG    0      0        0 eth0
</screen>

<para>Zie je, al het verkeer op <computeroutput>10.x.x.x</computeroutput>
gaat gaat naar mijn locale subnet via <computeroutput>eth0</computeroutput>
(de netwerkkaart), maar al het verkeer op het
<computeroutput>127.x.x.x</computeroutput> subnet gaat naar de
<computeroutput>lo</computeroutput> interface (loopback&emdash;een
nep "netwerkkaart" in de kernel die verkeer van de machine naar zichzelf
afhandelt).</para>

<para>De <computeroutput>lo</computeroutput> interface wordt ook gebruikt
wanneer je probeert te telnetten naar je eigen IP adres (onder Linux
tenminste). Dus vanaf de machine "kikker" kan ik zowel
"<command>telnet kikker</command>" als "<command>telnet localhost</command>"
(localhost is <computeroutput>127.0.0.1</computeroutput>) gebruiken en
in beide gevallen wordt <computeroutput>lo</computeroutput> gebruikt.</para>

<para>Dus, in het kort, maak je geen zorgen. De client en server draaien
prima op een en dezelfde machine.</para>

</answer>

</qandaentry>
-->

<!-- +++++++++++++++++++++++++ -->

<qandaentry>

<question><para>Hoe encrypt of comprimeer ik de data voor ik het over een
socket stuur?</para></question>

<answer>

<para>Een makkelijke manier om encryptie te gebruiken is SSL
(secure socket layer), maar dat ligt buiten het bestek van deze
handleiding.</para>

<para>Maar aangenomen dat je je eigen compressor- of encryptiesysteem
wil implementeren, is het het makkelijkst om je data te zien als lopend
door een serie stappen tussen beide uiteinden. Elke stap verandert de
data op een of andere manier.</para>

<para>
<orderedlist>
<listitem><para>de server leest data uit een bestand (of wat dan ook)</para></listitem>
<listitem><para>de server encrypt de data (dit voeg je zelf toe)</para></listitem>
<listitem><para>de server <function>send()</function> de versleutelde data</para></listitem>
</orderedlist>
</para>

<para>Nu andersom:</para>

<para>
<orderedlist continuation="continues">
<listitem><para>de client <function>recv()</function>t de versleutelde data</para></listitem>
<listitem><para>de client decrypt de data (dit voeg je zelf toe)</para></listitem>
<listitem><para>de client schrijft de data naar een bestand (of wat dan ook)</para></listitem>
</orderedlist>
</para>

<para>Op het punt waar je encrypt/decrypt zou je ook kunnen comprimeren.
Of allebei! Denk er alleen wel aan dat je comprimeert voor je encrypt.
<computeroutput>:)</computeroutput></para>

<para>Zolang de client maar netjes de stappen van de server ongedaan
maakt, blijft de data prima, hoeveel tussenstappen je ook toevoegd.</para>

<para>Dus het enige wat je moet doen om m'n code te gebruiken is de plaats
vinden waar de data gelezen wordt en verstuurd wordt (gebruik makend van
<function>send()</function> over het netwerk, en prop er wat code tussen
die de encryptie doet.</para>

</answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->

<qandaentry>

<question><para>Wat is die "<constant>PF_INET</constant>" die maar
op blijft duiken? Is het gerelateerd aan
<constant>AF_INET</constant>?</para></question>

<answer>
<para>Ja, inderdaad. Zie <link linkend="socket">het onderdeel
over <function>socket()</function></link> voor details.</para>

</answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->

<qandaentry>

<question><para>Hoe kan ik een server schrijven die shell commando's
accepteert van een client en die uitvoert?</para></question>

<answer>

<para>Om het simpel te houden, laten we zeggen dat de client
de verbinding <function>connect()</function>,
<function>send()</function> en <function>close()</function>t (dat wil zeggen,
er worden geen verdere systeemaanroepen uitgevoerd zonder dat de client
opnieuw verbinding maakt).</para>

<para>Het proces dat de client volgt is dit:</para>

<para>
<orderedlist>
<listitem><para><function>connect()</function>en met de server</para></listitem>
<listitem><para><function>send("/sbin/ls &gt; /tmp/client.out")</function></para></listitem>
<listitem><para><function>close()</function> de verbinding</para></listitem>
</orderedlist>
</para>

<para>Ondertussen verwerkt de server de data is voert deze uit:</para>

<para>
<orderedlist>
<listitem><para><function>accept()</function> de verbinding van de client</para></listitem>
<listitem><para><function>recv(str)</function> de commando string</para></listitem>
<listitem><para><function>close()</function> de verbinding</para></listitem>
<listitem><para><function>system(str)</function> om het commando uit te voeren</para></listitem>
</orderedlist>
</para>

<para><emphasis>Waarschuwing!</emphasis> Het draaien van een server
die uitvoert wat de client zegt is hetzelfde als het uitgeven van
externe toegang (shell access) en mensen kunnen dingen aan je account
veranderen wanneer ze verbinding maken met de server. Bijvoorbeeld,
in het bovenstaande voorbeeld, wat als de client
"<command>rm -rf ~</command>" verstuurt? Het verwijdert alle bestanden
in je account!</para>

<para>Dus je denkt slim te zijn en voorkomt dat de client alles behalve
een paar utilities niet meer kan gebruiken waarvan je weet dat ze veilig
zijn, zoals het <command>foobar</command> utility:</para>

<programlisting><![CDATA[
    if (!strcmp(str, "foobar")) {
        sprintf(sysstr, "%s > /tmp/server.out", str);
        system(sysstr);
    } ]]>
</programlisting>

<para>Maar je bent nog steeds niet veilig, helaas: wat als de client
"<command>foobar ; rm -rf ~</command>" invoert? Het veiligste wat je
kan doen is een kleine routine te schrijven die een escape
("<constant>\</constant>") karakter voor alle niet-alfanumerieke
karakters plaatst (inclusief spaties, indien van toepassing) in de
argumenten voor het commando.</para>

<para>Zoals je ziet is security een flink onderwerp wanneer de server
dingen gaat uitvoeren die de client verstuurt.</para>

</answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Ik stuur een sloot aan data, maar wanneer ik
<function>recv()</function>, wordt er maar 536 of 1460 bytes aan data
peer keer ontvangen. Maar als ik het op m'n lokale machine draai,
wordt alle data in &eacute;&eacute;n keer ontvangen. Wat is er
aan de hand?</para></question>

<answer>

<para>Je loopt tegen de MTU aan--het maximum aantal bytes wat het fysieke
medium aankan. Op de lokale machine gebruik je het loopback apparaat welke
zonder probleem 8K of meer aankan. Maar op ethernet, wat maar 1500 bytes met
een header aankan, loop je tegen dat limiet aan. Over een modem, met
576 MTU (weer inclusief header) loop je tegen het nog lagere limiet
aan.</para>

<para>Ten eerste moet je ervoor zorgen dat alle data verstuurd wordt
(zie de <link linkend="sendall"><function>sendall()</function></link>
functie implementatie voor details). Wanneer je daar zeker van bent,
moet je <function>recv()</function> in een loop (lus) aanroepen totdat
al de data gelezen is.</para>

<para>Lees het onderdeel over <link linkend="sonofdataencap">Data
Enkapsulatie</link> voor details over het ontvangen van complete
pakketten met gebruik van meerdere aanroepen naar
<function>recv()</function>.</para>

</answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Ik zit op een Windows doos en ik heb geen
<function>fork()</function> systeemaanroep of een of andere vorm
van <type>struct sigaction</type>. Wat nu?</para></question>

<answer>
<para>Als ze al ergens zijn, dan zitten ze in POSIX libraries welke
bij je compiler zouden kunnen zitten. Aangezien ik geen Windows doos heb
kan ik het je niet vertellen, maar ik meen me te herinneren dat Microsoft
een POSIX compatibiliteitslaag heeft, en daar zou <function>fork()</function>
tussen moeten zitten (en misschien zelf <type>struct sigaction</type>.</para>

<para>doorzoek de help documentatie die bij VC++ zit op "fork" of "POSIX".
Misschien dat dat je wat oplevert.</para>

<para>Als dat nou helemaal niet werkt, gooi dan de hele
<function>fork()</function>/<type>struct sigaction</type> zooi maar weg
en vervang het door het win32 equivalent: <function>CreateProcess()</function>.
Ik weet niet hoe <function>CreateProcess()</function> gebruikt moet worden--het
vereist een baziljoen argumenten, maar dat zou in de documentatie moeten
staan die bij VC++ zit.</para>

</answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Hoe verstuur ik data veilig via TCP/IP met
gebruik van encryptie?</para></question>

<answer><para>Werp eens een blik op het
<ulink url="&openssl;">OpenSSL project</ulink>.</para></answer>

</qandaentry>

<!-- +++++++++++++++++++++++++ -->
<qandaentry>

<question><para>Ik zit achter een firewall--hoe vertel ik andere mensen
buiten de firewall m'n IP adres zodat ze verbinding kunnen maken met
m'n machine?</para></question>

<answer><para>Helaas is het doel van een firewall voorkomen dat mensen
van buiten de firewall verbinding kunnen maken met machines binnen
de firewall, dus juist het toestaan daarvan wordt gezien als een
veiligheidsbreuk.</para>

<para>Dit wil niet zeggen dat alles verloren is. Bijvoorbeeld, je kunt
vaak wel <function>connect()</function>en door de firewall naar buiten
als het een of andere vorm van masquerading of NAT doet. Ontwerp je
programma's z&oacute; dat jij altijd degene bent die de verbinding
initialiseert, en alles komt goed.</para>

<para>Als je daar niet tevreden mee bent kun je je systeembeheerders
vragen of ze een gaatje in de firewall willen prikken zodat mensen
verbinding met je machine kunnen maken. De firewall de verbinding naar
jou doorsturen hetzij via z'n NAT software, of door een proxy of iets
dergelijks.</para>

<para>Let erop dat een gat in de firewall niet iets is om lichtzinnig
over te doen. Je moet er voor zorgen dat je niet de verkeerde mensen
toegang tot je interne netwerk geeft; als je een beginner bent, is het
een stuk moeilijker om software veilig te maken dan je zou denken.</para>

<para>Maak je systeembeheerder niet kwaad op me
<computeroutput>;-)</computeroutput></para></answer>

</qandaentry>

<!--
<qandaentry>

<question><para>When I have two interfaces, how do I
<function>bind()</function> to an interface by name (like
"<computeroutput>eth0</computeroutput>") instead of IP
address?</para></question>

<answer><para></para></answer>

</qandaentry>
-->

</qandaset>

</sect1> <!-- faq -->

<!-- ======================================================= -->
<!-- conclusion -->
<!-- ======================================================= -->

<sect1 id="conclusion">
<title>Disclaimer en een Hulpoproep</title>

<para>Nou, dat is het wel zo'n beetje. Hopelijk was in elk geval een deel
van de informatie in dit document een beetje nauwkeurig en ik hoop echt
dat er geen enorme fouten in zitten. Maar die zijn er natuurlijk
altijd.</para>

<para>Dus laat dit een waarschuwing voor je zijn! Het spijt me indien
een van de onnauwkeurigheden in dit document je enig leed hebben bezorgd,
maar je kunt me gewoon niet verantwoordelijk houden. Zie je, ik sta achter
geen enkel woord in dit document, juridisch gezien. Het hele verhaal zou
compleet en volledig verkeerd kunnen zijn!</para>

<para>Maar waarschijnlijk is het dat niet. Uiteindelijk heb ik vele, vele
uren zitten prutsen met deze stof, en verscheidene TCP/IP netwerk
programmaatjes geschreven op m'n werk, en multiplayer game engines
geschreven, enzovoorts. Maar ik ben niet de god van de sockets; ik ben
zomaar iemand.</para>

<para>Overigens, als iemand enige constructieve (of destructieve) kritiek
heeft over dit dokument, stuur dan een mailtje naar <email>&beejmail;</email>
en ik zal kijken of ik het een en ander recht kan zetten.</para>

<para>Mocht je je afvragen waarom ik dit heb gedaan, nou ja, voor het geld.
Ha! Nee, ik heb het gedaan omdat een hoop mensen me socket gerelateerde
vragen stelden, en wanneer ik ze vertelde dat ik zat te denken om een
socket pagina te maken zeiden ze, "Cool!" Bovendien, ik heb het gevoel dat
al deze zuurverdiende kennis verloren gaat als ik het niet deel met anderen.
Het web blijkt gewoon met perfecte medium. Ik moedig anderen aan om
vergelijkbare informatie aan te bieden wanneer het maar kan.</para>

<para>Genoeg hierover--verder programmeren!
<computeroutput>;-)</computeroutput></para>

</sect1> <!-- conclusion -->

</article>
