8. Programmering TCP-IP
8.1. Algemeen
8.1.1. Internetprotocollen
Hier geven we een inleiding tot de communicatieprotocollen van het internet, ook wel de TCP/IP-protocolsuite (Transfer Control Protocol / Internet Protocol) genoemd, naar de twee belangrijkste protocollen. Het is raadzaam dat de lezer een algemeen begrip heeft van de werking van netwerken en met name van de protocollen TCP/IP, voordat hij zich bezighoudt met het bouwen van gedistribueerde applicaties.
De volgende tekst is een gedeeltelijke vertaling van een tekst uit het document „Lan Workplace for Dos – Administrator’s Guide” van NOVELL, een document uit het begin van de jaren 90.
Het algemene concept van het opzetten van een netwerk van heterogene computers is voortgekomen uit onderzoek uitgevoerd door de DARPA (Defense Advanced Research Projects Agency) in de Verenigde Staten. Het DARPA heeft de reeks protocollen ontwikkeld die bekend staat onder de naam TCP/IP, waarmee heterogene machines met elkaar kunnen communiceren. Deze protocollen zijn getest op een netwerk met de naam ARPAnet, een netwerk dat later het INTERNET-netwerk werd. De protocollen TCP/IP definiëren formaten en regels voor verzending en ontvangst die onafhankelijk zijn van de organisatie van de netwerken en de gebruikte apparatuur.
Het netwerk dat is ontworpen door het DARPA en wordt beheerd door de protocollen TCP/IP is een pakketgeschakeld netwerk. Een dergelijk netwerk verzendt informatie over het netwerk in kleine stukjes die pakketten worden genoemd. Als een computer bijvoorbeeld een groot bestand verzendt, wordt dit in kleine stukjes opgedeeld die over het netwerk worden verzonden om op de bestemming weer te worden samengevoegd. TCP/IP definieert het formaat van deze pakketten, namelijk:
- herkomst van het pakket
- bestemming
- lengte
- type
8.1.2. Het model OSI
De protocollen TCP/IP volgen grotendeels het open netwerkmodel genaamd OSI (Open Systems Interconnection Reference Model), gedefinieerd door de ISO (International Standards Organisation). Dit model beschrijft een ideaal netwerk waarin de communicatie tussen machines kan worden weergegeven door een model met zeven lagen:

Elke laag ontvangt diensten van de onderliggende laag en biedt haar eigen diensten aan de bovenliggende laag aan. Stel dat twee applicaties op verschillende machines A en B met elkaar willen communiceren: dat doen ze op het niveau van de Application-laag. Ze hoeven niet alle details van de werking van het netwerk te kennen: elke toepassing geeft de informatie die ze wil verzenden door aan de onderliggende laag: de laag Présentation. De toepassing hoeft dus alleen de regels voor de koppeling met de laag Présentation te kennen.
Zodra de informatie zich in de laag Présentation bevindt, wordt deze volgens andere regels doorgestuurd naar de laag Session, enzovoort, totdat de informatie op het fysieke medium terechtkomt en fysiek naar de bestemmingsmachine wordt verzonden. Daar ondergaat de informatie het omgekeerde proces van wat er op de verzendende machine is gebeurd.
Op elke laag stuurt het verzendproces, dat verantwoordelijk is voor het verzenden van de informatie, deze naar een ontvangstproces op de andere machine die tot dezelfde laag behoort. Dit gebeurt volgens bepaalde regels die het protocol van de laag worden genoemd. We krijgen dus het volgende uiteindelijke communicatieschema:

De rol van de verschillende lagen is als volgt:
Zorgt voor de overdracht van bits via een fysiek medium. In deze laag bevinden zich eindapparatuur voor gegevensverwerking (E.T.T.D), zoals terminals of computers, evenals apparatuur voor het afsluiten van datacircuits (E.T.C.D), zoals modulators/demodulators, multiplexers en concentrators. De aandachtspunten op dit niveau zijn: . de keuze van de codering van de informatie (analoog of digitaal) . de keuze van de transmissiemodus (synchroon of asynchroon). | |
Verbergt de fysieke kenmerken van de fysieke laag. Detecteert en corrigeert transmissiefouten. | |
Beheert het pad dat de via het netwerk verzonden informatie moet volgen. Dit wordt de routage genoemd: het bepalen van de route die informatie moet volgen om bij de ontvanger aan te komen. | |
Maakt communicatie tussen twee applicaties mogelijk, terwijl de voorgaande lagen alleen communicatie tussen machines toelieten. Een dienst die door deze laag wordt geleverd, kan multiplexing zijn: de transportlaag kan één en dezelfde netwerkverbinding (van machine naar machine) gebruiken om informatie van meerdere applicaties te verzenden. | |
In deze laag vinden we diensten waarmee een applicatie een werksessie op een externe machine kan openen en in stand houden. | |
Deze laag heeft tot doel de weergave van gegevens op de verschillende machines te uniformiseren. Zo worden gegevens afkomstig van machine A door de Présentation-laag van machine A in een standaardformaat ‘verpakt’ voordat ze over het netwerk worden verzonden. Zodra de gegevens de laag Présentation van de ontvangende machine B bereiken – die ze dankzij hun standaardformaat herkent – worden ze op een andere manier verpakt, zodat de applicatie van machine B ze kan herkennen. | |
Op dit niveau bevinden zich de toepassingen die doorgaans dicht bij de gebruiker staan, zoals e-mail of bestandsoverdracht. |
8.1.3. Het model TCP/IP
Het model OSI is een ideaal model dat nog nooit is gerealiseerd. De reeks protocollen TCP/IP komt hier dicht bij in de volgende vorm:

Fysieke laag
In een lokaal netwerk wordt doorgaans gebruikgemaakt van Ethernet- of Token-Ring-technologie. We behandelen hier alleen de Ethernet-technologie.
Ethernet
Dit is de naam van een technologie voor lokale, pakketgeschakelde netwerken die begin jaren zeventig bij PARC Xerox werd uitgevonden en in 1978 door Xerox, Intel en Digital Equipment werd gestandaardiseerd. Het netwerk bestaat fysiek uit een coaxkabel met een diameter van ongeveer 1,27 cm en een lengte van maximaal 500 m. Het kan worden uitgebreid met behulp van répéteurs, waarbij twee apparaten niet door meer dan twee repeaters van elkaar gescheiden mogen zijn. De kabel is passief: alle actieve componenten bevinden zich op de apparaten die op de kabel zijn aangesloten. Elk apparaat is via een netwerktoegangskaart met de kabel verbonden, die bestaat uit:
- een zender (transceiver) die de aanwezigheid van signalen op de kabel detecteert en de analoge signalen omzet in digitale signalen en omgekeerd.
- een koppelaar die de digitale signalen van de zender ontvangt en deze doorgeeft aan de computer voor verwerking, of omgekeerd.
De belangrijkste kenmerken van de Ethernet-technologie zijn:
- Capaciteit van 10 megabit/seconde.
- Bustopologie: alle apparaten zijn aangesloten op dezelfde kabel

- Broadcast-netwerk – Een computer die gegevens verzendt, stuurt deze via de kabel met het adres van de ontvangende computer. Alle aangesloten computers ontvangen deze gegevens, maar alleen de computer waarvoor ze bestemd zijn, bewaart ze.
- De toegangsmethode is als volgt: de zender die wil verzenden, luistert naar de kabel – hij detecteert dan of er al dan niet een draaggolf aanwezig is, wat zou betekenen dat er een transmissie aan de gang is. Dit is de CSMA-techniek (Carrier Sense Multiple Access). Als er geen draaggolf aanwezig is, kan een zender besluiten om op zijn beurt te zenden. Er kunnen meerdere zenders zijn die deze beslissing nemen. De uitgezonden signalen vermengen zich: men spreekt dan van een botsing. De zender detecteert deze situatie: terwijl hij naar de kabel zendt, luistert hij tegelijkertijd naar wat er daadwerkelijk over de kabel gaat. Als hij detecteert dat de informatie die over de kabel gaat niet dezelfde is als die hij heeft verzonden, concludeert hij dat er een botsing is en stopt hij met zenden. De andere zenders die aan het zenden waren, zullen hetzelfde doen. Elke zender hervat zijn uitzending na een willekeurige tijd, afhankelijk van de betreffende zender. Deze techniek wordt CD (Collision Detect) genoemd. De toegangsmethode wordt daarom CSMA/CD genoemd.
- een 48-bits adressering. Elke machine heeft een adres, hier het fysieke adres genoemd, dat op de kaart staat die de machine met de kabel verbindt. Dit adres wordt het Ethernet-adres van de machine genoemd.
Netwerklaag
Op deze laag vinden we de protocollen IP, ICMP, ARP en RARP.
IP (Internet Protocol) | Verzendt pakketten tussen twee knooppunten in het netwerk |
ICMP (Internet Control Message Protocol) | ICMP zorgt voor de communicatie tussen het IP-protocolprogramma van de ene machine en dat van een andere machine. Het is dus een protocol voor het uitwisselen van berichten binnen het IP-protocol zelf. |
ARP (Address Resolution Protocol) | zet het internetadres van een machine om naar het fysieke adres van die machine |
RARP (Reverse Address Resolution Protocol) | zet het fysieke adres van een computer om naar het internetadres van die computer |
Transport-/sessielagen
In deze laag bevinden zich de volgende protocollen:
TCP (Transmission Control Protocol) | Zorgt voor een betrouwbare overdracht van informatie tussen twee clients |
UDP (User Datagram Protocol) | Zorgt voor een onbetrouwbare overdracht van informatie tussen twee clients |
Toepassings-/presentatie-/sessielagen
Hier vinden we diverse protocollen:
Terminalemulator waarmee machine A verbinding kan maken met machine B als terminal | |
maakt bestandsoverdracht mogelijk | |
maakt bestandsoverdracht mogelijk | |
maakt het uitwisselen van berichten tussen netwerkgebruikers mogelijk | |
zet een computernaam om in het internetadres van de computer | |
gemaakt door Sun MicroSystems, specificeert een standaard, machine-onafhankelijke weergave van de gegevens | |
ook gedefinieerd door Sun; het is een communicatieprotocol tussen externe applicaties, onafhankelijk van de transportlaag. Dit protocol is belangrijk: het ontlast de programmeur van de noodzaak om de details van de transportlaag te kennen en maakt applicaties draagbaar. Dit protocol is gebaseerd op het protocol XDR | |
, eveneens gedefinieerd door Sun. Dit protocol stelt een machine in staat om het bestandssysteem van een andere machine te „zien”. Het is gebaseerd op het voorgaande protocol RPC |
8.1.4. Werking van internetprotocollen
Toepassingen die zijn ontwikkeld in de TCP/IP-omgeving maken doorgaans gebruik van meerdere protocollen uit deze omgeving. Een applicatieprogramma communiceert met de hoogste laag van de protocollen. Deze laag geeft de informatie door aan de laag eronder, en zo verder, totdat deze de fysieke drager bereikt. Daar wordt de informatie fysiek doorgestuurd naar de ontvangende machine, waar ze dezelfde lagen weer doorloopt, ditmaal in omgekeerde volgorde, totdat ze de ontvangende applicatie van de verzonden informatie bereikt. Het volgende schema toont het traject van de informatie:

Laten we een voorbeeld nemen: de applicatie FTP, gedefinieerd op het niveau van de laag Application, die bestandsoverdrachten tussen computers mogelijk maakt.
- De applicatie levert een reeks bytes af die moet worden doorgegeven aan de laag transport.
- De laag transport splitst deze reeks bytes op in segments en TCP, en voegt aan het begin van elk segment het nummer van dat segment toe. De segmenten worden doorgegeven aan de netwerklaag, die wordt bestuurd door het protocol IP.
- De IP-laag maakt een pakket aan waarin het ontvangen segment TCP wordt ingekapseld. Aan het begin van dit pakket plaatst deze laag de internetadressen van de bron- en bestemmingscomputer. Ook bepaalt deze laag het fysieke adres van de bestemmingscomputer. Het geheel wordt doorgegeven aan de laag voor gegevensverbinding en fysieke verbinding, dat wil zeggen aan de netwerkkaart die de computer met het fysieke netwerk verbindt.
- Daar wordt het pakket IP op zijn beurt ingekapseld in een fysiek frame en via de kabel naar de ontvanger verzonden.
- Op de ontvangende machine doet de laag ‘Datalink & Fysieke verbinding’ het omgekeerde: deze ontkapselt het pakket IP uit het fysieke frame en geeft het door aan de laag IP.
- De laag IP controleert of het pakket correct is: ze berekent een checksum op basis van de ontvangen bits (checksum), een checksum die ze terug moet vinden in de header van het pakket. Als dat niet het geval is, wordt het pakket afgewezen.
- Als het pakket als correct wordt aangemerkt, ontkapselt de laag IP het segment TCP dat zich daarin bevindt en geeft het door aan de bovenliggende laag transport.
- De laag transport, in ons voorbeeld de laag TCP, controleert het segmentnummer om de juiste volgorde van de segmenten te herstellen.
- Deze laag berekent ook een controlesom voor het segment TCP. Als deze correct blijkt te zijn, stuurt de laag TCP een ontvangstbevestiging naar de bronmachine; anders wordt het segment TCP geweigerd.
- De laag TCP hoeft nu alleen nog maar het gegevensgedeelte van het segment door te sturen naar de toepassing die deze gegevens ontvangt in de bovenliggende laag.
8.1.5. Adresseringsproblemen op het internet
Een noeud in een netwerk kan een computer zijn, een slimme printer, een bestandsserver, eigenlijk alles wat kan communiceren met behulp van de TCP/IP-protocollen. Elk knooppunt heeft een fysiek adres waarvan het formaat afhankelijk is van het type netwerk. Op een Ethernet-netwerk wordt het fysieke adres gecodeerd in 6 bytes. Een adres in een X25-netwerk is een getal van 14 cijfers.
Het internetadres van een knooppunt is een logisch adres: het is onafhankelijk van de gebruikte hardware en het netwerk. Het is een adres van 4 bytes dat zowel een lokaal netwerk als een knooppunt van dat netwerk identificeert. Het internetadres wordt gewoonlijk weergegeven in de vorm van 4 getallen, de waarden van de 4 bytes, gescheiden door een punt. Zo wordt het adres van de computer Lagaffe van de faculteit Wetenschappen in Angers weergegeven als 193.49.144.1 en dat van de computer Liny als 193.49.144.9. Hieruit volgt dat het internetadres van het lokale netwerk 193.49.144.0 is. Er kunnen maximaal 254 knooppunten op dit netwerk aanwezig zijn.
Omdat internetadressen of IP-adressen onafhankelijk zijn van het netwerk, kan een computer in netwerk A communiceren met een computer in netwerk B zonder zich zorgen te hoeven maken over het type netwerk waarop deze zich bevindt: het volstaat dat hij zijn IP-adres kent. Het IP-protocol van elk netwerk zorgt voor de conversie van IP-adres <--> fysiek adres, in beide richtingen.
De IP-adressen moeten allemaal verschillend zijn. Officiële instanties zijn belast met de toewijzing ervan. In feite wijzen deze instanties een adres toe aan lokale netwerken, bijvoorbeeld 193.49.144.0 voor het netwerk van de faculteit der wetenschappen van Angers. De beheerder van dit netwerk kan vervolgens de adressen IP 193.49.144.1 tot en met 193.49.144.254 naar eigen inzicht toewijzen. Dit adres wordt doorgaans vastgelegd in een speciaal bestand op elke machine die op het netwerk is aangesloten.
8.1.5.1. De adresklassen IP
Een IP-adres is een reeks van 4 bytes, vaak aangeduid als I1.I2.I3.I4, die in feite twee adressen bevat:
- het netwerkadres
- het adres van een knooppunt in dit netwerk
Afhankelijk van de grootte van deze twee velden worden de IP-adressen onderverdeeld in drie klassen: klasse A, B en C.
Klasse A
Het adres IP: I1.I2.I3.I4 heeft de vorm R1.N1.N2.N3, waarbij
R1 het netwerkadres is
N1.N2.N3 het adres is van een computer in dat netwerk
Om precies te zijn, de vorm van een adres van klasse A, IP, is als volgt:

Het netwerkadres bestaat uit 7 bits en het knooppuntadres uit 24 bits. Er kunnen dus 127 klasse A-netwerken zijn, die elk maximaal 224 knooppunten bevatten.
Klasse B
Hier heeft het adres IP: I1.I2.I3.I4 de vorm R1.R2.N1.N2, waarbij
R1.R2 het adres van het netwerk is
N1.N2 het adres is van een computer in dit netwerk
Om precies te zijn, de vorm van een klasse B-adres IP is als volgt:

Zowel het netwerkadres als het knooppuntadres beslaat 2 bytes (precies 14 bits). Er kunnen dus 2¹⁴ klasse B-netwerken zijn, die elk maximaal 2¹⁶ knooppunten bevatten.
Klasse C
In deze klasse heeft het adres IP: I1.I2.I3.I4 de vorm R1.R2.R3.N1, waarbij
R1.R2.R3 het adres van het netwerk is
N1 het adres is van een computer in dit netwerk
Om precies te zijn, de vorm van een klasse C-adres IP is als volgt:

Het netwerkadres beslaat 3 bytes (minus 3 bits) en het knooppuntadres 1 byte. Er kunnen dus 221 klasse C-netwerken zijn met elk maximaal 256 knooppunten.
Aangezien het adres van de computer Lagaffe van de faculteit der wetenschappen van Angers 193.49.144.1 is, zien we dat de hoogste byte 193 is, dat wil zeggen in binair 11000001. Hieruit kunnen we afleiden dat het netwerk van klasse C is.
Gereserveerde adressen
. Sommige adressen, zoals IP, zijn netwerkadressen in plaats van knooppuntadressen in het netwerk. Dit zijn de adressen waarbij het knooppuntadres op 0 is gezet. Zo is het adres 193.49.144.0 het adres IP van het netwerk van de Faculteit der Wetenschappen van Angers. Bijgevolg kan geen enkel knooppunt in een netwerk het adres nul hebben.
. Wanneer in een adres IP het knooppuntadres uitsluitend uit 1-en bestaat, is er sprake van een broadcast-adres: dit adres verwijst naar alle knooppunten in het netwerk.
. In een klasse C-netwerk, dat theoretisch 2⁸ = 256 knooppunten toestaat, blijven er, als we de twee verboden adressen verwijderen, slechts 254 toegestane adressen over.
8.1.5.2. De conversieprotocollen Internetadres <--> Fysiek adres
We hebben gezien dat bij het verzenden van gegevens van de ene machine naar de andere, deze bij het passeren van de laag IP in pakketten werden ingekapseld. Deze hebben de volgende vorm:

Het pakket IP bevat dus de internetadressen van de bron- en bestemmingsmachines. Wanneer dit pakket wordt doorgegeven aan de laag die verantwoordelijk is voor het verzenden ervan over het fysieke netwerk, wordt er andere informatie aan toegevoegd om het fysieke frame te vormen dat uiteindelijk over het netwerk wordt verzonden. Het formaat van een frame op een Ethernet-netwerk is bijvoorbeeld als volgt:

In het uiteindelijke frame staan de fysieke adressen van de bron- en bestemmingscomputer. Hoe worden deze verkregen?
De verzendende machine, die het adres IP kent van de machine waarmee zij wil communiceren, verkrijgt het fysieke adres daarvan door gebruik te maken van een speciaal protocol genaamd ARP (Address Resolution Protocol).
- Hij verstuurt een pakket van een speciaal type, een zogenaamd ARP-pakket, dat het adres IP bevat van de machine waarvan het fysieke adres wordt gezocht. Hij heeft er ook voor gezorgd dat zijn eigen adres IP en zijn fysieke adres in dit pakket zijn opgenomen.
- Dit pakket wordt naar alle knooppunten in het netwerk verzonden.
- Deze herkennen het speciale karakter van het pakket. Het knooppunt dat zijn adres IP in het pakket herkent, reageert door zijn fysieke adres naar de afzender van het pakket te sturen. Hoe kan het dat? Het heeft in het pakket de adressen IP en het fysieke adres van de afzender gevonden.
- De afzender ontvangt dus het fysieke adres dat hij zocht. Hij slaat dit op in het geheugen om het later te kunnen gebruiken als er nog meer pakketten naar dezelfde ontvanger moeten worden verzonden.
Het IP-adres van een computer staat normaal gesproken in een van zijn bestanden, dat hij dus kan raadplegen om het te achterhalen. Dit adres kan worden gewijzigd: het volstaat om het bestand te bewerken. Het fysieke adres daarentegen staat opgeslagen in het geheugen van de netwerkkaart en kan niet worden gewijzigd.
Wanneer een beheerder zijn netwerk anders wil inrichten, kan het nodig zijn om de IP-adressen van alle knooppunten te wijzigen en dus de verschillende configuratiebestanden van de verschillende knooppunten te bewerken. Dit kan vervelend zijn en tot fouten leiden als er veel computers zijn. Een methode bestaat erin geen IP-adres aan de machines toe te wijzen: men voert dan een speciale code in het bestand in waarin de machine haar IP-adres zou moeten vinden. Wanneer de machine ontdekt dat hij geen IP-adres heeft, vraagt hij dit op via een protocol dat RARP (Reverse Address Resolution Protocol) wordt genoemd. Vervolgens verstuurt de machine via het netwerk een speciaal pakket, het zogenaamde RARP-pakket, vergelijkbaar met het voorgaande ARP-pakket, waarin het zijn fysieke adres opneemt. Dit pakket wordt naar alle knooppunten gestuurd, die het vervolgens herkennen als een RARP-pakket. Een van deze knooppunten, de zogenaamde server RARP, beschikt over een bestand met de koppeling fysiek adres <--> IP-adres van alle knooppunten. Vervolgens antwoordt hij de afzender van het pakket RARP door zijn IP-adres terug te sturen. Een beheerder die zijn netwerk opnieuw wil configureren, hoeft dus alleen maar het toewijzingsbestand van de server RARP te bewerken. Deze moet normaal gesproken een vast adres IP hebben, dat hij moet kunnen kennen zonder zelf het protocol RARP te hoeven gebruiken.
8.1.6. De zogenaamde IP-netwerklaag van het internet
Het protocol IP (Internet Protocol) definieert de vorm die de pakketten moeten aannemen en de manier waarop ze moeten worden verwerkt bij het verzenden of ontvangen ervan. Dit specifieke type pakket wordt een IP-datagram genoemd. We hebben dit al eerder besproken:

Het belangrijkste is dat het IP-datagram, naast de te verzenden gegevens, ook de internetadressen van de bron- en bestemmingscomputer bevat. Zo weet de bestemmingscomputer wie hem een bericht stuurt.
In tegenstelling tot een netwerkframe, waarvan de lengte wordt bepaald door de fysieke kenmerken van het netwerk waarover het wordt verzonden, wordt de lengte van het datagram IP door de software vastgesteld en zal deze dus op verschillende fysieke netwerken hetzelfde zijn. We hebben gezien dat het datagram IP, naarmate we van de netwerklaag naar de fysieke laag afdalen, in een fysiek frame wordt ingekapseld. We hebben het voorbeeld gegeven van het fysieke frame van een Ethernet-netwerk:

De fysieke frames worden van knooppunt naar knooppunt doorgestuurd naar hun bestemming, die zich mogelijk niet op hetzelfde fysieke netwerk bevindt als de verzendende machine. Het pakket IP kan dus achtereenvolgens in verschillende fysieke frames worden ingekapseld op de knooppunten die de verbinding vormen tussen twee netwerken van verschillende typen. Het is ook mogelijk dat het pakket IP te groot is om in een fysiek frame te worden ingekapseld. De software IP van het knooppunt waar dit probleem zich voordoet, splitst het pakket IP dan volgens precieze regels op in fragments, waarna elk deel afzonderlijk over het fysieke netwerk wordt verzonden. Ze worden pas op hun eindbestemming weer samengevoegd.
8.1.6.1. Routering
Routering is de methode waarmee de pakketten IP naar hun bestemming worden geleid. Er zijn twee methoden: directe routering en indirecte routering.
Directe routering
Directe routering verwijst naar het rechtstreeks doorsturen van een pakket IP van de afzender naar de ontvanger binnen hetzelfde netwerk:
- De machine die een datagram IP verstuurt, beschikt over het adres IP van de ontvanger.
- De machine verkrijgt het fysieke adres van de ontvanger via het protocol ARP of uit haar tabellen, indien dit adres al bekend is.
- Hij verstuurt het pakket via het netwerk naar dit fysieke adres.
Indirecte routering
Indirecte routering verwijst naar het doorsturen van een pakket IP naar een bestemming die zich op een ander netwerk bevindt dan dat van de afzender. In dit geval verschillen de netwerkadresdelen van de adressen IP van de bron- en bestemmingscomputer. De broncomputer herkent dit. Vervolgens stuurt hij het pakket naar een speciaal knooppunt, een zogenaamde router (router). Dit knooppunt verbindt een lokaal netwerk met andere netwerken en het adres IP ervan is in zijn tabellen opgeslagen; dit adres is aanvankelijk verkregen uit een bestand, uit een permanent geheugen of via informatie die over het netwerk circuleert.
Een router is gekoppeld aan twee netwerken en heeft binnen deze twee netwerken een adres IP.

In ons bovenstaande voorbeeld:
- Heeft netwerk nr. 1 het internetadres 193.49.144.0 en netwerk nr. 2 het adres 193.49.145.0.
- Binnen netwerk nr. 1 heeft de router het adres 193.49.144.6 en binnen netwerk nr. 2 het adres 193.49.145.3.
De router heeft als taak het pakket IP dat hij ontvangt – en dat vervat zit in een fysiek frame dat typisch is voor netwerk nr. 1 – om te zetten in een fysiek frame dat over netwerk nr. 2 kan worden verzonden. Als het adres IP van de ontvanger van het pakket zich in netwerk nr. 2 bevindt, stuurt de router het pakket rechtstreeks naar hem; anders stuurt hij het naar een andere router, die netwerk nr. 2 met netwerk nr. 3 verbindt, enzovoort.
8.1.6.2. Fout- en controlemeldingen
Nog steeds in de netwerklaag, dus op hetzelfde niveau als het protocol IP, bestaat het protocol ICMP (Internet Control Message Protocol). Dit protocol dient om berichten te verzenden over de interne werking van het netwerk: defecte knooppunten, opstoppingen bij een router, enzovoort ... De ICMP-berichten worden ingekapseld in IP-pakketten en over het netwerk verzonden. De IP-lagen van de verschillende knooppunten ondernemen de juiste acties op basis van de ICMP-berichten die ze ontvangen. Zo krijgt een applicatie zelf deze netwerkgerelateerde problemen nooit te zien.
Een knooppunt gebruikt de ICMP-informatie om zijn routeringstabellen bij te werken.
8.1.7. De transportlaag: de protocollen UDP en TCP
8.1.7.1. Het protocol UDP: User Datagram Protocol
Het protocol UDP maakt een onbetrouwbare gegevensuitwisseling tussen twee punten mogelijk, wat betekent dat de correcte bezorging van een pakket op de bestemming niet gegarandeerd is. De toepassing kan dit desgewenst zelf beheren, bijvoorbeeld door na het verzenden van een bericht te wachten op een ontvangstbevestiging alvorens het volgende bericht te verzenden.
Tot nu toe hebben we op netwerkniveau gesproken over IP-adressen van machines. Op één machine kunnen echter tegelijkertijd verschillende processen bestaan die allemaal met elkaar kunnen communiceren. Bij het verzenden van een bericht moet daarom niet alleen het adres IP van de ontvangende machine worden opgegeven, maar ook de „naam” van het ontvangende proces. Deze naam is in feite een nummer, het zogenaamde poortnummer. Bepaalde nummers zijn gereserveerd voor standaardtoepassingen: poort 69 voor de tftp-toepassing (trivial file transfer protocol) bijvoorbeeld.
De pakketten die door het UDP-protocol worden verwerkt, worden ook wel datagrammen genoemd. Ze hebben de volgende vorm:

Deze datagrammen worden ingekapseld in IP-pakketten en vervolgens in fysieke frames.
8.1.7.2. Het protocol TCP: Transfer Control Protocol
Voor veilige communicatie volstaat het protocol UDP niet: de applicatieontwikkelaar moet zelf een protocol ontwikkelen waarmee hij kan controleren of de pakketten correct worden doorgestuurd.
Het protocol TCP (Transfer Control Protocol) voorkomt deze problemen. De kenmerken ervan zijn als volgt:
- Het proces dat gegevens wil verzenden, brengt eerst een verbinding tot stand met het proces dat de informatie zal ontvangen. Deze verbinding wordt tot stand gebracht tussen een poort van de verzendende machine en een poort van de ontvangende machine. Tussen beide poorten ontstaat zo een virtueel pad dat uitsluitend is gereserveerd voor de twee processen die de verbinding tot stand hebben gebracht.
- Alle pakketten die door het bronproces worden verzonden, volgen dit virtuele pad en komen aan in de volgorde waarin ze zijn verzonden, wat niet gegarandeerd was in het protocol UDP, aangezien de pakketten verschillende paden konden volgen.
- De verzonden informatie heeft een continu karakter. Het verzendende proces verstuurt informatie in zijn eigen tempo. Deze informatie wordt niet noodzakelijkerwijs onmiddellijk verzonden: het protocol TCP wacht tot er voldoende informatie is verzameld om deze te verzenden. De informatie wordt opgeslagen in een structuur die het TCP-segment wordt genoemd. Zodra dit segment gevuld is, wordt het doorgestuurd naar de laag IP, waar het wordt ingekapseld in een pakket IP.
- Elk segment dat via het protocol TCP wordt verzonden, krijgt een nummer. Het ontvangende protocol TCP controleert of het de segmenten in de juiste volgorde ontvangt. Voor elk correct ontvangen segment stuurt het een ontvangstbevestiging naar de afzender.
- Zodra de afzender deze ontvangstbevestiging ontvangt, geeft hij dit door aan het verzendende proces. Dit proces kan dus vaststellen dat een segment goed is aangekomen, wat met het protocol UDP niet mogelijk was.
- Als het protocol TCP, dat een segment heeft verzonden, na verloop van tijd geen ontvangstbevestiging ontvangt, verzendt het het betreffende segment opnieuw, waardoor de kwaliteit van de dienstverlening bij de doorgifte van de informatie wordt gewaarborgd.
- De virtuele verbinding die tussen de twee communicerende processen tot stand is gebracht, is full-duplex: dit betekent dat de informatie in beide richtingen kan worden verzonden. Zo kan het bestemmingsproces ontvangstbevestigingen versturen terwijl het bronproces doorgaat met het verzenden van informatie. Hierdoor kan bijvoorbeeld het bronprotocol TCP meerdere segmenten verzenden zonder op een ontvangstbevestiging te wachten. Als het na verloop van tijd vaststelt dat het geen ontvangstbevestiging heeft ontvangen voor een bepaald segment met nummer n, zal het vanaf dat punt de verzending van de segmenten hervatten.
8.1.8. De applicatielaag
Bovenop de protocollen UDP en TCP bestaan er diverse standaardprotocollen:
TELNET
Met dit protocol kan een gebruiker van machine A in het netwerk verbinding maken met machine B (vaak de hostmachine genoemd). TELNET emuleert op machine A een zogenaamde universele terminal. De gebruiker gedraagt zich dus alsof hij beschikt over een terminal die is aangesloten op machine B. Telnet maakt gebruik van het protocol TCP.
FTP: (File Transfer Protocol)
Dit protocol maakt de uitwisseling van bestanden tussen twee machines op afstand mogelijk, evenals bestandsbewerkingen zoals het aanmaken van mappen. Het is gebaseerd op het protocol TCP.
TFTP: (Trivial File Transfer Control)
Dit protocol is een variant van FTP. Het is gebaseerd op het protocol UDP en is minder geavanceerd dan FTP.
DNS: (Domain Name System)
Wanneer een gebruiker bestanden wil uitwisselen met een computer op afstand, bijvoorbeeld via FTP, moet hij het internetadres van die computer kennen. Om bijvoorbeeld FTP uit te voeren op de computer Lagaffe van de universiteit van Angers, zou men FTP als volgt moeten starten: FTP 193.49.144.1
Hiervoor is een lijst nodig waarin de machines worden gekoppeld aan de adressen, bijvoorbeeld IP. Waarschijnlijk zouden de machines in deze lijst worden aangeduid met symbolische namen zoals:
machine DPX2/320 van de universiteit van Angers
Sun-computer van de Universiteit van Angers met adres ISERPA
Het is duidelijk dat het prettiger zou zijn om een machine aan te duiden met een naam in plaats van met zijn adres IP. Dit roept echter het probleem op van de uniekheid van de naam: er zijn miljoenen onderling verbonden machines. Men zou zich kunnen voorstellen dat een centrale instantie de namen toekent. Dat zou ongetwijfeld nogal omslachtig zijn. Het beheer van de namen is in feite verdeeld over domeinen. Elk domein wordt beheerd door een doorgaans zeer lichte organisatie die volledige vrijheid heeft bij de keuze van de namen van computers. Zo behoren de computers in Frankrijk tot het domein fr, dat wordt beheerd door het Inria in Parijs. Om de zaken nog verder te vereenvoudigen, wordt het beheer nog verder gedecentraliseerd: er worden domeinen gecreëerd binnen het domein fr. Zo behoort de universiteit van Angers tot het domein univ-Angers. De dienst die dit domein beheert, heeft alle vrijheid om de computers in het netwerk van de Universiteit van Angers een naam te geven. Voorlopig is dit domein nog niet onderverdeeld. Maar bij een grote universiteit met veel computers in het netwerk zou dat wel kunnen gebeuren.
De computer DPX2/320 van de Universiteit van Angers is Lagaffe genoemd, terwijl een PC en een 486DX50 de naam liny hebben gekregen. Hoe kunnen deze machines van buitenaf worden aangeduid? Door de hiërarchie van de domeinen waartoe ze behoren te vermelden. De volledige naam van de machine Lagaffe is dus:
Binnen de domeinen kunnen relatieve namen worden gebruikt. Binnen het domein fr en buiten het domein univ-Angers kan de machine Lagaffe dus worden aangeduid met
Ten slotte kan de machine binnen het domein univ-Angers eenvoudig worden aangeduid met
Een applicatie kan dus naar een machine verwijzen via haar naam. Uiteindelijk moet het internetadres van die machine echter toch worden verkregen. Hoe gebeurt dat? Stel dat we vanaf machine A willen communiceren met machine B.
- Als machine B tot hetzelfde domein behoort als machine A, zal het adres IP waarschijnlijk in een bestand op machine A te vinden zijn.
- Zo niet, dan vindt computer A in een ander bestand – of in hetzelfde bestand als hiervoor – een lijst met enkele naamservers en hun adressen IP. Een naamserver heeft als taak de koppeling te leggen tussen een computernaam en het bijbehorende adres IP. Computer A stuurt een speciaal verzoek naar de eerste naamserver in zijn lijst, een zogenaamd DNS-verzoek, waarin dus de naam van de gezochte computer is opgenomen. Als de geraadpleegde server deze naam in zijn tabellen heeft staan, stuurt hij het bijbehorende adres IP naar computer A. Zo niet, dan zal de server zelf in zijn bestanden een lijst vinden met naamservers die hij kan raadplegen. Dat zal hij dan doen. Zo wordt een aantal naamservers geraadpleegd, niet op willekeurige wijze, maar op een manier die het aantal verzoeken tot een minimum beperkt. Als de machine uiteindelijk wordt gevonden, komt het antwoord terug naar machine A.
XDR: (eXternal Data Representation)
Dit protocol, ontwikkeld door Sun MicroSystems, specificeert een standaard, machine-onafhankelijke gegevensweergave.
RPC: (Remote Procedure Call)
Ook gedefinieerd door Sun; dit is een communicatieprotocol tussen externe applicaties, onafhankelijk van de transportlaag. Dit protocol is belangrijk: het ontlast de programmeur van de noodzaak om de details van de transportlaag te kennen en maakt applicaties draagbaar. Dit protocol is gebaseerd op het protocol XDR
NFS: Network File System
Dit protocol, eveneens gedefinieerd door Sun, stelt een machine in staat om het bestandssysteem van een andere machine te „zien”. Het is gebaseerd op het voorgaande protocol RPC.
8.1.9. Conclusie
In deze inleiding hebben we enkele hoofdlijnen van de internetprotocollen uiteengezet. Voor wie zich verder in dit onderwerp wil verdiepen, is het uitstekende boek van Douglas Comer een aanrader:
Titel: TCP/IP: Architectuur, protocollen, toepassingen.
Auteur: Douglas COMER
Uitgever: InterEditions
8.2. Beheer van netwerkadressen in Java
8.2.1. Definitie
Elke computer op het internet wordt geïdentificeerd door een uniek adres of een unieke naam. Deze twee entiteiten worden in Java beheerd door de klasse InetAddress, waarvan hier enkele methoden worden weergegeven:
geeft de 4 bytes van het adres IP van de huidige instantie InetAddress | |
geeft het adres IP van de huidige instantie InetAddress | |
geeft de internetnaam van de huidige instantie InetAddress | |
geeft de adresidentiteit IP/internetnaam van de huidige instantie InetAddress | |
maakt de instantie InetAddress aan van de machine die wordt aangeduid door Host. Genereert een uitzondering als Host onbekend is. Host kan de internetnaam van een machine zijn of het adres IP in de vorm I1.I2.I3.I4 | |
maakt de instantie InetAddress aan van de machine waarop het programma met deze instructie wordt uitgevoerd. |
8.2.2. Enkele voorbeelden
8.2.2.1. De lokale machine identificeren
import java.net.*;
public class localhost{
public static void main (String arg[]){
try{
InetAddress adresse=InetAddress.getLocalHost();
byte[] IP=adresse.getAddress();
System.out.print("IP=");
int i;
for(i=0;i<IP.length-1;i++) System.out.print(IP[i]+".");
System.out.println(IP[i]);
System.out.println("adresse="+adresse.getHostAddress());
System.out.println("nom="+adresse.getHostName());
System.out.println("identité="+adresse);
} catch (UnknownHostException e){
System.out.println ("Erreur getLocalHost : "+e);
}// einde try
}// einde main
}// einde class
De resultaten van de uitvoering zijn als volgt:
Elke machine heeft een intern adres IP, namelijk 127.0.0.1. Wanneer een programma dit netwerkadres gebruikt, maakt het gebruik van de machine waarop het draait. Het voordeel van dit adres is dat er geen netwerkkaart voor nodig is. Men kan dus netwerkprogramma's testen zonder verbonden te zijn met een netwerk. Een andere manier om de lokale machine aan te duiden is door de naam „localhost“ te gebruiken.
8.2.2.2. Een willekeurige computer identificeren
import java.net.*;
public class getbyname{
public static void main (String arg[]){
String nomMachine;
// het argument wordt opgehaald
if(arg.length==0)
nomMachine="localhost";
else nomMachine=arg[0];
// we proberen het adres van de machine te verkrijgen
try{
InetAddress adresse=InetAddress.getByName(nomMachine);
System.out.println("IP : "+ adresse.getHostAddress());
System.out.println("nom : "+ adresse.getHostName());
System.out.println("identité : "+ adresse);
} catch (UnknownHostException e){
System.out.println ("Erreur getByName : "+e);
}// einde try
}// einde main
}// einde class
Met de Java-aanroep **getbyname** krijgt men de volgende resultaten:
Met de Java-aanroep **getbyname shiva.istia.univ-angers.fr** krijgt men:
Met de Java-aanroep getbyname www.ibm.com krijgt men:
8.3. Communicaties TCP-IP
8.3.1. Algemeen

