Een eenvoudige doch voedzame Webpagina

Inhoud

(Versie dd 27-06-04/29-06-10 , M.S.Ter Haseborg )

Aanleiding .

Wil je je eigenzinnigheid aan anderen ten toonspreiden dan moet je een Webpagina maken .
Daarvoor moet je , als anderen het ook moeten lezen , de beschikking hebben over toegang tot een server van de een of andere provider .

Je krijgt dan van je provider een directory toegewezen , iets in de trand van 129.125.22.13/export/home/usr/menno/jbnnoord (absoluut path) of www.fwn.rug.nl/menno (relative path)
Dit is je HOME_DIRECTORY .

Als je het makelijk wilt houden maak dan op je eigen machine een directory MYHOME aan.
Hieronder zet je je gegevens neer en een directory structuur voor je onderwerpen .
(Dat hoeft niet maar ik zou je het wel aanraden anders wordt het snel niet overzichtelijk )

Nu kun je op je eigen kompjuter knutselen en als het resultaat je aanstaat dan kun je alle bestanden en dirictory's copieren naar je provider . (gebruik iets als FTP daarvoor gaat sneller als via je Browser)

Maak nu eerst een startpagina (of kopier er een van iemand anders) , dat is de pagina van waaruit je je onderwerpen gaat benaderen en kort weer geeft wat een onderwerp voorsteld .
Gebruik zo min mogelijk plaatjes , dit kost veel tyd in smallband verbindingen .
Zet daartoe plaatjes in een aparte directory opdat de gebruiker weet dat het ophalen van die gegevens tyd kost .
Eerlijk gezegd zijn ook Word files vaak loei groot .
De naam van je start pagina mag je zelf weten als het maar eindigd op .htm (of .html) .
(Let op veel server zijn linux/unix server deze zijn hoofdletter/kleineletter gevoelig )
b.v www.fwn.rug.nl/menno/NOORD.HTM hiermee krijg je de startpagina te zien .

