Võrgurakenduste protokollid 1

Allikas: Lambda

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.

Kindlasti tutvu:

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.

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 minu teada kohustuslik, aga seda võiks kasutada ja panna sinna nimeks sinu enda proge nimi. Tähtsust sellel väärtusel 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:

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.

Viimase 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.

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:

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:

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:

Häid linke:

http2.0

Taustaks:

HTTP 2.0 ehk kõvasti täiendusi, mh täiustatud pipelining:

https://hpbn.co/http2

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! "


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:

JSON-RPC: http://json-rpc.org/wiki/specification