Wanneer een toepassing AppA op machine A wil communiceren met een toepassing AppB op machine B via het internet, moet deze verschillende gegevens kennen:
- het adres IP of de naam van machine B
- het poortnummer waarmee de applicatie AppB werkt. Computer B kan namelijk talrijke applicaties ondersteunen die via het internet werken. Wanneer deze computer informatie uit het netwerk ontvangt, moet hij weten voor welke applicatie deze informatie bestemd is. De applicaties op machine B hebben toegang tot het netwerk via poorten, ook wel communicatiepoorten genoemd. Deze informatie is opgenomen in het pakket dat door machine B wordt ontvangen, zodat het aan de juiste applicatie kan worden afgeleverd.
- De communicatieprotocollen die door machine B worden begrepen. In onze studie zullen we uitsluitend de protocollen TCP-IP gebruiken.
- het dialoogprotocol dat door de toepassing AppB wordt geaccepteerd. Machine A en machine B gaan namelijk met elkaar ‘praten’. Wat ze gaan zeggen, wordt ingekapseld in de protocollen TCP-IP. Wanneer echter aan het einde van de keten de applicatie AppB de door de applicatie AppA verzonden informatie ontvangt, moet zij deze kunnen interpreteren. Dit is vergelijkbaar met de situatie waarin twee personen, A en B, via de telefoon communiceren: hun gesprek wordt via de telefoon overgebracht. De spraak wordt door telefoon A in de vorm van signalen gecodeerd, via telefoonlijnen overgebracht en komt bij telefoon B aan om daar te worden gedecodeerd. Persoon B hoort dan gesproken woorden. Hier komt het begrip dialoogprotocol om de hoek kijken: als A Frans spreekt en B deze taal niet begrijpt, kunnen A en B geen zinvolle dialoog voeren.
Daarom moeten de twee communicerende applicaties het eens zijn over het type dialoog dat ze gaan hanteren. Zo is de dialoog met een dienst ftp bijvoorbeeld niet dezelfde als met een dienst pop: deze twee diensten accepteren niet dezelfde commando’s. Ze hebben een verschillend communicatieprotocol.
8.3.2. De kenmerken van het protocol TCP
We zullen hier alleen netwerkcommunicatie behandelen die gebruikmaakt van het transportprotocol TCP. Laten we hier nogmaals de kenmerken van dit protocol in herinnering brengen:
- Het proces dat gegevens wil verzenden, brengt eerst een verbinding tot stand met het proces dat de informatie zal ontvangen. Deze verbinding wordt tot stand gebracht tussen een poort van de verzendende machine en een poort van de ontvangende machine. Tussen beide poorten ontstaat zo een virtueel pad dat uitsluitend is gereserveerd voor de twee processen die de verbinding tot stand hebben gebracht.
- Alle pakketten die door het bronproces worden verzonden, volgen dit virtuele pad en komen aan in de volgorde waarin ze zijn verzonden
- De verzonden informatie heeft een continu karakter. Het verzendende proces verstuurt informatie in zijn eigen tempo. Deze informatie wordt niet noodzakelijkerwijs onmiddellijk verzonden: het protocol TCP wacht tot er voldoende informatie is verzameld om deze te verzenden. De informatie wordt opgeslagen in een structuur die het TCP-segment wordt genoemd. Zodra dit segment gevuld is, wordt het doorgestuurd naar de laag IP, waar het wordt ingekapseld in een pakket IP.
- Elk segment dat via het protocol TCP wordt verzonden, is genummerd. Het ontvangende protocol TCP controleert of het de segmenten in de juiste volgorde ontvangt. Voor elk correct ontvangen segment stuurt het een ontvangstbevestiging naar de afzender.
- Zodra de afzender deze ontvangstbevestiging ontvangt, geeft hij dit door aan het verzendende proces. Dit proces weet dan dat een segment goed is aangekomen.
- Als het protocol TCP, dat een segment heeft verzonden, na verloop van tijd geen ontvangstbevestiging ontvangt, verzendt het het betreffende segment opnieuw, waardoor de kwaliteit van de dienst voor het doorsturen van informatie wordt gewaarborgd.
- De virtuele verbinding die tussen de twee communicerende processen tot stand is gebracht, is full-duplex: dit betekent dat de informatie in beide richtingen kan worden verzonden. Zo kan het bestemmingsproces ontvangstbevestigingen versturen terwijl het bronproces doorgaat met het verzenden van informatie. Hierdoor kan bijvoorbeeld het bronprotocol TCP meerdere segmenten verzenden zonder op een ontvangstbevestiging te wachten. Als het na verloop van tijd vaststelt dat het geen ontvangstbevestiging heeft ontvangen voor een bepaald segment met nummer n, zal het vanaf dat punt de verzending van de segmenten hervatten.
8.3.3. De client-serverrelatie
Vaak is de communicatie via internet asymmetrisch: machine A initieert een verbinding om een dienst aan te vragen bij machine B; hij geeft aan dat hij een verbinding wil openen met de dienst SB1 van machine B. Deze accepteert of weigert het verzoek. Als machine B accepteert, kan machine A zijn verzoeken naar de dienst SB1 sturen. Deze verzoeken moeten voldoen aan het communicatieprotocol dat door de dienst SB1 wordt begrepen. Zo ontstaat een vraag-antwoorddialoog tussen machine A, die we de clientmachine noemen, en machine B, die we de servermachine noemen. Een van beide partners zal de verbinding verbreken.
8.3.4. Architectuur van een client
De architectuur van een netwerkprogramma dat gebruikmaakt van de diensten van een servertoepassing ziet er als volgt uit:
ouvrir la connexion avec le service SB1 de la machine B
si réussite alors
tant que ce n'est pas fini
préparer une demande
l'émettre vers la machine B
attendre et récupérer la réponse
la traiter
fin tant que
finsi
8.3.5. Architectuur van een server
De architectuur van een programma dat diensten aanbiedt, ziet er als volgt uit:
ouvrir le service sur la machine locale
tant que le service est ouvert
se mettre à l'écoute des demandes de connexion sur un port dit port d'écoute
lorsqu'il y a une demande, la faire traiter par une autre tâche sur un autre port dit port de service
fin tant que
Het serverprogramma behandelt het eerste verbindingsverzoek van een klant anders dan zijn latere verzoeken om een dienst te verkrijgen. Het programma levert de dienst zelf niet. Als het dat wel zou doen, zou het gedurende de looptijd van de dienst niet meer luisteren naar verbindingsverzoeken en zouden klanten dan niet bediend worden. Het gaat daarom anders te werk: zodra een verbindingsverzoek wordt ontvangen op de luisterpoort en vervolgens wordt geaccepteerd, maakt de server een taak aan die verantwoordelijk is voor het leveren van de door de client gevraagde dienst. Deze dienst wordt geleverd op een andere poort van de server, de zogenaamde servicepoort. Zo kunnen meerdere clients tegelijkertijd worden bediend.
Een servicetaken heeft de volgende structuur:
tant que le service n'a pas été rendu totalement
attendre une demande sur le port de service
lorsqu'il y en a une, élaborer la réponse
transmettre la réponse via le port de service
fin tant que
libérer le port de service
8.3.6. De Socket-klasse
8.3.6.1. Définition
De basisfunctie die wordt gebruikt door programma's die via het internet communiceren, is de socket. Dit Engelse woord betekent 'stopcontact'. Het wordt hier uitgebreid tot 'netwerkaansluiting'. Om informatie via het internet te kunnen verzenden en ontvangen, heeft een applicatie een netwerkaansluiting nodig, een socket. Dit hulpmiddel is oorspronkelijk ontwikkeld in de Unix-versies van de Universiteit van Berkeley. Het is sindsdien overgezet naar alle Unix-systemen en naar de Windows-wereld. Het bestaat ook op Java-virtuele machines in twee vormen: de klasse Socket voor clienttoepassingen en de klasse ServerSocket voor servertoepassingen. Hier lichten we enkele constructors en methoden van de klasse Socket toe:
opent een externe verbinding met poort port van de machine host |
geeft het nummer van de lokale poort weer die door de socket wordt gebruikt | |||
geeft het nummer van de externe poort weer waarmee de socket is verbonden | |||
geeft het lokale adres weer waaraan de socket is gekoppeld | |||
geeft het adres InetAddress van de externe verbinding weer waaraan de socket is gekoppeld | |||
maakt een invoerstroom aan waarmee de door de externe partner verzonden gegevens kunnen worden gelezen | |||
levert een uitvoerstroom waarmee gegevens naar de externe partner kunnen worden verzonden | |||
sluit de invoerstroom van de socket | |||
sluit de uitgaande stream van de socket | |||
sluit de socket en de bijbehorende I/O-stromen | |||
geeft een tekenreeks terug die de socket "vertegenwoordigt" | |||
8.3.6.2. Een verbinding openen met een server
We hebben gezien dat machine A twee gegevens nodig heeft om een verbinding te openen met een service op machine B:
- het adres IP of de naam van machine B
- het poortnummer waarop de gewenste dienst actief is
De constructor
maakt een socket aan en verbindt deze met machine host op poort port. Deze constructor genereert in verschillende gevallen een uitzondering:
- verkeerd adres
- verkeerde poort
- verzoek afgewezen
- …
We moeten deze uitzondering afhandelen:
Socket sClient=null;
try{
sClient=new Socket(host,port);
} catch(Exception e){
// de verbinding is mislukt - de fout wordt afgehandeld
….
}
Als de verbindingsaanvraag slaagt, krijgt de client lokaal een poort toegewezen om met machine B te communiceren. Zodra de verbinding tot stand is gebracht, kan deze poort worden achterhaald met de methode:
Als de verbinding tot stand komt, hebben we gezien dat de server de dienst laat verzorgen door een andere taak die op een zogenaamde servicepoort draait. Dit poortnummer kan worden achterhaald met de methode:
8.3.6.3. Informatie via het netwerk verzenden
Met de volgende methode kan een schrijfstroom naar de socket en dus naar het netwerk worden verkregen:
Alles wat via deze stream wordt verzonden, wordt ontvangen op de servicepoort van de server. Veel applicaties hebben een dialoog in de vorm van tekstregels die eindigen met een regeleinde. Daarom is de methode println in dergelijke gevallen erg handig. We zetten de uitgangsstroom OutputStream dan om in de stroom PrintWriter, die de methode println heeft. Het schrijven kan een uitzondering genereren.
8.3.6.4. Informatie van het netwerk lezen
Je kunt een leesstroom van de informatie die binnenkomt via de socket verkrijgen met de methode:
Alles wat in deze stream wordt gelezen, is afkomstig van de servicepoort van de server. Voor toepassingen met een dialoog in de vorm van tekstregels die eindigen met een regeleinde, is het raadzaam de methode readLine te gebruiken. Hiervoor zetten we de invoerstroom InputStream om in de stroom BufferedReader, die de methode readLine() bevat. Het lezen kan een uitzondering genereren.
8.3.6.5. De verbinding afsluiten
Dit gebeurt met de methode:
De methode kan een uitzondering genereren. De gebruikte bronnen, met name de netwerkpoort, worden vrijgegeven.
8.3.6.6. De architectuur van de client
We beschikken nu over de elementen om de basisarchitectuur van een internetclient te beschrijven:
Socket sClient=null;
try{
// er wordt verbinding gemaakt met de dienst die draait op poort P van machine M
sClient=new Socket(M,P);
// de in- en uitgangsstromen van de clientsocket worden aangemaakt
BufferedReader in=new BufferedReader(new InputStreamReader(sClient.getInputStream()));
PrintWriter out=new PrintWriter(sClient.getOutputStream(),true);
// verzoek-antwoordlus
boolean fini=false;
String demande;
String réponse;
while (! fini){
// het verzoek wordt voorbereid
demande=…
// het verzoek wordt verzonden
out.println(demande);
// het antwoord wordt gelezen
réponse=in.readLine();
// het antwoord wordt verwerkt
…
}
// het is klaar
sClient.close();
} catch(Exception e){
// de uitzondering wordt afgehandeld
….
}
Om het voorbeeld niet te ingewikkeld te maken, hebben we geen poging gedaan om de verschillende soorten uitzonderingen af te handelen die worden gegenereerd door de constructor Socket of de methoden readline, getInputStream, getOutputStream en close. Alles is samengevoegd tot één enkele uitzondering.
8.3.7. De klasse ServerSocket
8.3.7.1. Définition
Deze klasse is bedoeld voor het beheer van sockets aan de serverzijde. Hier lichten we enkele constructors en methoden van deze klasse toe:
maakt een luistersocket aan op poort port | |
hetzelfde, maar stelt in count de grootte van de wachtrij in, c.a.d. het maximale aantal clientverbindingen dat in de wachtrij wordt geplaatst als de server bezet is wanneer de clientverbinding binnenkomt. |
geeft het nummer van de luisterpoort weer die door de socket wordt gebruikt | |
geeft het lokale adres InetAddress weer waaraan de socket is gekoppeld | |
zet de server in de wachtstand voor een verbinding (blokkerende bewerking). Bij het tot stand komen van een verbinding met een client, retourneert het een socket waarmee de client wordt bediend. | |
sluit de socket en de bijbehorende I/O-stromen | |
retourneert een tekenreeks die de socket „vertegenwoordigt“ | |
sluit de servicesocket en maakt de bijbehorende bronnen vrij |
8.3.7.2. De service openen
Dit gebeurt met de volgende twee constructors:
port is de luisterpoort van de service: de poort waar clients hun verbindingsverzoeken naar sturen. count is de maximale grootte van de wachtrij van de service (standaard 50), waarin de verbindingsverzoeken van clients worden opgeslagen waarop de server nog niet heeft gereageerd. Wanneer de wachtrij vol is, worden binnenkomende verbindingsverzoeken afgewezen. Beide constructies genereren een uitzondering.
8.3.7.3. Een verbindingsverzoek accepteren
Wanneer een client een verbindingsverzoek indient op de luisterpoort van de service, accepteert deze het verzoek met de methode:
Deze methode retourneert een instantie van Socket: dit is de servicesocket, via welke de service wordt geleverd, meestal door een andere taak. De methode kan een uitzondering genereren.
8.3.7.4. Lezen/schrijven via de servicesocket
Aangezien de servicesocket een instantie is van de klasse Socket, verwijzen we naar de voorgaande paragrafen waarin dit onderwerp is behandeld.
8.3.7.5. De klant identificeren
Zodra de servicesocket is verkregen, kan de klant worden geïdentificeerd met de methode
uit de klasse Socket. Hiermee krijgt men toegang tot het adres IP en de naam van de klant.
8.3.7.6. De service afsluiten
Dit gebeurt met de methode
van de klasse ServerSocket. Hierdoor worden de bezette bronnen vrijgegeven, met name de luisterpoort. De methode kan een uitzondering genereren.
8.3.7.7. Basisstructuur van een server
Op basis van het bovenstaande kunnen we de basisstructuur van een server als volgt weergeven:
SocketServer sEcoute=null;
try{
// de service wordt geopend
int portEcoute=…
int maxConnexions=…
sEcoute=new ServerSocket(portEcoute,maxConnexions);
// verwerking van inlogverzoeken
boolean fini=false;
Socket sService=null;
while( ! fini){
// in behandeling nemen en goedkeuren van een aanvraag
sService=sEcoute.accept();
// de dienst wordt uitgevoerd door een andere taak waaraan de dienstsocket wordt doorgegeven
new Service(sService).start();
// er wordt opnieuw gewacht op verbindingsverzoeken
}
// het is klaar – de service wordt afgesloten
sEcoute.close();
} catch (Exception e){
// de uitzondering wordt afgehandeld
…
}
De klasse Service is een thread die er als volgt uit zou kunnen zien:
public class Service extends Thread{
Socket sService; // de servicesocket
// constructor
public Service(Socket S){
sService=S;
}
// uitvoeren
public void run(){
try{
// de invoer- en uitvoerstromen worden aangemaakt
BufferedReader in=new BufferedReader(new InputStreamReader(sService.getInputStream()));
PrinttWriter out=new PrintWriter(sService.getOutputStream(),true);
// verzoek-antwoordlus
boolean fini=false;
String demande;
String réponse;
while (! fini){
// het verzoek wordt gelezen
demande=in.readLine();
// de aanvraag wordt verwerkt
…
// het antwoord wordt voorbereid
réponse=…
// het antwoord wordt verzonden
out.println(réponse);
}
// het is voltooid
sService.close();
} catch(Exception e){
// de uitzondering wordt afgehandeld
….
}// try
} // uitvoeren
8.4. Toepassingen
8.4.1. Echo-server
We gaan een echo-server schrijven die vanuit een venster met de naam DOS wordt gestart met het commando:
De server draait op de poort die als parameter wordt doorgegeven. Hij stuurt de client gewoon het verzoek terug dat deze hem heeft gestuurd, samen met zijn identiteit (IP+naam). Hij accepteert 2 verbindingen in zijn wachtrij. Hiermee hebben we alle onderdelen van een TCP-server. Het programma ziet er als volgt uit:
// aanroep: serveurEcho poort
// echo-server
// stuurt de regel die de client heeft verzonden terug naar de client
import java.net.*;
import java.io.*;
public class serveurEcho{
public final static String syntaxe="Syntaxe : serveurEcho port";
public final static int nbConnexions=2;
// hoofdprogramma
public static void main (String arg[]){
// is er een argument
if(arg.length != 1)
erreur(syntaxe,1);
// dit argument moet een geheel getal >0 zijn
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(arg[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// de luistersocket wordt aangemaakt
ServerSocket ecoute=null;
try{
ecoute=new ServerSocket(port,nbConnexions);
} catch (Exception e){
erreur("Erreur lors de la création de la socket d'écoute ("+e+")",3);
}
// vervolg
System.out.println("Serveur d'écho lancé sur le port " + port);
// servicelus
boolean serviceFini=false;
Socket service=null;
while (! serviceFini){
// wachten op een client
try{
service=ecoute.accept();
} catch (IOException e){
erreur("Erreur lors de l'acceptation d'une connexion ("+e+")",4);
}
// de verbinding wordt geïdentificeerd
try{
System.out.println("Client ["+identifie(service.getInetAddress())+","+
service.getPort()+"] connecté au serveur [" + identifie (InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
erreur("identification liaison",1);
}
// de service wordt verzorgd door een andere taak
new traiteClientEcho(service).start();
}// einde while
}// einde main
// fouten weergeven
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// identificeert
private static String identifie(InetAddress Host){
// hostidentificatie
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// einde class
// verzorgt de service voor een klant van de echo-server
class traiteClientEcho extends Thread{
private Socket service; // servicesocket
private BufferedReader in; // invoerstroom
private PrintWriter out; // uitgangssignaal
// constructor
public traiteClientEcho(Socket service){
this.service=service;
}
// run-methode
public void run(){
// aanmaken van invoer- en uitvoerstromen
try{
in=new BufferedReader(new InputStreamReader(service.getInputStream()));
} catch (IOException e){
erreur("Erreur lors de la création du flux déentrée de la socket de service ("+e+")",1);
}// einde try
try{
out=new PrintWriter(service.getOutputStream(),true);
} catch (IOException e){
erreur("Erreur lors de la création du flux de sortie de la socket de service ("+e+")",1);
}// einde try
// de identificatie van de verbinding wordt naar de client verzonden
try{
out.println("Client ["+identifie(service.getInetAddress())+","+
service.getPort()+"] connecté au serveur [" + identifie (InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
erreur("identification liaison",1);
}
// lus voor het lezen van verzoeken en het schrijven van antwoorden
String demande,reponse;
try{
// de service stopt wanneer de client een einde-van-bestand-markering verstuurt
while ((demande=in.readLine())!=null){
// echo van het verzoek
reponse="["+demande+"]";
out.println(reponse);
// de service stopt wanneer de client "einde" verstuurt
if(demande.trim().toLowerCase().equals("fin")) break;
}// einde while
} catch (IOException e){
erreur("Erreur lors des échanges client/serveur ("+e+")",3);
}// einde try
// de socket wordt gesloten
try{
service.close();
} catch (IOException e){
erreur("Erreur lors de la fermeture de la socket de service ("+e+")",2);
}// einde try
}// einde run
// fouten weergeven
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// einde fout
// identificatie
private String identifie(InetAddress Host){
// hostidentificatie
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// einde klasse
De twee klassen die nodig zijn voor de service zijn in één bronbestand samengebracht. Slechts één ervan, namelijk die met de functie main, heeft het attribuut public. De structuur van de server komt overeen met de algemene architectuur van TCP-servers. Er is een methode (identifie) toegevoegd waarmee de verbinding tussen de server en een client kan worden geïdentificeerd. Hier volgen enkele resultaten:
De server wordt gestart met het commando
Vervolgens geeft hij in het controlevenster het volgende bericht weer:
Om deze server te testen, gebruiken we het programma telnet, dat zowel onder Unix als onder Windows beschikbaar is. Telnet is een universele TCP-client die geschikt is voor alle servers die in hun communicatie tekstregels accepteren die eindigen met een regeleinde-teken. Dat is het geval bij onze echo-server. We starten een eerste telnet-client onder Windows (in dit voorbeeld Windows 2000) door telnet in te typen in een DOS-venster:
DOS>telnet
Microsoft (R) Windows 2000 (TM) version 5.00 (numéro 2195)
Client Telnet Microsoft
Client Telnet numéro 5.00.99203.1
Le caractère d'échappement est 'CTRL+$'
Microsoft Telnet> help
Les commandes peuvent être abrégées. Les commandes prises en charge sont :
close ferme la connexion en cours
display affiche les paramètres d'opération
open ouvre une connexion à un site
quit quitte telnet
set définit les options (entrez 'set ?' pour afficher la liste)
status affiche les informations d'état
unset annule les options (entrez 'unset ?' pour afficher la liste)
? ou help affiche des informations d'aide
Microsoft Telnet> set ?
NTLM Active l'authentification NTLM.
LOCAL_ECHO Active l'écho local.
TERM x (où x est ANSI, VT100, VT52 ou VTNT))
CRLF Envoi de CR et de LF
Microsoft Telnet> set local_echo
Microsoft Telnet> open localhost 187
Het programma telnet geeft standaard geen echo weer van de opdrachten die via het toetsenbord worden ingevoerd. Om deze echo te krijgen, voert u de volgende opdracht uit:
Om een verbinding met de server te openen, waarbij de poort van de echo-service (187) en het adres van de machine waarop deze zich bevindt (localhost) worden opgegeven, voer je het volgende commando uit:
In het DOS-venster van de client verschijnt dan het volgende bericht:
In het servervenster verschijnt het bericht:
Serveur d'écho lancé sur le port 187
Client [127.0.0.1,tahe,1059] connecté au serveur [127.0.0.1,tahe,187]
Hier verwijzen tahe en localhost naar dezelfde machine. In het clientvenster telnet kun je tekstregels invoeren. De server geeft deze weer:
Client [127.0.0.1,tahe,1059] connectÚ au serveur [127.0.0.1,tahe,187]
je suis là
[je suis là]
au revoir
[au revoir]
Merk op dat de poort van de client (1059) correct wordt gedetecteerd, maar dat de servicepoort (187) identiek is aan de luisterpoort (187), wat onverwacht is. Men zou namelijk verwachten dat de poort van de servicesocket wordt verkregen en niet de luisterpoort. Er zou moeten worden gecontroleerd of men onder Unix dezelfde resultaten krijgt. Laten we nu een tweede client starten: telnet. Het servervenster ziet er dan als volgt uit:
Serveur d'écho lancé sur le port 187
Client [127.0.0.1,tahe,1059] connecté au serveur [127.0.0.1,tahe,187]
Client [127.0.0.1,tahe,1060] connecté au serveur [127.0.0.1,tahe,187]
In het venster van de tweede client kun je ook tekstregels invoeren:
Client [127.0.0.1,tahe,1060] connecté au serveur [127.0.0.1,tahe,187]
ligne1
[ligne1]
ligne2
[ligne2]
Zo zien we dat de echo-server meerdere clients tegelijk kan bedienen. De clients telnet kunnen worden beëindigd door het DOS-venster te sluiten waarin ze worden uitgevoerd.
8.4.2. Een Java-client voor de echo-server
In het vorige deel hebben we een telnet-client gebruikt om de echo-service te testen. Nu gaan we onze eigen client schrijven:
// aanroep: clientEcho machinepoort
// client van de echo-server
// stuurt regels naar de server, die deze terugkaatst
import java.net.*;
import java.io.*;
public class clientEcho{
public final static String syntaxe="Syntaxe : clientEcho machine port";
// hoofdprogramma
public static void main (String arg[]){
// zijn er twee argumenten
if(arg.length != 2)
erreur(syntaxe,1);
// het eerste argument moet de naam van een bestaande machine zijn
String machine=arg[0];
InetAddress serveurAddress=null;
try{
serveurAddress=InetAddress.getByName(machine);
} catch (Exception e){
erreur(syntaxe+"\nMachine "+machine+" inaccessible (" + e +")",2);
}
// de poort moet een geheel getal >0 zijn
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(arg[1]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\nPort incorrect ("+E+")",3);
// er wordt verbinding gemaakt met de server
Socket sClient=null;
try{
sClient=new Socket(machine,port);
} catch (Exception e){
erreur("Erreur lors de la création de la socket de communication ("+e+")",4);
}
// de verbinding wordt geïdentificeerd
try{
System.out.println("Client : Client ["+identifie(InetAddress.getLocalHost())+","+
sClient.getLocalPort()+"] connecté au serveur [" + identifie (sClient.getInetAddress())
+ "," + sClient.getPort() + "]");
} catch (Exception e) {
erreur("identification liaison ("+e+")",5);
}
// de leesstroom voor de toetsaanslagen wordt aangemaakt
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
erreur("Création du flux d'entrée clavier ("+e+")",6);
}
// de invoerstroom voor de clientsocket wordt aangemaakt
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(sClient.getInputStream()));
} catch (Exception e){
erreur("Création du flux d'entrée de la socket client("+e+")",7);
}
// de uitvoerstroom aanmaken die bij de clientsocket hoort
PrintWriter out=null;
try{
out=new PrintWriter(sClient.getOutputStream(),true);
} catch (Exception e){
erreur("Création du flux de sortie de la socket ("+e+")",8);
}
// lus van verzoeken en antwoorden
boolean serviceFini=false;
String demande=null;
String reponse=null;
// het door de server verzonden bericht wordt direct na het tot stand brengen van de verbinding gelezen
try{
reponse=in.readLine();
} catch (IOException e){
erreur("Lecture réponse ("+e+")",4);
}
// weergave van het antwoord
System.out.println("Serveur : " +reponse);
while (! serviceFini){
// een via het toetsenbord ingevoerde regel lezen
System.out.print("Client : ");
try{
demande=IN.readLine();
} catch (Exception e){
erreur("Lecture ligne ("+e+")",9);
}
// verzoek verzenden via het netwerk
try{
out.println(demande);
} catch (Exception e){
erreur("Envoi demande ("+e+")",10);
}
// wachten op/lezen van het antwoord
try{
reponse=in.readLine();
} catch (IOException e){
erreur("Lecture réponse ("+e+")",4);
}
// antwoord weergeven
System.out.println("Serveur : " +reponse);
// is het klaar?
if(demande.trim().toLowerCase().equals("fin")) serviceFini=true;
}
// het is klaar
try{
sClient.close();
} catch(Exception e){
erreur("Fermeture socket ("+e+")",11);
}
}// hand
// fouten weergeven
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// identificeren
private static String identifie(InetAddress Host){
// host-ID
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// einde klasse
De structuur van deze client komt overeen met de algemene architectuur van de tcp-clients. Hier zijn de verschillende mogelijke uitzonderingen één voor één afgehandeld, wat het programma zwaarder maakt. Dit zijn de resultaten die worden verkregen bij het testen van deze client:
Client : Client [127.0.0.1,tahe,1045] connecté au serveur [127.0.0.1,localhost,187]
Serveur : Client [127.0.0.1,localhost,1045] connectÚ au serveur [127.0.0.1,tahe,187]
Client : 123
Serveur : [123]
Client : abcd
Serveur : [abcd]
Client : je suis là
Serveur : [je suis là]
Client : fin
Serveur : [fin]
De regels die beginnen met Client zijn de regels die door de client zijn verzonden en de regels die beginnen met Serveur zijn de regels die door de server zijn teruggestuurd.
8.4.3. Een generieke TCP-client
Veel diensten die in de beginjaren van het internet zijn ontstaan, werken volgens het eerder besproken echo-servermodel: de communicatie tussen client en server verloopt via de uitwisseling van tekstregels. We gaan een generieke TCP-client schrijven die als volgt wordt gestart: java cltTCPgenerique server poort
Deze client TCP maakt verbinding met poort port van de server serveur. Vervolgens maakt hij twee threads aan:
- een thread die de op het toetsenbord ingevoerde opdrachten leest en deze naar de server verstuurt
- een thread die de antwoorden van de server leest en deze op het scherm weergeeft
Waarom twee threads, terwijl dat in de vorige toepassing niet nodig was? In die toepassing was het communicatieprotocol bekend: de client stuurde één regel en de server antwoordde met één regel. Elke dienst heeft zijn eigen specifieke protocol en er zijn ook de volgende situaties:
- de client moet meerdere regels tekst verzenden voordat hij een antwoord krijgt
- het antwoord van een server kan uit meerdere regels tekst bestaan
Daarom is de lus waarbij één regel naar de server wordt verzonden en vervolgens één regel van de server wordt ontvangen, niet altijd geschikt. We gaan daarom twee afzonderlijke lussen maken:
- een lus voor het lezen van de op het toetsenbord ingevoerde opdrachten die naar de server moeten worden verzonden. De gebruiker geeft het einde van de opdrachten aan met het trefwoord fin.
- een lus voor het ontvangen en weergeven van de antwoorden van de server. Dit wordt een oneindige lus die alleen wordt onderbroken wanneer de netwerkverbinding door de server wordt verbroken of wanneer de gebruiker via het toetsenbord het commando fin invoert.
Om deze twee lussen van elkaar te scheiden, hebben we twee onafhankelijke threads nodig. Laten we een uitvoervoorbeeld bekijken waarin onze generieke TCP-client verbinding maakt met een dienst SMTP (SendMail Transfer Protocol). Deze dienst is verantwoordelijk voor het doorsturen van e-mail naar de ontvangers. Hij draait op poort 25 en maakt gebruik van een communicatieprotocol waarbij tekstregels worden uitgewisseld.
Dos>java clientTCPgenerique istia.univ-angers.fr 25
Commandes :
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
help
<-- 502 5.3.0 Sendmail 8.11.6 -- HELP not implemented
mail from: machin@univ-angers.fr
<-- 250 2.1.0 machin@univ-angers.fr... Sender ok
rcpt to: serge.tahe@istia.univ-angers.fr
<-- 250 2.1.5 serge.tahe@istia.univ-angers.fr... Recipient ok
data
<-- 354 Enter mail, end with "." on a line by itself
Subject: test
ligne1
ligne2
ligne3
.
<-- 250 2.0.0 g4D6bks25951 Message accepted for delivery
quit
<-- 221 2.0.0 istia.univ-angers.fr closing connection
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Laten we deze client-server-uitwisselingen eens bekijken:
- de service SMTP verstuurt een welkomstbericht wanneer een client verbinding maakt:
- sommige services hebben een commando help dat informatie geeft over de commando's die met de service kunnen worden gebruikt. Hier is dat niet het geval. De commando's SMTP die in het voorbeeld worden gebruikt, zijn de volgende:
- mail from: expéditeur, om het e-mailadres van de afzender van het bericht aan te geven
- rcpt to: destinataire, om het e-mailadres van de ontvanger van het bericht aan te geven. Als er meerdere ontvangers zijn, wordt het commando rcpt to: zo vaak als nodig is herhaald voor elke ontvanger.
- data, waarmee aan de server SMTP wordt aangegeven dat het bericht wordt verzonden. Zoals aangegeven in het antwoord van de server, bestaat dit bericht uit een reeks regels die eindigt met een regel die uitsluitend uit een punt bestaat. Een bericht kan headers bevatten die door een lege regel van de hoofdtekst worden gescheiden. In ons voorbeeld hebben we een onderwerp opgegeven met het trefwoord Subject:
- zodra het bericht is verzonden, kan men de server laten weten dat men klaar is met het commando quit. De server verbreekt dan de netwerkverbinding. De leesthread kan deze gebeurtenis detecteren en stoppen.
- De gebruiker typt vervolgens ‘fin’ op het toetsenbord om ook de thread die de ingevoerde opdrachten leest, te stoppen.
Als we de ontvangen e-mail controleren, zien we het volgende (Outlook):

Merk op dat de dienst SMTP niet kan vaststellen of een afzender geldig is of niet. Daarom kun je het veld from in een bericht nooit vertrouwen. In dit geval bestond de afzender machin@univ-angers.fr niet.
Met deze generieke TCP-client kunnen we het communicatieprotocol van internetdiensten achterhalen en op basis daarvan gespecialiseerde klassen bouwen voor clients van deze diensten. Laten we het communicatieprotocol van de dienst POP (Post Office Protocol) onderzoeken, waarmee e-mails die op een server zijn opgeslagen, kunnen worden opgehaald. Deze dienst werkt op poort 110.
Dos> java clientTCPgenerique istia.univ-angers.fr 110
Commandes :
<-- +OK Qpopper (version 4.0.3) at istia.univ-angers.fr starting.
help
<-- -ERR Unknown command: "help".
user st
<-- +OK Password required for st.
pass monpassword
<-- +OK st has 157 visible messages (0 hidden) in 11755927 octets.
list
<-- +OK 157 visible messages (11755927 octets)
<-- 1 892847
<-- 2 171661
...
<-- 156 2843
<-- 157 2796
<-- .
retr 157
<-- +OK 2796 octets
<-- Received: from lagaffe.univ-angers.fr (lagaffe.univ-angers.fr [193.49.144.1])
<-- by istia.univ-angers.fr (8.11.6/8.9.3) with ESMTP id g4D6wZs26600;
<-- Mon, 13 May 2002 08:58:35 +0200
<-- Received: from jaume ([193.49.146.242])
<-- by lagaffe.univ-angers.fr (8.11.1/8.11.2/GeO20000215) with SMTP id g4D6wSd37691;
<-- Mon, 13 May 2002 08:58:28 +0200 (CEST)
...
<-- ------------------------------------------------------------------------
<-- NOC-RENATER2 Tl. : 0800 77 47 95
<-- Fax : (+33) 01 40 78 64 00 , Email : noc-r2@cssi.renater.fr
<-- ------------------------------------------------------------------------
<--
<-- .
quit
<-- +OK Pop server at istia.univ-angers.fr signing off.
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
De belangrijkste commando's zijn de volgende:
- user login, waarbij je je login opgeeft op de machine waarop je e-mails staan
- wachtwoord password, waarbij je het wachtwoord opgeeft dat bij de vorige login hoort
- list, om de lijst met berichten te krijgen in de vorm van nummer en grootte in bytes
- retr i, om bericht nr. i te lezen
- quit, om de dialoog te beëindigen.
Laten we nu eens kijken naar het communicatieprotocol tussen een client en een webserver, die doorgaans op poort 80 draait:
Dos> java clientTCPgenerique istia.univ-angers.fr 80
Commandes :
GET /index.html HTTP/1.0
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
<--
<-- <head>
<-- <meta http-equiv="Content-Type"
<-- content="text/html; charset=iso-8859-1">
<-- <meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
<-- <title>Bienvenue a l'ISTIA - Universite d'Angers</title>
<-- </head>
....
<-- face="Verdana"> - Dernire mise jour le <b>10 janvier 2002</b></font></p>
<-- </body>
<-- </html>
<--
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Een webclient stuurt zijn opdrachten naar de server volgens het volgende schema:
Pas nadat de lege regel is ontvangen, reageert de webserver. In het voorbeeld hebben we slechts één commando gebruikt:
waarmee de server wordt gevraagd om URL /index.html en wordt aangegeven dat er wordt gewerkt met het protocol HTTP versie 1.0. De meest recente versie van dit protocol is 1.1. Het voorbeeld laat zien dat de server heeft gereageerd door de inhoud van het bestand index.html terug te sturen en vervolgens de verbinding heeft verbroken, aangezien we zien dat de thread voor het lezen van de antwoorden is beëindigd. Voordat de inhoud van het bestand index.html werd verzonden, heeft de webserver een reeks headers verzonden, afgesloten met een lege regel:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
De regel <html> is de eerste regel van het bestand /index.html. Het bovenstaande wordt de headers HTTP (HyperText Transfer Protocol) genoemd. We gaan hier niet in detail op deze headers ingaan, maar onthoud dat onze generieke client hier toegang toe biedt, wat nuttig kan zijn om ze te begrijpen. De eerste regel bijvoorbeeld:
geeft aan dat de benaderde webserver het protocol HTTP/1.1 ondersteunt en dat hij het gevraagde bestand inderdaad heeft gevonden (200 OK), waarbij 200 een HTTP-antwoordcode is. De regels
geven de client door dat hij 11251 bytes zal ontvangen die de tekst HTML (HyperText Markup Language) vertegenwoordigen en dat de verbinding aan het einde van de verzending zal worden verbroken.
Dit is dus een erg handige TCP-client. Hij doet ongetwijfeld minder dan het programma telnet dat we eerder hebben gebruikt, maar het was interessant om hem zelf te schrijven. Het programma van de generieke TCP-client is als volgt:
// geïmporteerde pakketten
import java.io.*;
import java.net.*;
public class clientTCPgenerique{
// ontvangt als parameter de kenmerken van een service in de vorm van
// serverpoort
// maakt verbinding met de service
// maakt een thread aan om op het toetsenbord ingevoerde opdrachten te lezen
// deze worden naar de server verzonden
// maakt een thread aan om de antwoorden van de server te lezen
// deze worden op het scherm weergegeven
// het geheel wordt beëindigd met het commando 'fin' dat via het toetsenbord wordt ingevoerd
// instantievariabele
private static Socket client;
public static void main(String[] args){
// syntaxis
final String syntaxe="pg serveur port";
// aantal argumenten
if(args.length != 2)
erreur(syntaxe,1);
// de naam van de server wordt genoteerd
String serveur=args[0];
// de poort moet een geheel getal >0 zijn
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[1]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
client=null;
// er kunnen problemen optreden
try{
// er wordt verbinding gemaakt met de dienst
client=new Socket(serveur,port);
}catch(Exception ex){
// fout
erreur("Impossible de se connecter au service ("+ serveur
+","+port+"), erreur : "+ex.getMessage(),3);
// einde
return;
}//catch
// de lees- en schrijfthreads worden aangemaakt
new ClientSend(client).start();
new ClientReceive(client).start();
// einde hoofdthread
return;
}// main
// foutmeldingen weergeven
public static void erreur(String msg, int exitCode){
// fout weergeven
System.err.println(msg);
// afsluiten met fout
System.exit(exitCode);
}//fout
}//klasse
class ClientSend extends Thread {
// klasse die verantwoordelijk is voor het lezen van via het toetsenbord ingevoerde opdrachten
// en deze via een als parameter doorgegeven TCP-client naar een server te verzenden
private Socket client; // de TCP-client
// constructor
public ClientSend(Socket client){
// hier wordt de TCP-client vastgelegd
this.client=client;
}//constructor
// de Run-methode van de thread
public void run(){
// lokale gegevens
PrintWriter OUT=null; // netwerk-schrijfstroom
BufferedReader IN=null; // toetsenbordstroom
String commande=null; // op het toetsenbord ingevoerde opdracht
// foutbeheer
try{
// aanmaken van de netwerkschrijfstroom
OUT=new PrintWriter(client.getOutputStream(),true);
// aanmaken van de toetsenbordinvoerstroom
IN=new BufferedReader(new InputStreamReader(System.in));
// lus voor invoer en verzending van opdrachten
System.out.println("Commandes : ");
while(true){
// lezen van via het toetsenbord ingevoerde opdracht
commande=IN.readLine().trim();
// klaar?
if (commande.toLowerCase().equals("fin")) break;
// commando naar de server verzenden
OUT.println(commande);
// volgend commando
}//while
}catch(Exception ex){
// fout
System.err.println("Envoi : L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// einde - de streams worden gesloten
try{
OUT.close();client.close();
}catch(Exception ex){}
// het einde van de thread wordt gemeld
System.out.println("[Envoi : fin du thread d'envoi des commandes au serveur]");
}//run
}//klasse
class ClientReceive extends Thread{
// klasse die verantwoordelijk is voor het lezen van de tekstregels bestemd voor een
// TCP-client die als parameter is doorgegeven
private Socket client; // de TCP-client
// constructor
public ClientReceive(Socket client){
// de TCP-client wordt geregistreerd
this.client=client;
}//constructor
// Run-methode van de thread
public void run(){
// lokale gegevens
BufferedReader IN=null; // netwerkleesstroom
String réponse=null; // serverantwoord
// foutbeheer
try{
// aanmaken van de netwerkleesstroom
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// lus voor het lezen van tekstregels uit de stream IN
while(true){
// netwerkstream lezen
réponse=IN.readLine();
// stream gesloten?
if(réponse==null) break;
// weergave
System.out.println("<-- "+réponse);
}//while
}catch(Exception ex){
// fout
System.err.println("Réception : L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// einde - de streams worden gesloten
try{
IN.close();client.close();
}catch(Exception ex){}
// het einde van de thread wordt gemeld
System.out.println("[Réception : fin du thread de lecture des réponses du serveur]");
}//run
}//klasse
8.4.4. Een generieke TCP-server
Nu richten we ons op een server
- die de door zijn clients verzonden commando's op het scherm weergeeft
- en als antwoord de tekstregels terugstuurt die door een gebruiker via het toetsenbord zijn ingevoerd. De gebruiker fungeert dus als server.
Het programma wordt gestart met: java serveurTCPgenerique portEcoute, waarbij portEcoute de poort is waarop de clients verbinding moeten maken. De dienstverlening aan de client wordt verzorgd door twee threads:
- een thread die zich uitsluitend bezighoudt met het lezen van de door de client verzonden tekstregels
- een thread die zich uitsluitend bezighoudt met het lezen van de antwoorden die de gebruiker via het toetsenbord invoert. Deze thread geeft met het commando `fin` aan dat de verbinding met de client wordt beëindigd.
De server maakt twee threads per klant aan. Als er n klanten zijn, zullen er 2n threads tegelijkertijd actief zijn. De server stopt zelf nooit, tenzij de gebruiker op het toetsenbord op Ctrl-C drukt. Laten we enkele voorbeelden bekijken.
De server wordt gestart op poort 100 en we gebruiken de generieke client om met de server te communiceren. Het venster van de client ziet er als volgt uit:
E:\data\serge\MSNET\c#\netwerk\generieke tcp-client> java clientTCPgenerique localhost 100
Commandes :
commande 1 du client 1
<-- réponse 1 au client 1
commande 2 du client 1
<-- réponse 2 au client 1
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]
De regels die beginnen met <-- zijn de regels die van de server naar de client worden verzonden, de andere zijn die van de client naar de server. Het servervenster ziet er als volgt uit:
Dos> java serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au client 1]
De regels die beginnen met <-- zijn de regels die van de client naar de server zijn verzonden. De regels N: zijn de regels die van de server naar client nr. N zijn verzonden. De bovenstaande server is nog steeds actief, terwijl client 1 is beëindigd. We starten een tweede client voor dezelfde server:
Dos> java clientTCPgenerique localhost 100
Commandes :
commande 3 du client 2
<-- réponse 3 au client 2
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]
Het servervenster ziet er dan als volgt uit:
Dos> java serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au client 1]
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- commande 3 du client 2
réponse 3 au client 2
2 : [fin du Thread de lecture des demandes du client 2]
fin
[fin du Thread de lecture des réponses du serveur au client 2]
^C
Laten we nu een webserver simuleren door onze generieke server op poort 88 te starten:
Dos> java serveurTCPgenerique 88
Serveur générique lancé sur le port 88
Laten we nu een browser openen en de pagina URL http://localhost:88/exemple.html opvragen. De browser maakt vervolgens verbinding met poort 88 van de machine localhost en vraagt vervolgens de pagina /exemple.html op:

Laten we nu eens kijken naar het venster van onze server:
Dos>java serveurTCPgenerique 88
Serveur générique lancé sur le port 88
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- GET /exemple.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2
914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
Zo ontdekken we de headers HTTP die door de browser worden verzonden. Hierdoor kunnen we het protocol HTTP beetje bij beetje ontrafelen. In een eerder voorbeeld hadden we een webclient gemaakt die slechts één commando verzond: GET. Dat was toen voldoende. We zien hier dat de browser nog andere informatie naar de server stuurt. Deze informatie is bedoeld om de server te laten weten met welk type client hij te maken heeft. We zien ook dat de headers HTTP eindigen met een lege regel.
Laten we een antwoord voor onze client opstellen. De gebruiker achter het toetsenbord is hier de echte server en kan met de hand een antwoord opstellen. Laten we ons het antwoord van een webserver in een eerder voorbeeld nog eens in herinnering brengen:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
Laten we eens proberen een soortgelijk antwoord te geven:
...
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
2 : HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :
2 : <html>
2 : <head><title>Serveur generique</title></head>
2 : <body>
2 : <center>
2 : <h2>Reponse du serveur generique</h2>
2 : </center>
2 : </body>
2 : </html>
2 : fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du Thread de lecture des demandes du client 2]
[fin du Thread de lecture des réponses du serveur au client 2]
Regels die beginnen met 2: worden vanaf de server naar client nr. 2 verzonden. Het commando fin verbreekt de verbinding tussen de server en de client. In ons antwoord hebben we ons beperkt tot de volgende HTTP-headers:
HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :
We geven de bestandsgrootte van het bestand dat we gaan verzenden (Content-Length) niet aan, maar we vermelden alleen dat we de verbinding zullen verbreken (Connection: close) nadat dit bestand is verzonden. Dit is voldoende voor de browser. Zodra de verbinding wordt verbroken, weet de browser dat het antwoord van de server is voltooid en geeft hij de pagina HTML weer die naar hem is verzonden. Deze pagina ziet er als volgt uit:
2 : <html>
2 : <head><title>Serveur generique</title></head>
2 : <body>
2 : <center>
2 : <h2>Reponse du serveur generique</h2>
2 : </center>
2 : </body>
2 : </html>
De gebruiker verbreekt vervolgens de verbinding met de client door het commando fin in te voeren. De browser weet dan dat het antwoord van de server is voltooid en kan het vervolgens weergeven:

Als we hierboven View/Source invoeren om te zien wat de browser heeft ontvangen, krijgen we:

dat wil zeggen precies wat we vanaf de generieke server hebben verzonden.
De code van de generieke TCP-server is als volgt:
// pakketten
import java.io.*;
import java.net.*;
public class serveurTCPgenerique{
// hoofdprogramma
public static void main (String[] args){
// ontvangt verzoeken van klanten op de luisterpoort
// maakt een thread aan om de verzoeken van de client te lezen
// deze worden op het scherm weergegeven
// maakt een thread aan om op het toetsenbord ingevoerde opdrachten te lezen
// deze worden als antwoord naar de klant verzonden
// het geheel wordt afgesloten met het commando 'fin' dat via het toetsenbord wordt ingevoerd
final String syntaxe="Syntaxe : pg port";
// instantievariabele
// is er een argument
if(args.length != 1)
erreur(syntaxe,1);
// de poort moet een geheel getal >0 zijn
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// de luisterdienst wordt aangemaakt
ServerSocket ecoute=null;
int nbClients=0; // aantal verwerkte clients
try{
// de service wordt aangemaakt
ecoute=new ServerSocket(port);
// opvolging
System.out.println("Serveur générique lancé sur le port " + port);
// lus voor klantenservice
Socket client=null;
while (true){ // oneindige lus – wordt gestopt met Ctrl-C
// wachten op een klant
client=ecoute.accept();
// de service wordt uitgevoerd door afzonderlijke threads
nbClients++;
// de lees-/schrijfthreads worden aangemaakt
new ServeurSend(client,nbClients).start();
new ServeurReceive(client,nbClients).start();
// we gaan weer over tot het afwachten van verzoeken
}// einde while
}catch(Exception ex){
// de fout wordt gemeld
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);
}//catch
}// einde main
// fouten weergeven
public static void erreur(String msg, int exitCode){
// fout weergeven
System.err.println(msg);
// afsluiten met fout
System.exit(exitCode);
}//fout
}//klasse
class ServeurSend extends Thread{
// klasse die verantwoordelijk is voor het lezen van antwoorden die via het toetsenbord worden ingevoerd
// en deze via een aan de constructor doorgegeven TCP-client naar een client te verzenden
Socket client; // de TCP-client
int numClient; // clientnummer
// constructor
public ServeurSend(Socket client, int numClient){
// we noteren de TCP-client
this.client=client;
// en het nummer ervan
this.numClient=numClient;
}//fabrikant
// Run-methode van de thread
public void run(){
// lokale gegevens
PrintWriter OUT=null; // netwerk-schrijfstroom
String réponse=null; // van het toetsenbord gelezen antwoord
BufferedReader IN=null; // toetsenbordstroom
// tracking
System.out.println("Thread de lecture des réponses du serveur au client "+ numClient + " lancé");
// foutbeheer
try{
// aanmaken van de netwerkschrijfstroom
OUT=new PrintWriter(client.getOutputStream(),true);
// aanmaken van de toetsenbordstroom
IN=new BufferedReader(new InputStreamReader(System.in));
// lus voor invoer en verzending van opdrachten
while(true){
// klantidentificatie
System.out.print("--> " + numClient + " : ");
// uitlezen van via het toetsenbord ingevoerde reactie
réponse=IN.readLine().trim();
// klaar?
if (réponse.toLowerCase().equals("fin")) break;
// antwoord naar de server verzenden
OUT.println(réponse);
// volgend antwoord
}//while
}catch(Exception ex){
// fout
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// einde - de streams worden gesloten
try{
OUT.close();client.close();
}catch(Exception ex){}
// het einde van de thread wordt gemeld
System.out.println("[fin du Thread de lecture des réponses du serveur au client "+ numClient+ "]");
}//run
}//klasse
class ServeurReceive extends Thread{
// klasse die verantwoordelijk is voor het lezen van de naar de server verzonden tekstregels
// via een TCP-client die aan de constructor wordt doorgegeven
Socket client; // de TCP-client
int numClient; // clientnummer
// constructor
public ServeurReceive(Socket client, int numClient){
// de TCP-client wordt genoteerd
this.client=client;
// en het nummer ervan
this.numClient=numClient;
}//fabrikant
// Run-methode van de thread
public void run(){
// lokale gegevens
BufferedReader IN=null; // netwerkleesstroom
String réponse=null; // serverantwoord
// monitoring
System.out.println("Thread de lecture des demandes du client "+ numClient + " lancé");
// foutbeheer
try{
// aanmaken van de netwerkleesstroom
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// lus voor het lezen van tekstregels uit de stream IN
while(true){
// netwerkstream lezen
réponse=IN.readLine();
// stream gesloten?
if(réponse==null) break;
// weergave
System.out.println("<-- "+réponse);
}//while
}catch(Exception ex){
// fout
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// einde – de streams worden gesloten
try{
IN.close();client.close();
}catch(Exception ex){}
// het einde van de thread wordt gemeld
System.out.println("[fin du Thread de lecture des demandes du client "+ numClient+"]");
}//run
}//klasse
8.4.5. Een webclient
In het vorige voorbeeld hebben we enkele van de HTTP-headers gezien die een browser verstuurde:
<-- GET /exemple.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2
914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
We gaan een webclient schrijven waaraan we een URL als parameter doorgeven en die de inhoud van deze URL op het scherm weergeeft. We gaan ervan uit dat de webserver die voor de URL wordt benaderd, het protocol HTTP 1.1 ondersteunt. Van de bovenstaande headers gebruiken we alleen de volgende:
- de eerste header geeft aan welke pagina we willen
- de tweede welke server we benaderen
- de derde dat we willen dat de server de verbinding verbreekt nadat hij ons heeft geantwoord.
Als we hierboven GET vervangen door HEAD, zal de server ons alleen de headers HTTP sturen en niet de pagina HTML.
Onze webclient wordt als volgt aangeroepen: java clientweb URL cmd, waarbij URL degewenste URL is en cmd een van de twee trefwoorden GET of HEAD om aan te geven of we alleen de headers (HEAD) willen of ook de inhoud van de pagina (GET). Laten we een eerste voorbeeld bekijken. We starten de server IIS en vervolgens de webclient op dezelfde machine:
dos>java clientweb http://localhost HEAD
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.0
Date: Mon, 13 May 2002 09:23:37 GMT
Connection: close
Location: /IISSamples/Default/welcome.htm
Content-Length: 189
Content-Type: text/html
Set-Cookie: ASPSESSIONIDGQQQGUUY=HMFNCCMDECBJJBPPBHAOAJNP; path=/
Cache-control: private
Het antwoord
betekent dat de opgevraagde pagina van plaats is veranderd (dus van URL). De nieuwe URL wordt aangegeven door de Location:-header:
Als we GET gebruiken in plaats van HEAD in de aanroep naar de webclient:
dos>java clientweb http://localhost GET
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.0
Date: Mon, 13 May 2002 09:33:36 GMT
Connection: close
Location: /IISSamples/Default/welcome.htm
Content-Length: 189
Content-Type: text/html
Set-Cookie: ASPSESSIONIDGQQQGUUY=IMFNCCMDAKPNNGMGMFIHENFE; path=/
Cache-control: private
<head><title>L'objet a changé d'emplacement</title></head>
<body><h1>L'objet a changé d'emplacement</h1>Cet objet peut être trouvé <a HREF="/IISSamples/Default/we
lcome.htm">ici</a>.</body>
We krijgen hetzelfde resultaat als met HEAD, met daarnaast de hoofdtekst van de pagina HTML. Het programma is als volgt:
// geïmporteerde pakketten
import java.io.*;
import java.net.*;
public class clientweb{
// vraagt om een URL
// geeft de inhoud ervan weer op het scherm
public static void main(String[] args){
// syntaxis
final String syntaxe="pg URI GET/HEAD";
// aantal argumenten
if(args.length != 2)
erreur(syntaxe,1);
// de gevraagde URI wordt genoteerd
String URLString=args[0];
String commande=args[1].toUpperCase();
// controle van de geldigheid van de URI
URL url=null;
try{
url=new URL(URLString);
}catch (Exception ex){
// URI is onjuist
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),2);
}//opvang
// controle van de bestelling
if(! commande.equals("GET") && ! commande.equals("HEAD")){
// onjuiste bestelling
erreur("Le second paramètre doit être GET ou HEAD",3);
}
// de relevante gegevens worden uit URL gehaald
String path=url.getPath();
if(path.equals("")) path="/";
String query=url.getQuery();
if(query!=null) query="?"+query; else query="";
String host=url.getHost();
int port=url.getPort();
if(port==-1) port=url.getDefaultPort();
// we kunnen aan de slag
Socket client=null; // de klant
BufferedReader IN=null; // de leesstroom van de klant
PrintWriter OUT=null; // de schrijfstroom van de klant
String réponse=null; // antwoord van de server
try{
// er wordt verbinding gemaakt met de server
client=new Socket(host,port);
// de invoer- en uitvoerstromen van de klant worden aangemaakt TCP
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT=new PrintWriter(client.getOutputStream(),true);
// URL wordt opgevraagd - verzending van de headers HTTP
OUT.println(commande + " " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println();
// het antwoord wordt gelezen
while((réponse=IN.readLine())!=null){
// het antwoord wordt verwerkt
System.out.println(réponse);
}//while
// het is klaar
client.close();
} catch(Exception e){
// de uitzondering wordt afgehandeld
erreur(e.getMessage(),4);
}//catch
}//main
// foutmeldingen weergeven
public static void erreur(String msg, int exitCode){
// fout weergeven
System.err.println(msg);
// afsluiten met fout
System.exit(exitCode);
}//fout
}//klasse
Het enige nieuwe in dit programma is het gebruik van de klasse URL. Het programma ontvangt een URL (Uniform Resource Locator) of URI (Uniform Resource Identifier) in de vorm http://serveur:port/cheminPageHTML?param1=val1;param2=val2;.... Met de klasse URL kunnen we de tekenreeks van de URL opsplitsen in de verschillende onderdelen. Er wordt een object URL aangemaakt op basis van de tekenreeks URLstring die als parameter is ontvangen:
// geldigheid van de URL controleren
URL url=null;
try{
url=new URL(URLString);
}catch (Exception ex){
// URI onjuist
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),2);
}//catch
Als de als parameter ontvangen tekenreeks URL geen geldige URL is (ontbrekend protocol, ontbrekende server, ...), wordt er een uitzondering gegenereerd. Zo kunnen we de geldigheid van de ontvangen parameter controleren. Zodra het object URL is aangemaakt, hebben we toegang tot de verschillende elementen ervan. Als het object url uit de vorige code dus is aangemaakt op basis van de tekenreeks
dan hebben we:
url.getHost()=serveur
url.getPort()=port of -1 als de poort niet is opgegeven
url.getPath()=cheminPageHTML of de lege tekenreeks als er geen pad is
url.getQuery()=param1=val1;param2=val2;... of null als er geen verzoek is
uri.getProtocol()=http
8.4.6. Webclient die omleidingen afhandelt
De vorige webclient verwerkt geen eventuele omleiding van de URL die hij heeft aangevraagd. De volgende client verwerkt deze wel.
- hij leest de eerste regel van de door de server verzonden headers HTTP om te controleren of daarin de tekenreeks 302 Object moved voorkomt, die duidt op een omleiding
- hij leest de volgende headers. Als er een omleiding is, zoekt hij naar de regel „Location: url“ die de nieuwe URL van de opgevraagde pagina aangeeft en noteert hij deze URL.
- het geeft de rest van het antwoord van de server weer. Als er een omleiding is, worden stap 1 tot en met 3 herhaald met de nieuwe URL. Het programma accepteert niet meer dan één omleiding. Deze limiet wordt bepaald door een constante die kan worden aangepast.
Hier volgt een voorbeeld:
Dos>java clientweb2 http://localhost GET
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.0
Date: Mon, 13 May 2002 11:38:55 GMT
Connection: close
Location: /IISSamples/Default/welcome.htm
Content-Length: 189
Content-Type: text/html
Set-Cookie: ASPSESSIONIDGQQQGUUY=PDGNCCMDNCAOFDMPHCJNPBAI; path=/
Cache-control: private
<head><title>L'objet a chang d'emplacement</title></head>
<body><h1>L'objet a chang d'emplacement</h1>Cet objet peut tre trouv <a HREF="/IISSamples/Default/we
lcome.htm">ici</a>.</body>
<--Redirection vers l'URL http://localhost:80/IISSamples/Default/welcome.htm-->
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Connection: close
Date: Mon, 13 May 2002 11:38:55 GMT
Content-Type: text/html
Accept-Ranges: bytes
Last-Modified: Mon, 16 Feb 1998 21:16:22 GMT
ETag: "0174e21203bbd1:978"
Content-Length: 4781
<html>
<head>
<title>Bienvenue dans le Serveur Web personnel</title>
</head>
....
</body>
</html>
Het programma is als volgt:
// geïmporteerde pakketten
import java.io.*;
import java.net.*;
import java.util.regex.*;
public class clientweb2{
// vraagt om een URL
// geeft de inhoud ervan weer op het scherm
public static void main(String[] args){
// syntaxis
final String syntaxe="pg URL GET/HEAD";
// aantal argumenten
if(args.length != 2)
erreur(syntaxe,1);
// de aangevraagde URI wordt genoteerd
String URLString=args[0];
String commande=args[1].toUpperCase();
// controle op de geldigheid van de URI
URL url=null;
try{
url=new URL(URLString);
}catch (Exception ex){
// URI is onjuist
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),2);
}//opgevangen
// controle van de bestelling
if(! commande.equals("GET") && ! commande.equals("HEAD")){
// onjuiste bestelling
erreur("Le second paramètre doit être GET ou HEAD",3);
}
// we kunnen aan de slag
Socket client=null; // de klant
BufferedReader IN=null; // de leesstroom van de klant
PrintWriter OUT=null; // de schrijfstroom van de klant
String réponse=null; // antwoord van de server
final int nbRedirsMax=1; // er wordt niet meer dan één omleiding toegestaan
int nbRedirs=0; // aantal lopende omleidingen
String premièreLigne; // eerste regel van het antwoord
boolean redir=false; // geeft aan of er al dan niet een omleiding is
String locationString=""; // de tekenreeks URL van een eventuele omleiding
// reguliere expressie om een omleidingsstring URL te vinden
Pattern location=Pattern.compile("^Location: (.+?)$");
// foutafhandeling
try{
// er kunnen meerdere URL-strings worden opgevraagd als er omleidingen zijn
while(nbRedirs<=nbRedirsMax){
// we halen de nuttige informatie uit de URL
String protocol=url.getProtocol();
String path=url.getPath();
if(path.equals("")) path="/";
String query=url.getQuery();
if(query!=null) query="?"+query; else query="";
String host=url.getHost();
int port=url.getPort();
if(port==-1) port=url.getDefaultPort();
// er wordt verbinding gemaakt met de server
client=new Socket(host,port);
// we maken de in- en uitgaande stromen van de client TCP aan
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT=new PrintWriter(client.getOutputStream(),true);
// URL wordt opgevraagd - verzending van de headers HTTP
OUT.println(commande + " " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println();
// de eerste regel van het antwoord wordt gelezen
premièreLigne=IN.readLine();
// schermuitvoer
System.out.println(premièreLigne);
// omleiding?
if(premièreLigne.endsWith("302 Object moved")){
// er is een omleiding
redir=true;
nbRedirs++;
}//if
// de volgende HTTP-headers totdat de lege regel wordt gevonden die het einde van de headers aangeeft
boolean locationFound=false;
while(!(réponse=IN.readLine()).equals("")){
// het antwoord wordt weergegeven
System.out.println(réponse);
// als er een omleiding is, zoek je naar de Location-header
if(redir && ! locationFound){
// de regel wordt vergeleken met de relationele uitdrukking location
Matcher résultat=location.matcher(réponse);
if(résultat.find()){
// als deze is gevonden, noteer dan de omleidingscode URL
locationString=résultat.group(1);
// er wordt genoteerd dat er iets is gevonden
locationFound=true;
}//if
}//als
// volgende header
}//terwijl
// de volgende regels van het antwoord
System.out.println(réponse);
while((réponse=IN.readLine())!=null){
// het antwoord wordt weergegeven
System.out.println(réponse);
}//while
// de verbinding wordt verbroken
client.close();
// zijn we klaar?
if ( ! locationFound || nbRedirs>nbRedirsMax)
break;
// er moet een omleiding worden uitgevoerd – de nieuwe wordt opgebouwd URL
URLString=protocol +"://"+host+":"+port+locationString;
url=new URL(URLString);
// tracking
System.out.println("\n<--Redirection vers l'URL "+URLString+"-->\n");
}//terwijl
} catch(Exception e){
// de uitzondering wordt afgehandeld
erreur(e.getMessage(),4);
}//catch
}//main
// foutmeldingen weergeven
public static void erreur(String msg, int exitCode){
// fout weergeven
System.err.println(msg);
// afsluiten met fout
System.exit(exitCode);
}//fout
}//klasse
8.4.7. Server voor belastingberekening
We gaan verder met de oefening IMPOTS, die al in verschillende vormen is behandeld. Laten we de laatste versie nog eens bekijken:
Er is een basisklasse voor belastingen aangemaakt. De attributen daarvan bestaan uit drie tabellen met getallen:
public class impots{
// de gegevens die nodig zijn voor de berekening van de belasting
// zijn afkomstig van een externe bron
protected double[] limites=null;
protected double[] coeffR=null;
protected double[] coeffN=null;
// veld ‘fabrikant’ leeg
protected impots(){}
// constructor
public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception{
De klasse impots heeft twee constructors:
- een constructor waaraan de drie gegevensarrays worden doorgegeven die nodig zijn voor de berekening van de belasting
- een constructor zonder parameters die alleen door onderliggende klassen kan worden gebruikt
Van deze klasse is de klasse impotsJDBC afgeleid, waarmee de drie tabellen limites, coeffR en coeffN kunnen worden gevuld op basis van de inhoud van een database:
public class impotsJDBC extends impots{
// toevoeging van een constructor waarmee kan worden geconstrueerd
// de tabellen ‘limites’, ‘coeffr’ en ‘coeffn’ op basis van de tabel
// belastingen uit een database
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
// dsnIMPOTS: naam DSN van de database
// userIMPOTS, mdpIMPOTS: gebruikersnaam/wachtwoord voor toegang tot de database
Er was een grafische applicatie geschreven. De applicatie maakte gebruik van een object van de klasse impotsJDBC. De applicatie en dit object bevonden zich op dezelfde machine. We zijn van plan om het testprogramma en het object impotsJDBC op verschillende machines te plaatsen. We krijgen dan een client-server-toepassing waarbij het externe object impotsJDBC als server fungeert. De nieuwe klasse heet ServeurImpots en is afgeleid van de klasse impotsJDBC:
// geïmporteerde pakketten
import java.net.*;
import java.io.*;
import java.sql.*;
public class ServeurImpots extends impotsJDBC {
// attributen
int portEcoute; // de poort waarop wordt geluisterd naar verzoeken van klanten
boolean actif; // serverstatus
// constructor
public ServeurImpots(int portEcoute,String DSNimpots, String USERimpots, String MDPimpots)
throws IOException, SQLException, ClassNotFoundException {
// bovenliggende constructie
super(DSNimpots, USERimpots, MDPimpots);
// de luisterpoort wordt genoteerd
this.portEcoute=portEcoute;
// momenteel inactief
actif=false;
// maakt een thread aan en start deze om de via het toetsenbord ingevoerde opdrachten te lezen
// de server wordt op basis van deze opdrachten beheerd
Thread admin=new Thread(){
public void run(){
try{
admin();
}catch (Exception ignored){}
}
};
admin.start();
}//ServeurImpots
De enige nieuwe parameter in de constructor is de poort waarop naar verzoeken van clients wordt geluisterd. De overige parameters worden rechtstreeks doorgegeven aan de basisklasse impotsJDBC. De belasting-server wordt aangestuurd door commando's die via het toetsenbord worden ingevoerd. Daarom wordt er een thread aangemaakt om deze commando's te lezen. Er zijn twee mogelijke commando’s: start om de dienst te starten en stop om deze definitief te stoppen. De methode admin die deze commando’s afhandelt, is als volgt:
public void admin() throws IOException{
// leest de via het toetsenbord ingevoerde beheercommando's van de server
// in een eindeloze lus
String commande=null;
BufferedReader IN=new BufferedReader(new InputStreamReader(System.in));
while(true){
// vraagt om invoer
System.out.print("Serveur d'impôts>");
// commando lezen
commande=IN.readLine().trim().toLowerCase();
// commando uitvoeren
if(commande.equals("start")){
// actief?
if(actif){
//fout
System.out.println("Le serveur est déjà actif");
// we gaan verder
continue;
}//if
// de luisterservice wordt aangemaakt en gestart
Thread ecoute=new Thread(){
public void run(){
ecoute();
}
};
ecoute.start();
}//if
else if(commande.equals("stop")){
// einde van alle uitvoeringsthreads
System.exit(0);
}//als
else {
// fout
System.out.println("Commande incorrecte. Utilisez (start,stop)");
}//if
}//while
}//admin
Als het via het toetsenbord ingevoerde commando start is, wordt een thread gestart die luistert naar verzoeken van klanten. Als het ingevoerde commando stop is, worden alle threads gestopt. De luisterthread voert de methode ecoute uit:
public void ecoute(){
// thread voor het afhandelen van verzoeken van klanten
// de luisterservice wordt aangemaakt
ServerSocket ecoute=null;
try{
// de service wordt aangemaakt
ecoute=new ServerSocket(portEcoute);
// opvolging
System.out.println("Serveur d'impôts lancé sur le port " + portEcoute);
// servicelus
Socket liaisonClient=null;
while (true){ // oneindige lus
// wachten op een klant
liaisonClient=ecoute.accept();
// de service wordt door een andere taak verzorgd
new traiteClientImpots(liaisonClient,this).start();
// we gaan weer over op het afwachten van verzoeken
}// einde while
}catch(Exception ex){
// de fout wordt gemeld
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);
}//catch
}//luisterthread
Er is een klassieke TCP-server die luistert op poort portEcoute. Verzoeken van clients worden verwerkt door de methode run van de thread traiteCientImpots, aan de constructor waarvan twee parameters worden doorgegeven:
- het Socket-object liaisonClient waarmee de client kan worden bereikt
- het object impotsJDBC this, dat toegang geeft tot de methode this.calculer voor het berekenen van de belasting.
// -------------------------------------------------------
// verzorgt de dienstverlening aan een klant van de belastingserver
class traiteClientImpots extends Thread{
private Socket liaisonClient; // verbinding met de klant
private BufferedReader IN; // invoerstroom
private PrintWriter OUT; // uitgaande stroom
private impotsJDBC objImpots; // belastingobject
// constructor
public traiteClientImpots(Socket liaisonClient,impotsJDBC objImpots){
this.liaisonClient=liaisonClient;
this.objImpots=objImpots;
}//bouwer
De methode run verwerkt verzoeken van klanten. Dit zijn tekstregels die twee vormen kunnen aannemen:
- berekening gehuwd (j/n) nbEnfants salaireAnnuel
- fincalculs
Vorm 1 maakt het mogelijk om belasting te berekenen, vorm 2 verbreekt de verbinding tussen client en server.
// run-methode
public void run(){
// levert de dienst aan de klant
try{
// invoerstroom
IN=new BufferedReader(new InputStreamReader(liaisonClient.getInputStream()));
// uitvoerstroom
OUT=new PrintWriter(liaisonClient.getOutputStream(),true);
// een welkomstbericht naar de klant sturen
OUT.println("Bienvenue sur le serveur d'impôts");
// lus voor het lezen van verzoeken en het schrijven van antwoorden
String demande=null;
String[] champs=null; // de onderdelen van het verzoek
String commande=null; // het commando van de klant: berekening of einde berekeningen
while ((demande=IN.readLine())!=null){
// het verzoek wordt opgesplitst in velden
champs=demande.trim().toLowerCase().split("\\s+");
// twee geaccepteerde verzoeken: berekening en eindberekeningen
commande=champs[0];
if(! commande.equals("calcul") && ! commande.equals("fincalculs")){
// fout bij de klant
OUT.println("Commande incorrecte. Utilisez (calcul,fincalculs).");
// volgende bestelling
continue;
}//if
if(commande.equals("calcul")) calculerImpôt(champs);
if(commande.equals("fincalculs")){
// afscheidsbericht aan de klant
OUT.println("Au revoir...");
// vrijgeven van bronnen
try{ OUT.close();IN.close();liaisonClient.close();}
catch(Exception ex){}
// einde
return;
}//if
//volgende aanvraag
}//while
}catch (Exception e){
erreur("L'erreur suivante s'est produite ("+e+")",2);
}// einde try
}// einde Run
De belasting wordt berekend met behulp van de methode calculerImpôt, die als parameter de tabel met velden uit de aanvraag van de klant ontvangt. De geldigheid van de aanvraag wordt gecontroleerd en eventueel wordt de belasting berekend en teruggestuurd naar de klant.
// belastingberekening
public void calculerImpôt(String[] champs){
// verwerkt de aanvraag: berekening gehuwden nbEnfants salaireAnnuel
// opgesplitst in velden in de veldtabel
String marié=null;
int nbEnfants=0;
int salaireAnnuel=0;
// geldigheid van de argumenten
try{
// er zijn minimaal 4 velden nodig
if(champs.length!=4) throw new Exception();
// getrouwd
marié=champs[1];
if (! marié.equals("o") && ! marié.equals("n")) throw new Exception();
// kinderen
nbEnfants=Integer.parseInt(champs[2]);
// salaris
salaireAnnuel=Integer.parseInt(champs[3]);
}catch (Exception ignored){
// formaatfout
OUT.println(" syntaxe : calcul marié(O/N) nbEnfants salaireAnnuel");
// voltooid
return;
}//if
// de belasting kan worden berekend
long impot=objImpots.calculer(marié.equals("o"),nbEnfants,salaireAnnuel);
// het antwoord wordt naar de klant verzonden
OUT.println(""+impot);
}//berekenen
Een testprogramma zou er als volgt uit kunnen zien:
// oproep: serveurImpots poort dsnImpots userImpots mdpImpots
import java.io.*;
public class testServeurImpots{
public static final String syntaxe="Syntaxe : pg port dsnImpots userImpots mdpImpots";
// hoofdprogramma
public static void main (String[] args){
// er zijn 4 argumenten nodig
if(args.length != 4)
erreur(syntaxe,1);
// de poort moet een geheel getal >0 zijn
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// de belasting-server wordt aangemaakt
try{
new ServeurImpots(port,args[1],args[2],args[3]);
}catch(Exception ex){
//fout
System.out.println("L'erreur suivante s'est produite : "+ex.getMessage());
}//catch
}//Main
// fouten weergeven
public static void erreur(String msg, int exitCode){
// fout weergeven
System.err.println(msg);
// afsluiten met fout
System.exit(exitCode);
}//fout
}// einde klasse
We voeren de gegevens die nodig zijn voor het aanmaken van een object ServeurImpots in het testprogramma in, waarna het dit object aanmaakt.
Laten we een eerste uitvoering proberen:
dos>java testServeurImpots 124 mysql-dbimpots admimpots mdpimpots
Serveur d'impôts>start
Serveur d'impôts>Serveur d'écho lancé sur le port 124
stop
Het commando
maakt een object ServeurImpots aan dat nog niet luistert naar verzoeken van clients. Het is het commando start, ingevoerd via het toetsenbord, dat dit luisteren start. Het commando stop stopt de server. Laten we nu een client gebruiken. We gebruiken de eerder aangemaakte generieke client. De server is gestart:
dos>java testServeurImpots 124 mysql-dbimpots admimpots mdpimpots
Serveur d'impôts>start
Serveur d'impôts>Serveur d'écho lancé sur le port 124
De generieke client wordt in een ander DOS-venster gestart:
We zien dat de client het welkomstbericht van de server correct heeft ontvangen. We sturen nog enkele commando's:
x
<-- Commande incorrecte. Utilisez (calcul,fincalculs).
calcul
<-- syntaxe : calcul marié(O/N) nbEnfants salaireAnnuel
calcul o 2 200000
<-- 22506
calcul n 2 200000
<-- 33388
fincalculs
<-- Au revoir...
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
We gaan terug naar het servervenster om de server te stoppen:
dos>java testServeurImpots 124 mysql-dbimpots admimpots mdpimpots
Serveur d'impôts>start
Serveur d'impôts>Serveur d'écho lancé sur le port 124
stop
8.5. Oefeningen
8.5.1. Oefening 1 - Generieke grafische client TCP
8.5.1.1. Presentatie van de applicatie
We willen een programma maken dat via internet kan communiceren met de belangrijkste TCP-diensten. We noemen dit een generieke TCP-client. Als je deze applicatie eenmaal begrijpt, zul je merken dat alle TCP-clients op elkaar lijken. Het programmavenster ziet er als volgt uit:

De betekenis van de verschillende besturingselementen is als volgt:
nr. | naam | type | functie |
1 | TxtRemoteHost | JTextField | naam van de machine die de gewenste dienst aanbiedt |
2 | TxtPort | JTextField | poort van de gevraagde dienst |
3 | TxtSend | JTextField | tekst van het bericht dat door de client naar de server wordt verzonden |
4 | OptRCLF OptLF | JCheckBox | knoppen waarmee kan worden aangegeven hoe de regels in de client/server-dialoog eindigen RCLF: carriage return (#13) + line feed (#10) LF: regeleinde (#10) |
5 | LstSuivi | JList | toont berichten over de communicatiestatus tussen de client en de server |
6 | LstDialogue | JList | geeft de berichten weer die zijn uitgewisseld door de client (->) en de server (<-) |
7 | CmdAnnuler | JButton | verborgen – bevindt zich onder de dialooglijst – Verschijnt wanneer de verbinding actief is en maakt het mogelijk deze te verbreken als de server niet reageert |
De beschikbare menuopties zijn:
optie | subopties | rol |
Verbinding | Verbinden | verbindt de client met de server |
Afmelden | sluit de verbinding af | |
Afsluiten | Sluit het programma af | |
Berichten | Verzenden | Verzendt het bericht van de besturingselement TxtSend naar de server |
RazSuivi | Wist de lijst LstSuivi | |
RazDialogue | Wist de lijst LstDialogue | |
Auteur | toont een copyrightvenster |
8.5.1.2. FONCTIONNEMENT DE De APPLICATION
Wanneer het hoofdscherm van de applicatie wordt geladen, vinden de volgende acties plaats:
- het venster wordt gecentreerd op het scherm
- alleen de menuopties Connexion/Quitter & Auteur zijn actief
- de knop Annuler is verborgen
- de lijsten LstSuivi en LstDialogue zijn leeg
Deze optie is alleen beschikbaar als de velden „Externe host“ en „Poortnummer“ zijn ingevuld en er momenteel geen verbinding actief is. Als u op deze optie klikt, gebeurt het volgende:
- de geldigheid van de poort wordt gecontroleerd: het moet een geheel getal >0 zijn
- er wordt een thread gestart om de verbinding met de server tot stand te brengen
- de knop Annuler verschijnt, zodat de gebruiker de lopende verbinding kan onderbreken
- alle menuopties worden uitgeschakeld, behalve Quitter en Auteur
De verbinding kan op verschillende manieren worden beëindigd:
- De gebruiker heeft op de knop Annuler gedrukt: de verbindingsthread wordt gestopt en het menu wordt teruggezet naar de oorspronkelijke staat. In de log wordt aangegeven dat de verbinding door de gebruiker is beëindigd.
- De verbinding wordt met een fout beëindigd: we doen hetzelfde als hierboven en geven bovendien in de log de oorzaak van de fout aan.
- De verbinding wordt correct beëindigd: de knop Annuler wordt verwijderd, in de log wordt aangegeven dat de verbinding tot stand is gebracht, het menu RazSuivi wordt vrijgegeven, het menu Connecter wordt geblokkeerd en het menu Déconnecter wordt vrijgegeven
Deze optie is alleen beschikbaar wanneer er een verbinding met de server bestaat. Wanneer deze wordt geactiveerd, wordt de verbinding met de server verbroken en wordt het menu teruggezet naar de begintoestand. In de status wordt aangegeven dat de verbinding door de client is verbroken.
Deze optie verbreekt een eventuele actieve verbinding met de server en sluit de applicatie af.
Deze optie is alleen beschikbaar als aan de volgende voorwaarden is voldaan:
-
er is verbinding gemaakt met de server
-
er is een bericht dat verzonden moet worden
Als aan deze voorwaarden is voldaan, wordt de tekst in het veld TxtSend (3) naar de server verzonden, afgesloten met de reeks RCLF als de optie RCLF is aangevinkt, anders met de reeks LF. Een eventuele fout bij het verzenden wordt gemeld in de volglijst.
Wissen respectievelijk de lijsten LstSuivi en LstDialogue. Deze opties zijn uitgeschakeld wanneer de bijbehorende lijsten leeg zijn.
Deze knop onderaan het formulier verschijnt alleen wanneer de klant verbinding probeert te maken met de server. Deze verbinding kan mislukken omdat de server niet of onjuist reageert. De knop Annuler biedt de gebruiker dan de mogelijkheid om het verbindingsverzoek te onderbreken.
De lijst LstSuivi (5) houdt de verbinding bij. Deze geeft de belangrijkste momenten van de verbinding weer:
-
het tot stand brengen ervan door de client
-
het afsluiten ervan door de server of de klant
-
alle fouten die kunnen optreden zolang de verbinding actief is
De lijst LstDialogue (6) houdt de dialoog bij die tussen de client en de server tot stand komt. Een thread houdt op de achtergrond in de gaten wat er op de communicatiesocket van de client gebeurt en geeft dit weer in lijst 6.
Dit menu opent een zogenaamd copyrightvenster:

Verbindingsfouten worden gemeld in de volglijst 6, fouten met betrekking tot de client/server-dialoog in de dialooglijst 7. Bij een verbindingsfout wordt de client/server-dialoog gesloten en wordt het formulier teruggezet in de oorspronkelijke staat, klaar voor een nieuwe verbinding.
8.5.1.3. TRAVAIL naar FAIRE
Voer de hierboven beschreven werkzaamheden uit in twee vormen:
- standalone-toepassing
- applet
8.5.2. Oefening 2 - Een bronnen-server
8.5.2.1. INTRODUCTION
Een instelling beschikt over meerdere krachtige rekenerservers die via internet toegankelijk zijn. Elke machine die gebruik wil maken van deze rekendiensten, stuurt een gegevensbestand naar poort 756 van een van de servers. Dit bestand bevat diverse gegevens: gebruikersnaam, wachtwoord, opdrachten die het gewenste type berekening aangeven, en de gegevens waarop de berekening moet worden uitgevoerd. Als het gegevensbestand correct is, verwerkt de gekozen rekenserver het en stuurt de resultaten terug naar de client in de vorm van een tekstbestand.
Een dergelijke opzet biedt talrijke voordelen:
- elk type client (PC, Mac, Unix,...) kan deze dienst gebruiken
- de klant kan zich overal op het internet bevinden
- de rekenmiddelen zijn geoptimaliseerd: er zijn slechts enkele krachtige machines nodig. Zo kan een kleine organisatie zonder eigen rekenmiddelen gebruikmaken van deze dienst tegen een financiële bijdrage die wordt berekend op basis van de gebruikte rekentijd.
Ondanks de kracht van de machines kan een berekening soms meerdere uren duren: de server is dan niet beschikbaar voor andere klanten. Voor een klant ontstaat dan het probleem om een beschikbare reken-server te vinden. Hiervoor wordt een „beheerder van rekenbronnen“ gebruikt, hierna de GRC-server genoemd. Deze dienst draait op één enkele machine en werkt op poort 864 in TCP-modus. Een klant die toegang tot een rekenserver wenst, richt zich tot deze dienst. De server GRC, die de volledige lijst met reken servers bevat, antwoordt door de naam van een momenteel inactieve server door te geven. De client hoeft vervolgens alleen nog maar zijn gegevens naar de aangewezen server te sturen.
We zijn van plan de server GRC te schrijven.
8.5.2.2. De INTERFACE VISUELLE
De visuele interface ziet er als volgt uit:

De interface toont twee lijsten met servers:
- links de lijst met inactieve servers, die dus beschikbaar zijn voor berekeningen
- rechts de lijst met servers die bezet zijn door berekeningen van een klant.
De menustructuur is als volgt:
Hoofdmenu | Submenu | Rol |
Service | Starten | Start de TCP-service op poort 864 |
Stoppen | De service stoppen | |
Afsluiten | Sluit de applicatie af | |
Auteur | Copyrightinformatie |
De structuur van de besturingselementen op het formulier is als volgt:
Naam | Type | Rol |
listLibres | JList | Lijst met beschikbare servers |
listOccupés | JList | Lijst met bezette servers |
8.5.2.3. FONCTIONNEMENT DE APPLICATION
Bij het laden van de applicatie wordt de lijst listLibres gevuld met de lijst van namen van de rekenerservers die worden beheerd door de GRC. Deze worden gedefinieerd in een bestand ‘Servers’ dat als parameter wordt doorgegeven. Dit bestand bevat een lijst met servernamen, één per regel, en wordt dus gebruikt om de lijst listLibres te vullen. Het menu ‘Starten’ is toegestaan, het menu ‘Stoppen’ is geblokkeerd.
Deze optie
- start de luisterdienst op poort 864 van de machine
- blokkeert het menu 'Starten'
- maakt het menu ‘Stoppen’ beschikbaar
Deze optie stopt de dienst:
- de lijst met bezette servers wordt leeggemaakt
- de lijst met vrije servers wordt gevuld met de inhoud van het bestand 'Servers'
- het menu 'Starten' wordt ingeschakeld
- het menu Afsluiten wordt geblokkeerd
De toepassing wordt afgesloten.
De client/server-communicatie verloopt via de uitwisseling van tekstregels die eindigen met de reeks RCLF. De server GRC herkent twee commando’s: getserveur en finservice. Hieronder wordt de functie van deze twee commando’s toegelicht:
- 1-getserveur
De client vraagt of er een reken-server voor hem beschikbaar is.
De server GRC kiest dan de eerste server die in zijn lijst met vrije servers wordt gevonden en stuurt de naam daarvan terug naar de client in de vorm:
Bovendien voegt hij de aan de klant toegewezen server toe aan de lijst met bezette servers in de vorm:
zoals blijkt uit het volgende voorbeeld, waarin de server calcul1.istia.univ-angers.fr bezig is met het bedienen van de client met het adres IP 193.52.43.5:

Een client kan geen getserveur-commando verzenden als er al een reken-server aan hem is toegewezen. Voordat de server GRC de client antwoordt, controleert hij daarom of het adres IP van de client niet al voorkomt in de lijst met bezette servers. Als dat het geval is, antwoordt de server GRC:
Ten slotte is er het geval waarin er geen enkele rekenserver beschikbaar is: de lijst met vrije servers is leeg. In dat geval antwoordt de server GRC:
In alle gevallen verbreekt de server GRC, nadat hij de client heeft beantwoord, de verbinding met deze client om andere clients te kunnen bedienen.
- 2-finservice
De client geeft aan dat hij de reken-server die hij gebruikte niet meer nodig heeft.
De server GRC controleert eerst of de klant inderdaad een klant is die hij bediende. Hiervoor controleert hij of het adres IP van de client voorkomt in de lijst met bezette servers. Als dat niet het geval is, antwoordt de server GRC:
Als de client wordt herkend, antwoordt de server GRC:
en verplaatst de aan deze klant toegewezen rekenserver naar de lijst met vrije servers. Om het vorige voorbeeld te herhalen: als de klant het commando finservice verstuurt, wordt de weergave van de server GRC:

Nadat het antwoord is verzonden, ongeacht de inhoud ervan, verbreekt de server GRC de verbinding.
8.5.2.4. TRAVAIL A FAIRE
Schrijf de applicatie als een op zichzelf staand programma dat bijvoorbeeld getest kan worden met een telnet-client of met de generieke TCP-client uit de vorige oefening.
8.5.3. Oefening 3 - een SMTP-client
8.5.3.1. INTRODUCTION
We willen hier een client bouwen voor de dienst SMTP (SendMail Transfer Protocol) waarmee e-mail kan worden verzonden. Onder Unix of Windows is het programma telnet een client die werkt met het protocol tcp. Het kan ‘communiceren’ met elke tcp-service die tekstopdrachten accepteert die eindigen op de reeks RCLF, dat wil zeggen de codetekens ASCII 13 en 10. Hier volgt een voorbeeld van een conversatie met de dienst smtp voor het verzenden van e-mail:
// antwoord van de SMTP-server
Trying 193.52.43.2...
Connected to istia.univ-angers.fr.
Escape character is '^]'.
220-Istia.Istia.Univ-Angers.fr Sendmail 8.6.10/8.6.9 ready at Tue, 16 Jan 1996 07:53:12 +0100
220 ESMTP spoken here
// opmerkingen --------------
Het programma telnet kan elke service aanroepen via de syntaxis
telnet machine_service port_service
De communicatie tussen client en server verloopt via tekstregels die eindigen met de reeks RCLF.
De antwoorden van de dienst smtp hebben de volgende vorm:
berichtnummer of
berichtnummer
De SMTP-server kan meerdere antwoordregels verzenden. De laatste regel van het antwoord wordt aangeduid met een nummer gevolgd door een spatie, terwijl bij de voorgaande regels van het antwoord het nummer wordt gevolgd door een streepje -.
Een nummer groter dan of gelijk aan 500 duidt op een foutmelding.
// einde opmerkingen
// antwoord van de SMTP-server
214-Commands:
214- HELO EHLO MAIL RCPT DATA
214- RSET NOOP QUIT HELP VRFY
214- EXPN VERB
214-For more info use "HELP <topic>".
214-To report bugs in the implementation send email to
214- sendmail@CS.Berkeley.EDU.
214-For local information send email to Postmaster at your site.
214 End of HELP info
// opmerkingen ---------
Het mail-commando heeft de volgende syntaxis:
mail from: e-mailadres van de afzender van het bericht
// einde opmerkingen
// antwoord van de SMTP-server
// opmerkingen
De server smtp controleert de geldigheid van het adres van de afzender niet: hij neemt het adres over zoals het is opgegeven
// einde opmerkingen
// opmerkingen ---------
Het commando rcpt heeft de volgende syntaxis:
rcpt to: e-mailadres van de ontvanger van het bericht
Als het e-mailadres een adres is van de machine waarop de server smtp draait, controleert deze of het adres daadwerkelijk bestaat; anders wordt er geen controle uitgevoerd. Als er een controle heeft plaatsgevonden en er is een fout gedetecteerd, wordt dit gemeld met een nummer >= 500.
Er kunnen zoveel rcpt to-opdrachten worden gegeven als gewenst: hiermee kan een bericht naar meerdere personen worden verzonden.
// einde opmerkingen
// antwoord van de SMTP-server
// opmerkingen ---------
Het commando data heeft de volgende syntaxis:
data
regel1
regel2
...
.
Daarna volgen de tekstregels waaruit het bericht bestaat; het bericht moet eindigen met een regel die uitsluitend het teken "punt" bevat.
Het bericht wordt vervolgens verzonden naar de ontvanger die is opgegeven met het commando rcpt.
// einde commentaar
// antwoord van de SMTP-server
// tekst van het bericht, ingevoerd via het toetsenbord
subject: essai smtp
essai smtp a partir de telnet
.
// opmerkingen
In de tekstregels van het data-commando kan een 'subject:'-regel worden opgenomen om het onderwerp van de e-mail te specificeren. Deze regel moet worden gevolgd door een lege regel.
// antwoord van de SMTP-server
// opmerkingen
Het commando quit verbreekt de verbinding met de dienst smtp
// einde opmerkingen
// antwoord van de SMTP-server
8.5.3.2. De INTERFACE VISUELLE
We stellen voor om een programma te bouwen met de volgende visuele interface:

De besturingselementen hebben de volgende functie:
Nummer | Type | Functie |
1 | JTextField | Reeks e-mailadressen gescheiden door een komma |
2 | JTextField | Onderwerp van het bericht |
3 | JTextField | Reeks e-mailadressen gescheiden door een komma |
4 | JTextField | Reeks e-mailadressen gescheiden door een komma |
5 | JTextArea | Tekst van het bericht |
6 | JList | volglijst |
7 | JList | dialooglijst |
8 | JButton | De knop 'Annuleren' wordt niet weergegeven, maar verschijnt wanneer de client verbinding vraagt met de server SMTP. Hiermee kan de gebruiker dit verzoek afbreken als de server niet reageert. |
8.5.3.3. LES MENUS
De menustructuur van de applicatie is als volgt:
Hoofdmenu | Submenu | Rol |
E-mail | ||
Verzenden | Verzend het bericht van controle 5 | |
Afsluiten | De applicatie afsluiten | |
Opties | ||
Tracking verbergen | Maak bedieningselement 6 onzichtbaar | |
Tracking resetten | De volglijst leegmaken 6 | |
Dialoogvenster verbergen | Maakt de dialooglijst onzichtbaar 7 | |
Dialooglijst wissen | Dialooglijst leegmaken 7 | |
Configureren | Hiermee kan de gebruiker het volgende opgeven - het adres van de SMTP-server die door het programma wordt gebruikt - zijn e-mailadres | |
Opslaan... | Slaat de vorige configuratie op in een .ini-bestand | |
Auteur | Copyright-informatie |
8.5.3.4. FONCTIONNEMENT DE APPLICATION
Dit menu opent het volgende venster:

Beide velden moeten worden ingevuld om de knop OK te activeren. Beide gegevens moeten in globale variabelen worden opgeslagen, zodat ze beschikbaar zijn voor andere modules.
Deze optie is alleen beschikbaar als aan de volgende voorwaarden is voldaan:
- de configuratie is voltooid
- er is een bericht om te verzenden
- er is een onderwerp
- er is ten minste één ontvanger in de velden 1, 3 en 4
Als aan deze voorwaarden is voldaan, verloopt de reeks gebeurtenissen als volgt:
- het formulier wordt in een status gezet waarin alle acties die de communicatie tussen client en server kunnen verstoren, worden geblokkeerd
- er wordt verbinding gemaakt via poort 25 van de server die in de configuratie is opgegeven
- de client communiceert vervolgens met de SMTP-server volgens het hierboven beschreven protocol
- het veld „mail from:“ gebruikt het e-mailadres van de afzender dat in de configuratie is opgegeven
- het veld „rcpt to:“ wordt gebruikt voor elk van de e-mailadressen die in de velden 1, 3 en 4 zijn aangetroffen
- in de regels die na het commando data worden verzonden, staan de volgende teksten:
- een regel Subject: met de tekst van het onderwerp van controle 2
- een regel Cc:: adressen van de controle 3
- een regel Bcc: met de adressen van controle 4
- de tekst van het bericht van controle 5
- het eindpunt
Deze knop onderaan het formulier verschijnt alleen wanneer de klant verbinding maakt met de server smtp. Deze verbinding kan mislukken omdat de server smtp niet of onjuist reageert. De knop Annuler biedt de gebruiker dan de mogelijkheid om het verbindingsverzoek te onderbreken.
De lijst (6) houdt de verbinding bij. Deze geeft de belangrijkste momenten van de verbinding weer:
- het tot stand brengen ervan door de client
- de afsluiting door de server of de klant
- alle verbindingsfouten
De lijst (7) houdt de dialoog smtp bij die tussen de client en de server tot stand komt.
Deze twee lijsten zijn gekoppeld aan menuopties:
Volglijst verbergen | Maakt de volglijst 6 en de tekst erboven onzichtbaar. Als de hoogte die deze twee besturingselementen innemen H is, worden alle besturingselementen eronder met een hoogte van H omhoog geschoven en wordt de totale grootte van het formulier met H verkleind. Bovendien maakt ‘Volgnummer verbergen’ de optie RazSuivi hieronder onzichtbaar. |
Tracking wissen | Wist de volglijst 6 |
Dialoogvenster verbergen | Maakt de dialooglijst 7 onzichtbaar, evenals de tekst erboven en de menuoptie RazDialogue eronder. Net als bij 'Tracking verbergen' wordt de positie van de onderliggende bedieningselementen (bijvoorbeeld knop Annuler) opnieuw berekend en wordt de venstergrootte verkleind. |
Dialoogvenster leegmaken | Wist de dialooglijst 7 |
Dit menu opent een zogenaamd copyrightvenster:

Verbindingsfouten worden gemeld in de volglijst 6, fouten met betrekking tot de client/server-dialoog in de dialooglijst 7. Bij een fout wordt de gebruiker hiervan op de hoogte gesteld via een foutmelding en wordt de lijst met de oorzaak van de fout weergegeven, indien deze eerder verborgen was. Bovendien wordt de client/server-dialoog gesloten en wordt het formulier teruggezet naar de oorspronkelijke staat.
8.5.3.5. GESTION van UN FICHIER DE CONFIGURATION
Het is wenselijk dat de gebruiker de software niet telkens opnieuw hoeft te configureren wanneer hij deze gebruikt. Als de optie ‘Opties/Configuratie opslaan bij afsluiten’ is aangevinkt, worden bij het afsluiten van het programma de twee gegevens die via de optie Options/Configurer zijn verkregen, evenals de status van de twee volglijsten, opgeslagen in een bestand sendmail.ini dat zich in dezelfde map bevindt als het .exe-bestand van het programma. Dit bestand heeft de volgende indeling:
SmtpServer=shiva.istia.univ-angers.fr
ReplyAddress=serge.tahe@istia.univ-angers.fr
Suivi=0
Dialogue=1
De regels SmtpServer en ReplyAddress bevatten de twee gegevens die zijn opgehaald via de optie Opties/Configureren. De regels Suivi en Dialogue geven de status van de lijsten ‘Opvolging’ en ‘Dialoog’ weer: 1 (aanwezig), 0 (afwezig).
Bij het laden van het programma wordt het bestand sendmail.ini gelezen, indien het bestaat, en wordt het formulier dienovereenkomstig geconfigureerd. Als het bestand sendmail.ini niet bestaat, wordt er gehandeld alsof het wel bestaat:
Als het bestand sendmail.ini wel bestaat maar onvolledig is (er ontbreken regels), wordt de ontbrekende regel vervangen door de overeenkomstige regel hierboven. Als de regel Suivi=... dus ontbreekt, doen we alsof we Suivi=1 hebben.
Alle regels die niet overeenkomen met het sjabloon:
worden genegeerd, evenals die waarbij het trefwoord ongeldig is. Het trefwoord mag in hoofdletters of kleine letters worden geschreven: dat maakt niet uit.
In de optie Options/Configurer worden de waarden SmtpServer en ReplyAddress weergegeven die momenteel actief zijn. De gebruiker kan deze vervolgens desgewenst wijzigen.
8.5.3.6. TRAVAIL naar FAIRE
Voer de hierboven beschreven werkzaamheden uit. Het wordt aangeraden om het beheer van het configuratiebestand als laatste af te handelen.
8.5.4. Oefening 4 - client POPPASS
8.5.4.1. Introduction
We zijn van plan een client TCP te maken die kan communiceren met de server POPPASSD, die op poort 106 draait. Met deze dienst kan men zijn wachtwoord wijzigen op een machine UNIX. Het protocol voor de client/server-communicatie is als volgt:
1 - De communicatie verloopt via de uitwisseling van berichten die eindigen met de reeks RCLF
2 - De client stuurt opdrachten naar de server
- De server antwoordt met berichten die beginnen met driecijferige getallen: XXX. Als XXX=200 is, is het commando correct uitgevoerd; anders is er een fout opgetreden.
3 - De volgorde van de uitwisselingen is als volgt:
- de server antwoordt met een welkomstbericht
- de server vraagt om het wachtwoord als de aanmelding wordt geaccepteerd; anders wordt er een foutmelding gegeven
- de server vraagt om het nieuwe wachtwoord; als het wachtwoord wordt geaccepteerd, volgt er een foutmelding
- de server bevestigt dat het nieuwe wachtwoord is geaccepteerd; anders wordt er een foutmelding gegeven
- de server stuurt een afsluitbericht en verbreekt de verbinding
8.5.4.2. Het formulier van de client

De betekenis van de verschillende controles is als volgt:
nr. | naam | type | functie |
1 | txtRemoteHost | JTextField | servernaam |
2 | txtLogin | JTextField | gebruikersnaam |
3 | txtMdp | JTextField | Wachtwoord van de gebruiker |
4 | txtNewMdp | JTextField | Nieuw wachtwoord van de gebruiker |
5 | txtConfirmation | JTextField | Bevestiging van het nieuwe wachtwoord |
6 | lstSuivi | JList | Berichten over de verbindingsstatus |
7 | lstDialogue | JList | Berichten van de client/server-dialoog |
10 | cmdAnnuler | JButton | niet afgebeeld - Knop die verschijnt wanneer er verbinding met de server wordt gemaakt. Hiermee kunt u de verbinding verbreken. |
8.5.4.3. De menu's
Titel | Naam van het besturingselement | Functie |
Verbinding | mnuconnexion | |
Verbinden | mnuconnecter | start de verbinding met de server |
Afsluiten | mnuQuitter | sluit de applicatie af |
Berichten | mnuMessages | |
RazSuivi | mnuRazSuivi | wist de lijst lstSuivi |
RazDialogue | mnuRazDialogue | wist de lijst lstDialogue |
Auteur | mnuAuteur | toont het copyrightvenster |
8.5.4.4. Werking van de applicatie
Wanneer het hoofdscherm van de applicatie wordt geladen, vinden de volgende handelingen plaats:
- het scherm wordt gecentreerd op het beeldscherm
- alleen de menuopties Connexion/Quitter en Auteur zijn actief
- de knop Annuler is verborgen
- de lijsten LstSuivi en LstDialogue zijn leeg
Deze optie is alleen beschikbaar als de velden 1 tot en met 5 zijn ingevuld. Als u op deze optie klikt, gebeurt het volgende:
- er wordt een thread gestart om de verbinding met de server tot stand te brengen
- de knop Annuler verschijnt, zodat de gebruiker de lopende verbinding kan onderbreken
- alle menuopties worden uitgeschakeld, behalve Quitter & Auteur
De volgorde van de gebeurtenissen is vervolgens als volgt:
- De gebruiker heeft op de knop Annuler gedrukt: de verbindingsthread wordt gestopt en het menu wordt teruggezet naar de oorspronkelijke staat. In de log wordt aangegeven dat de verbinding door de gebruiker is verbroken.
- Het verbindingsverzoek wordt door de server geaccepteerd. Vervolgens wordt de dialoog met de server gestart om het wachtwoord te wijzigen. De uitwisselingen van deze dialoog worden vastgelegd in de lijst LstDialogue. Zodra de dialoog is beëindigd, wordt de verbinding met de server verbroken en wordt het formuliermenu teruggezet naar de oorspronkelijke toestand.
- Zolang de dialoog actief is, blijft de knop Annuleren zichtbaar, zodat de gebruiker de verbinding kan beëindigen als hij dat wenst.
- Als er tijdens de communicatie een fout optreedt, wordt de verbinding verbroken en wordt de oorzaak van de fout weergegeven in de volglijst LstSuivi.
Deze optie verbreekt een eventuele actieve verbinding met de server en sluit de toepassing af.
Wissen respectievelijk de lijsten LstSuivi en LstDialogue. Deze opties zijn uitgeschakeld wanneer de bijbehorende lijsten leeg zijn.
Deze knop onderaan het formulier verschijnt alleen wanneer de klant bezig is met inloggen of al is ingelogd op de server. De knop Annuler biedt de gebruiker de mogelijkheid om de verbinding met de server te verbreken.
De lijst LstSuivi (5) houdt de verbinding bij. Deze geeft de belangrijkste momenten van de verbinding weer:
-
het tot stand brengen ervan door de client
-
het afsluiten ervan door de server of de klant
-
alle fouten die kunnen optreden zolang de verbinding actief is
De lijst LstDialogue (6) houdt de dialoog bij die tussen de client en de server tot stand komt.
Dit menu opent een zogenaamd copyrightvenster:

Communicatiefouten worden weergegeven in de volglijst 6, fouten met betrekking tot de client/server-dialoog in de dialooglijst 7. Bij een verbindingsfout wordt de client/server-dialoog gesloten en wordt het formulier teruggezet naar de oorspronkelijke staat, klaar voor een nieuwe verbinding.
8.5.4.5. TRAVAIL A FAIRE
Voer de hierboven beschreven werkzaamheden eerst uit in de vorm van een zelfstandige toepassing en vervolgens als applet.