Hoe je een HTML bestand schrijft is een heel ander verhaal .
Er zijn hiertoe genoeg leuke boeken en www pagina's over .
Hier twee : HTML van Jan Kampherbeek of kijk op http://html.op-het.net .
De altyd goede manier is om te kijken hoe anderen het doen .
Als je een html pagina op je beeld hebt staan klik op je rechter muisknop en via broncode(sourcecode) kun je de text lezen . (Dit werkt in alle browser's ongeacht welk operating systeem gebruikt wordt .)
Iets zal ik er over zeggen , dit soort bestanden zijn simple text bestanden en kunnen worden aangemaakt met iets als edit.com of notepad.exe (onder Microsoft) of kate,vi,elvis(linux).

Er is een gemeen ding en wel hoe je een top-of-form krijgt op een printer .

<style> a {text-decoration:none}
        .post {font-family:Courier,monospace}
        .posting {color:blue;background:#FFFFCC}
        .pre {page-break-after:always}
</style>
Als je nu nu een stuk text taged met als class=pre dan zal op de end tag een top-form worden gegeven .
Bv.
<pre class=pre> Oh weer een pagina </pre>

Layout .

Hierover zal iedereen van menig oververschillen . Ik ben uitgegaan van de kleinste hoeveelheid operatoren . Ik heb hiertoe een pagina ingedeeld in n onderwerpen . Ieder onderwerp is een tabel . Iedere tabel heeft twee kollommen . De linker geeft aan waar de informatie te vinden/optehalen is , de rechter geeft een korte beschrijving waar over de informatie gaat .
Voorbeeld .
<table> <!-- entry table -->
 <tr><td>left side</td><td>right side</td></tr>
</table> <!-- end table -->
<br> <!-new line -->

Netwerking .

Even iets omtrend hoe het werkt , niet tedenken dat het zo is maar geeft aan hoe het ongeveer verloopt .
Jij als gebruiker (aanvrager van informatie) bent wat men noemt client , de vraag steller .
Jij vraag wordt beantwoord door diegene het antwoord naar je toe stuurt de server .
Het verkregen antwoordt wordt door jouw client omgezet in een beeld wat je op je monitor ziet .
Jij kunt dat een nieuwe vraag stellen , b.v. door een link aanteklikken , en het spelletje begint opnieuw .
(client server kunnen in een kompjuter zitten z.g.n. loopback verbinding (o.a. 127.0.0.1) )
De client server praten met elkaar d.m.v. het HTTP protocol . Dit zend bestanden van de een naar de ander .
Je client leest de file kijkt of er een programma is waarmee het voor jouw op het beeld kan zetten .
Bekende extentie zijn htm/html HTML , doc WORD , xsl,csv EXCEL , pfd ARCOBAT , txt NOTEPAD voor linux gelden andere programma's doc hangt aan ABI etc .

Netwerk communicatie bestaat uit een waslijst protocollen , een goed simple verhaal is gegeven door Atmel .
Waar het mij om gaat is hoe kan ik data van a naar b sturen .
Voor deze is een oplossing bedacht op de Buckley universiteit ergens beginjaren 1950 genaamt sockets.
Het idee erachter is , dat je een stopkontakt hebt met als je daar een stekker insteekt dus een verbinding hebt.
Dat stopkontakt maak je met socket() welke jou een stopkontakt_nummer geeft .
Je wil met iemand praten dus moet je er een steker insteken , maar die stekker moet voorzien worden van een geven met wie je wil praten(IP_adres) en waarover(port_type) .
Om de steker in het stopkontakt te steken zeg je connect(socket,met_wie_je_wil_verbinden) en als het goed gaat heb je dan verbinding met de andere kant . Je kunt nu data zenden en/of ontvangen totdat jou of de andere kant de verbinding verbreekt (close(socket) of closesocket(socket))
Als je op deze manier verbinding maak ben je wat men noemt een "client" .

Net als bij de telefoon , wil de anderekant je niet hebben dan geeft connect() een bezet(toon) melding.

Als je met iemand kontakt wilt opnemen moet die wel in staat zijn dit te beantwoorden .
De anderekant van een verbinding wordt een "server" genoemd . (vandaar de kreet client/server techniek)
Integenstelling tot diegene die verbinding wil hebben , dit niet altijd doet , moet de server altijd luister of er iemand is die met hem/haar wil praten . (er zijn technieken die server kunnen starten (awake functie))

Ook hierbij maak je een kontaktdoos (socket) met een contra stekker die je met de gegeven van jouzelf doorgeven d.w.z wie je bent en waarover gepraat kan worden . Doormiddel van bind(socket,wie_ik_ben) is de verbinding klaar voor ontvangst .

Nu maar wachten of er iemand met jouw wil praten .
Om dit te konstateren vraag je of er een client is m.b.v. client_id=accept(my_socket,wie_ben_jij) .
Dit geeft een wie_ben_jij beschrijving met onderanderen wie jij bent en over welke port ik met jij kan praten , feitelijk interesseerd je die praat port helemaal niet .

Nu kun je praten d.m.v. send(client_id,buffer,lengte_buffer) of
                luisteren m.b.v. recv(client_id,buffer,lengte_buffer)

Als je er zat van bent close(client_id) je de client_id (niet jouw socket want dan ben jij ook weg) .

Over het algemeen wordt de afspraak gehanteerd dat eerst de client zend waarop de server antwoord enz.
(Dit wordt onderanderen gebruikt bij je browser(client) verbinding met je provider(server) )

Praktijk .

Zoals meestal lijkt de theorie nog wel tebegrijpen maar het uitvoeren van die theorie is ineens een heel ander verhaal .

Het eerste feest is om tevinden of de jouw gevonden voorbeelden wel kunnen draaien op jouw pc.
Zo zijn er dus allerlei platforms met allen een iets andere interpretatie van een kommando.
In unix/linux moet je iets hebben als sys/socket.h
In ms/mingwing/cingwing/djcpp heb je iets als winsock.h
In Borland is er iets als netinet/tcp.h

Voorlopig heb ik een beetje gestoeid met winsock gebruik als vertaler mingwing/gcc .
Het eerste waar ik tegenaanloop is dat je winsock met iets onduidelijk moet initeren .
Merkwaardig genoeg met een termologie uit WSA wat een event versie van winsock is voor MSWindows .
Maar goed klad dat over en je init werkt . Een tweede verschil met unix/linux is dat veel routine namen anders zijn . close -> closesocket , ioctl -> ioctlsocket , write -> send , read -> recv .
De constanten heten meestal wel het zelfde evenals de structures , ga niet zitten bitneuken in de structures want die zijn niet voor alle platforms op de zelfde wijze gemaakt .

De transmissie wordt gezien als een data stream . (unix/linix ziet volgens mij alles als streams)
Tevens wordt er de transmissie gesproken over blocked en een nonblocked methode .
Het enige wat ik er van begrijp is dat normaal een blocked methode wordt gebruik hetgeen inhoud dat je niet moet proberen telezen als er geen data is want de lees opdracht wacht tot er ooits iets komt (busy form of waiting) Je komt daar ook niet weer uit .
Om blocking tevoorkomen kun je opvragen hoeveel gegevens er nog in je verbinding aanwezig zijn (ioctlsocket()) als dit nul is moet je geen ontvangstopdracht (recv) geven .
Je moet wel een wait() functie maken want tussen twee pakketten kan er een zekere tijd staan .
Dit is wat genoemd wordt een time_out periode .

Hoe een teverzenden bericht weet dat het beeindigd is , weet ik niet .
Er is een methode , verzend data en beeindig dit met een close() (zoals FTP dit doet in het data channel) .
Je kunt dit detecteren met select(socket,read,write,unbound,time_out) .
Het maakt gebruik van een structure genaam FD , volstrekt onbegrijkbelijk . Het is een serie macro's .
Je zet een groep voor read_check , write_check en eventueel bound_check en een timeout .
(Overigens kan ik de timout binnen mingwing niet aan de loop krijgen )
Er is een wat vreemde uitleg over wat bedoeld is met deze check's .
write_check zegt dat de connect bestaand en beschreven kan worden .
read_check zegt dat de connect niet bestaat of gelezen kan worden .
Wat ik gedaan heb is een select() met read en write check , deze antwoord altijd .
Als write_check false is is de verbinding weg , een read_check en write_check true betekend dat je kunt lezen zonder een blocking te krijgen . Hoogstens krijg je een EOF melding als de verbinding is gesloten .
Als er dus een write_check true en een read_check false is kom je tijdens lezen in een timeout cycles .
Ik lost dit op door een _sleep(100) (sampling 0.1 sec) van de channel (zie netlib.c).

Een close() geeft op recv() altijd als resultaat/aantal een 0 .
De andere methode is een protocol afspraak , bv beeindig ieder transport met een crlf en de laatste regel met crlf .
(Dit doet HTTP , ergens staat dat het protocol blok wordt beeindigd met een lege regel . Is er nog text verstuurd dan staat het aantal character in CONTENT-LENGTH)
(In DOS is een cr altyd een cr/lf , in UNIX het is het lf. zie dos2unix.)

De andere bedoeling van select() is een polling systeem te maken zonder gebruik te maken van fork().

Ook hoe nonblocking en wat ms assynchroon noemt , werkt c.q. doet is mij tot opheden niet duidelijk .

Een andere kreet is een keep_alive functie , ik heb het vermoeden dat dit iets met proxi servers temaken heeft .
Zodat meerdere files verstuurt kunnen worden met het zelfde socket .
Normaal na close() wordt de socket terug gegeven aan het systeem en wordt er een nieuwe gemaakt bij de eerst volgende connect() .

Toepassingen betreffende socket lees dan eens ,
www.andrew.cmu.edu/~kevinm/sockets.html
www.lowtek.com/sockets/select.html
AVR460 : Embedded Web Server (zie www.atmel.com)

Toepassingen hebben soms een vast port nummer b.v. HTML is port 80 .
Lees maar eens www.pronix.de/c/standard_c/c-programmierung_27.shtml (CGI)
Leuk en korrect is HTML & INTERNET door Jan Kampherbeek .
De rest van de definities kun je lezen op www.w3.org , meestal werkt het niet op jouw Internet_Browser (Netscape,Explorer,Konquikador).
(Een nieuwe methode is XML waarin men informatie(text) scheid van de opmaak.
De opmaak wordt gedaan door style_sheets (CSS/XSL) , de kranten doen dit al jaren!)

POP3(eMail) is port 110 , FTP is port 21 , SMTP is port 25 .
Lees maar eens www.vbip/winsock/socket-send/socket-send-02.asp

HTTP protocol .

Dit bestaat uit vraag (client) antwoord (server) .
GET bla bla HTTP/1.1---------->(request)
HOST : 129.125.22.13   
USER-AGENT :   
CONTENT-TYPE : text/plain   
CONNECTION : keep-alive   
'lege regel'   
(answer)<---------HTTP/1.1 200 OK status 200 is oke
    CONTENT-TYPE : text/html
    CONTENT-LENGTH : 200
    CONTENT-CACHE : no-cache
    CONNECTION : close
    'lege regel'
    data/text/image file
    'server -> close client socket'

Formeel bestaat het transport uit 5 lagen .
request/status ; general_header ; req/response_header ; entity_header ; entity_body .

Het meeste wat overgestuurd is weinig interresant . Maar de volgende zijn nuttig te weten .
De content-cache : no-cache geeft aan dat de browser de gegevens niet mag cachen en/of vergelijken met de cache .
Dit is belangrijk als je het protocol gebruikt als een interactive user interface .
De content-length is belangrijk als je gegevens verstuurt met POST .
(Deze gegevens stuur ik meestal naar POST.CMD file dan kan ik ze later rustig mishandelen )
De content-type is lastig deze wordt deels gegeven door form's enctype= , default is dit meestal www-form-urlencoded maar wordt door de verschillende browser anders behandeld .
Als jij gegevens verstuurt kun je de content-type een waarde van het file type geven .
Platte text is text/plain , html opmaak is text/html , (gif)plaatjes is image/gif .

Een voorbeeld : (zonder alle bla bla die nodig is) UPLOAD (van jouw naar de server)

Je hebt een server nodig die zelf opdrachten kan deligeren (bv. CGI) of je knutseld zelf een in elkaar .
Je start je browser een zoekt kontakt met je server meestal http://127.0.0.1:5000/upload . (loop-back)
Je programma UPLOAD stuurt nu een form terug waarin naar de file gevraagd wordt welke je wilt versturen .

<form enctype=multipart/form-data boundary=mylife action=upload+r method=post>
<input type=file name=bestand>
Als je verzend (submit) geeft stuurt je browser de vraag upload+r plus de file over naar de server .
Je server eet de file op en stopt dat in post.cmd en start het programma upload r .
Je programma UPLOAD met parameter r stuurt terug de vraag hoe moet de file gaan heten .
<form action=upload+n method=get>
<input type=text name=" " size=60>
Als je verzend(submit) geeft stuurt je browser de vraag upload+n?+=filenaam naar de server .
Je programma UPLOAD start met parameter's n filenaam . Op grond van de parameter n wordt de file POST.CMD gekopierd naar filenaam en stuurt als antwoord dat het programma zijn werk heeft gedaan (of iets van dien) .

Server Scripting .

Het voorbeeld hierboven gegeven is eigenlijk een form van Server Scripting .
Het eerste wat de Server krijgt is het request in de form van GET /file.ext?=overige+onzin .
Nu hang het van de Server af of deze een programma heeft wat geassocieerd is met file.ext . Ofwel kan de server het gegeven file.ext zien als een uittevoeren programma . Meestal wordt dit gedaan opgrond van de ext welke bepaald waarmee de file.ext wordt uitgevoerd . Om de overige gebruiker's niet teblokkeren wordt er een parrallel taak gestart (fork()) waarin de gewenste opdracht wordt uitgevoerd .
Bijvoorbeeld GET /j11?=kollum04 hetgeen onstaat als je vraagt : http://myserver/j11 kollum04 zo op je server het programma j11 starten met als parameter kollum04 .

Jammer de meeste servers(c.q. providers) staan niet toe dat je willekeurige programma's (executeble's) mag starten . Meestal mag je een beperkt aantal Scriptors (interpreters) gebruiken . Wat het verschil is tussen een Script en een Interpreter is mij zolangzaam aan niet meer duidelijk . Vroeger was een scriptor iets waarmee je een batchjob schreef , de huidige scriptor's zijn komplete programma talen zoals Basic .

Enkele bekende script's zijn PHP , Python , Perl , Rubby , Euphoria . Je zou er zelfs QBasic voor kunnen gebruiken . Als je er maar voor zorgt dat de resultaten welke je terug stuurt naar de gebruiker in HTML formaat staan . (We zijn er nog steeds vanuit gegaan dat we een HTTP protocol gebruiken.) Wat inhoud dat je in de tegebruiken scriptor er voor moet zorgen dat je de html_header's en footer's maakt . Dit integenstelling tot de familie PHP's waar het programma wat je wil uitvoeren zich in de HTML beschrijving bevind.

voorbeeld
file :prog.phpfile :prog.ex
 <html><body>  -- begin Euphoria program
 <?php /* enter PHP */  puts(1,"<html><body>")
 echo "Het welbekende voorbeeld : Hello World";  puts(1,"Het welbekende voorbeeld : Hello World")
 ?> <!-- leave PHP -->  puts(1,"</body></html>")
 </body></html>  -- end Euphoria program

(er wel van uitgaand dat je server leest via stdin en schrijft op stdout .)

Al deze script talen beschikken over alle moderne controle structuren , als mede allerlei dynamische data structuren . De meesten bevatten een heleboel wrapper naar allerlei systeem routines zoals eMail , DataBase's .

Het zal duidelijk zijn , hoop ik , dat de meeste servers (c.q. provider) niet zitten te springen op deze mannier van scripting . Daar het een groot beslag kan gaan leggen op de systemresourcing . Dit in tegenstelling tot het gebruikt van java_applet die n.l op jouw machine (client) worden uitgevoerd . (Een voorbeeld van scripting over hoe je inschrijving van deelnemers kunt bewerken .)

Keep-Alive

In het HTTP 1.0 protocol wordt na iedere transmissie de client socket gesloten . Dit betekend dat voor iedere aanvraag door de client een nieuwe socket wordt geopend . Bij veel aanvragen ( door plaatjes e.d. ) kan dit betekennen dat je geen socket's meer kunt verkrijgen . Iedere gesloten socket kan n.l. niet onmiddelijk weer worden gebruikt , dit krijgt een WAIT-TIME mee van ongeveer 3 min . Je schijnt door bij de socket initiatie de parameter 'reuse' mee te geven dit naar 0 te reduceren (alleen is het mij nog niet gelukt) .
Om nu de aanvragen van socket's te beperken is in het HTTP 1.1 de opdracht Connection: Keep-Alive opgenomen . Dit betekend dat de volgende aanvragen steeds gebruik maken van de zelfde socket . Het betekend dat je de client socket niet moet worden gesloten zoals in het bovenstaande voorbeeld gebeurd . Ook moet je geen accept opdracht uitvoeren omdat de socket nog openstaat . De client heeft een max tyd voor hoelang hij de socket openhoud (meestal 300 sec) . Dit omtevoorkomen dat de socket open blijft staan terwijl er geen aktie meer worden gevraagd . Echter sluit de client de socket dan krijgt de server de melding 'socket-close' waarop deze de socket moet sluiten .
(Overigens blijken veel browser's , als deze geen connection: closed als antwoord terug krijgen , de overige aanvragen zoals b.v. css en frame's en plaatjes over meerdere verbindingen te verdelen . Firefox ken meer dan 15 verbindingen en IE iets van 2 . Dit betekend dat je server wel meerdere verbindingen tegelijk moet kunnen behandelen . Je kunt dit omzijlen door de max-connections van de browser op 1 te zetten .)
Opgrond waarvan weet nu de client wanneer de transmissie van de server klaar is ? Hiertoe dient de parameter CONTENT-LENGTH worden opgegeven . Deze bepaald de aantal overtesturen characters .
Wat nu als dit bij het begin van het antwoord nog niet bekent is ( dynamische website's ) . Zet dan het transmissie protocol in Transfer-Encoding: chunked .

Chunked Encoding

Om de lengte , welke van tevoren niet bekend is , toch tekunnen bepalen in de client wordt de data in de header verpakt .
voorbeeld header
HTTP/1.1 200 OK
CONTENT_TYPE: text/html
CONNECTION: Keep-Alive
TRANSFER-ENCODING: chunked
lege regel
lengte(volgende regel) in HexDecimal
byte stream
lengte(volgende regel) in HexDecimal
byte stream
lengte(volgende regel) in HexDecimal
byte stream
0 (afsluiting data)
lege regel
lege regel (afsluiting protocol)
Tussen afsluiting data en afsluiting protocol mogen nog andere protocol opdrachten staan .

ETag hergebruik informatie

Het niet altijd nodig om de informatie voor je website opnieuw te laden . Denk maar eens aan alle mooie plaatjes die je stuurd of aan de ingewikkelde CCS bestanden die je mogelijk gebruikt .
Met behulp van de opdracht ETag: "iets unieks" kun je berijken dat bij een volgende aanvraag voor dezelfde informatie je browser iets mee stuurd als If-None-Match: "iets unieks" . Dat betekend als "iets unieks" hetzelfde zijn de browser de gevraagde informatie noch heeft en je deze niet weer behoefd over te sturen . Dan stuur je als antwoord no-modify :
voorbeeld header
HTTP/1.1 304 OK
CONNECTION: Keep-Alive
lege regel (afsluiting protocol)
De code 304 geeft aan de browser de informatie dat hij de aanwezige informatie opnieuw mag gebruiken . Dit "iets unieks" kun je maken door b.v. "bestandsnaam-datum-tijd" als Etag gegeven tegebruiken . Quotes (") zijn verplicht .

Fork's en Thread's

Als jij als server wil functioneren betekent het dat je een binnenkommende vraag gaat beantwoorden .
Wat gebeurt er nu met de tweede aanvrager als je nog bezig bent de eerste te beantwoorden ?
De tweede aanvrager krijgt dan als antwoord helemaal niets totdat de eerste vraag is beantwoord of een time_out is berijkt . Waarom je de melding krijgt 'pagina niet berijkbaar' .
Daar het antwoord geven meestal veel IO tyd kost zou je in tussen tyd wel de tweede aanvrager kunnen gaan beantwoorden .
Dit wordt berijkt door iedere aanvrager in een 'eigen programma' tezetten welke 'paralel' worden verwerkt .
Nou ja 'paralel' alleen als je beschikt over meerdere processoren . Meestal heb je er maar 1 (duur genoeg) . In geval van 1 processor wordt de tyd verdeeld over alle 'paralel' programma's zodat iedereen een beetje van de soep krijgt .
De manier om een programma 'paralel' temaken heet forking . Oftewel je maakt een kindje van het ouder programma . Dit kindje erft alles van zijn ouders en behoudt dit ook als zijn ouder naar de ewige jachtvelden is gebracht (gestoped is) .
Dit is het grootste verschil tussen thread's en fork's n.l. je kunt de ouder in een thread niet stoppen terwijl de kinderen blijven spelen . Het andere verschil is dat de (systeem)overhead van fork's groter is dan die van de thread's .

Niet alle systemen kennen de opties m.b.t. een fork , je kunt dit omzijlen door de aanvragen te multiplexen .(zie http://www.lowtek.com/sockets/select.html)