Võrgurakenduste protokollid 1
Sisukord
Alguses on TCP/IP ja socketid
- IP on interneti baasprotokoll: saadab lihtsalt pakette edasi.
- TCP on IP peale ehitatud kontrolli/optimeerimisprotokoll, mis simuleerib pidevat mõlemipoolset sidet.
- UDP on IP peale ehitatud ilma kontrollita lightweight protokoll, mis kontrolle ei tee, aga võimaldab näiteks pordi numbrit saata.
Arvuti opsüsteemis on IP/TCP/UDP sisseehitatud. Nende kasutamine käib nn socketite teegi kaudu.
Mis on socket? Ta on analoogiline failideskriptorile, mille kaudu faile lugeda ja kirjutada saab, aga võrgu jaoks. Beej's guide ütleb "You make a call to the socket() system routine. It returns the socket descriptor, and you communicate through it using the specialized send() and recv() (man send, man recv) socket calls."
Kindlasti tutvu:
- IP/TCP/UDP loengumaterjal: mis värk on.
- Vaata seda põhjalikku socketite kasutamise tutorialit pythonis!
Viimases on kaks head algusnäidet, lisasin esimesse igavese tsükli.
TCP server, mis kuulab ühendusevõtmisi, loeb saadetud sõnumi ja saadab sellesama sõnumi tagasi, töötab nii Linuxil kui Windowsil:
#!/usr/bin/env python3 import socket HOST = '127.0.0.1' # Standard loopback interface address (localhost) PORT = 65432 # Port to listen on (non-privileged ports are > 1023) while True: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((HOST, PORT)) s.listen() conn, addr = s.accept() with conn: print('Connected by', addr) while True: data = conn.recv(1024) if not data: break conn.sendall(data)
Tcp klient, mis saadab sõnumi, loeb vastuse ja trükib selle välja:
#!/usr/bin/env python3 import socket HOST = '127.0.0.1' # The server's hostname or IP address PORT = 65432 # The port used by the server with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((HOST, PORT)) s.sendall(b'Hello, world') data = s.recv(1024) print('Received', repr(data))
Eelmise tutorialiga sarnane mitmeosaline tutorial on ka hea. Iga osa lõpus on kollane link järgmisele.
Eriti targaks saab, kui loed Beej põhjalikku socketi-tutorialit C-s. See võtab rohkem aega.
Kuidas monitoorida oma arvutis võrguliiklust? Universaalne hea tööriist on wireshark. Rohkem low-level ja Linuxi suunas on tcpdump. Oma masinas toimuvat liiklust saad vaadata ntx sudo tcpdump -i lo port 8080 -v: see näide vaatab porti 8080 ja lokaalset liiklust.
Http
The web protocol: how to send web data over the TCP connection. Read
Small example (from the intro above):
Request:
GET /path/file.html HTTP/1.0 From: someuser@jmarshall.com User-Agent: HTTPTool/1.0 [blank line here]
The server should respond with something like the following, sent back through the same socket:
HTTP/1.0 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/html Content-Length: 1354 <html> <body> <h1>Happy New Millennium!</h1> (more file contents) . . . </body> </html>
Http lisadetaile
Toore TCP suhtluse järgi näeb http välja selliselt.
Päring serverile
GET failipath HTTP/1.1 Host: masinkuhupäringjõuab lisainfo1:lisainfovaartus1 lisainfo2:lisainfovaartus2 suvaline tekst
kus
- failipath oleks näiteks /cgi-bin/prog?c=150&d=aaeeff jne
- GET asemel võib kasutada POST
- HTTP/1.1 asemel võib kasutada HTTP/1.0
- lisainfo:l... headeriridu ei ole üldjuhul vaja saata, vahel on neist kasu
Iga rea lõpus alguses kuni sisuni peab olema carriage return ja line feed paar, enne sisu peab olema tühi rida. GET päringu puhul sisu ei anta.
- HTTP/1.1 puhul on Host: väli kohustuslik, kuid sinna võib panna mida iganes
- HTTP/1.0 puhul ei ole Host: kohustuslik.
User-Agent: ei ole kohustuslik, aga seda võiks kasutada ja panna sinna nimeks sinu enda proge nimi. Tähtsust sellel väärtusel enamasti ei ole.
Terviknäide OK päringust:
GET /cgi-bin/prog?c=150&d=aaeeff HTTP/1.1 Host: dijkstra.cs.ttu.ee User-Agent: minuproge
ja terviknäide veel lihtsamast HTTP/1.0 päringust:
GET /cgi-bin/progr?c=150&d=aaeeff HTTP/1.0 User-Agent: minuproge
Vastus serverilt:
HTTP/1.1 200 OK lisainfo:lisainfovaartus lisainfo:vaartus Content-Length: 123 Content-Type: text/plain vastus tekst
Content-Length ei ole päris kohustuslik, kuid on mõistlik. Seda võib kasutada ntx selleks, et allokeerida piisavalt ruumi data jaoks.
Kui teha serverirakendus mõne olemasoleva http serveri külge, siis reeglina see serverirakendus ise annab välja headeri alguse read alates
HTTP/1.1 200 OK
ja sinna peaks rakendus lisama
Content-type: text/plain
ja võib ka (aga ei pea)
Content-Length: sisupikkus
ja seejärel trükkida tühi rida ja seejärel trükkida juba saadetav sisu.
Konkreetne vastusenäide serverilt:
HTTP/1.1 200 OK Date: Sun, 13 Nov 2011 16:53:12 GMT Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.18 with Suhosin-Patch Keep-Alive: timeout=15, max=97 Connection: Keep-Alive Content-Length: 169 Content-Type: text/plain sisu.....
Kus siis Content-Length: reani trükkis apache server ja alates Content-Length: 169 trükkis minu rakendus ning meid huvitav sisu algab peale esimest tühja rida.
Vaata ka:
Veel http lugemist
HTTP kohta olulist:
HTTP2 on põnev uuem versioon HTTP-st. Tasub sirvida:
- Google selgitused, miks ja kuidas on http2 disainitud
- Digitaloceani tutorial: http2 vs http1
- Google soovitused veebioptimeeringuteks HTTP2 jaoks
HTTP3 on värskelt kasutusele võtmise alguses uusim HTTP variant, mis **kasutab UDP-d**, mitte TCPd!
Kuidas teha pythonis http serverit
Kaks põhivarianti:
- Ehitad ise socketite peale päiste lisamisega ühenduse.
- Kasutad pythonisse sisse-ehitatud http teeke.
- Spetsiaalsed http- ja networkinduse lisandusteegid/frameworkid, milles võib olla sisemine C komponent ja pyhtoni pealisehitus. Olulisemad on flask ja twisted.
Pythoni oma teegi kasutamise primitiivnäide on käsurealt käivitatav (python 3 variant)
python -m http.server 8080
mis serveerib faile samas kataloogis etteantud pordil. Enda jaoks muutmiseks tuleb hakata muidugi progema.
Sama mehaanikaga, aga progetav ja kasulik näide: Pythoni http serveriteegi kasutamine
Ise socketite peale serveri ehitamise juures on abiks see pisut vigane näide. Parandasin viimast veidi, siin parandatud joncardasise server: saad otse käima lasta ja töötab python kolmega.
https
Ehk kuidas käib krüpteeritud http:
Selle juures oleks hea aru saada public key cryptography põhimõtetest.
Same origin policy
Brauserid vaikimisi ei luba javascriptil võtta ajaxiga ühendust teistesse serveritesse, kui see, kust põhileht tuli. Võrdluseks: pildid, css ja js failid samsa võivad tulla teistest serveritest.
Erinevad lahendused:
- jsonp (json Padded) wikis ja w3schools-s
- CORS mehhanism: wikipedias ja pikemalt, vt sealt preflight teemat!
- Websocketid järgnevas.
websockets ja taustaks push/longpoll
See on javascripti/brauserisse sissehitatud teegindus koos vastava protokolliga, et javascriptist teha otse socketi-tüüpi ühendusi. Selle saab teha näiteks püsiva TCP kanali, mitte lihtsalt ühekordseid päringuid, nagu xmlhttprequesti ehk ajaxiga.
Taustaks:
- push ülevaade wikipedias vt eriti long poll peatükk
- Tornado server pythonis, ok longpoll mehhanism
Häid linke:
- wiki
- http://blog.teamtreehouse.com/an-introduction-to-websockets
- https://www.html5rocks.com/en/tutorials/websockets/basics/
http2.0 ja 3.0
Taustaks:
- HTTP 1.1 keepalive (töötab ok)
- HTTP 1.1 pipelining ja selle probleemid (ei tööta eriti ok)
HTTP 2.0 ehk kõvasti täiendusi, mh täiustatud pipelining:
Olulist eelmiselt lingilt:
- "HTTP/2 no longer needs multiple TCP connections to multiplex streams in parallel; each stream is split into many frames, which can be interleaved and prioritized. As a result, all HTTP/2 connections are persistent, and only one connection per origin is required, which offers numerous performance benefits."
- "Unless you are implementing a web server (or a custom client) by working with raw TCP sockets, then you won’t see any difference: all the new, low-level framing is performed by the client and server on your behalf. The only observable differences will be improved performance and availability of new capabilities like request prioritization, flow control, and server push! "
Kopeeritud linke ülevaltpoolt:
HTTP2: tasub sirvida:
- Google selgitused, miks ja kuidas on http2 disainitud
- Digitaloceani tutorial: http2 vs http1
- Google soovitused veebioptimeeringuteks HTTP2 jaoks
HTTP3 on värskelt kasutusele võtmise alguses uusim HTTP variant, mis **kasutab UDP-d**, mitte TCPd!
Server push HTTP2 jaoks oli ja läks minema:
Statistikat:
Curl näited
Curl on väga kasulik käsurea-tööriist pluss C teek erinevate http päringute tegemiseks. Tal on väga palju võimalusi eri parameetreid sättida jne.
Curl normaalpäring
curl 'http://81.20.146.85/cgi-bin/prog?c=150&d=aaeeff'
Curl normaalpäring, kus vastuse headerid kirjutatakse faili headers.txt
curl 'http://81.20.146.85/cgi-bin/prog?c=150&d=aaeeff' -D headers.txt
Curl päring HTTP/1.0-ga curl 'http://81.20.146.85/cgi-bin/prog?c=150&d=aaeeff' --http1.0
Curl päring, kus annad suvalise Host: headeri
curl 'http://81.20.146.85/cgi-bin/prog?c=150&d=45' -D suva.txt -H "Host: aaa"
Ja sama HTTP/1.0ga
curl 'http://81.20.146.85/cgi-bin/prog?c=150&d=45' -D suva.txt - -H "Host: aaa" -http1.0
Get and post
- GET: all data sent is in the headers & URL. If your form has METHOD="GET" in its FORM tag, your server program will receive the encoded form input in the environment variable QUERY_STRING.
- POST: when sending, after headers comes text. If your form has METHOD="POST" in its FORM tag, your server program will receive the encoded form input on stdin. The server will NOT send you an EOF on the end of the data, instead you should use the environment variable CONTENT_LENGTH to determine how much data you should read from stdin.
Post answer reading example in C:
if (getenv("CONTENT_LENGTH") == NULL || sscanf(getenv("CONTENT_LENGTH"), "%d", &cl) != 1) { data_error(NULL,"bad http format or missing content length received"); return 1; } // Create space for content: querybuf = malloc(cl + 1); if (querybuf == NULL) { data_error(NULL,"cannot malloc for http request: bad content length?"); return 1; } // Read content: c='a'; for(i=0; i<cl && c!=EOF; i++) { c=fgetc(stdin); querybuf[i]=c; } querybuf[i]=0;
Json
JSON details and official spec: