Tutorials

Client - Server interactie

Een simpele tutorial over het maken van een Client en een Server en daartussen interactie te maken.

Pagina 1

Inleiding

Clients en Servers, iedere webprogrammeur weet wat het zijn. Om het
toch nog even kort te zeggen: een Client is een lokale machine die
interactie pleegt met de Server waar de applicatie die de Client
vraagt draait. In onze HTTP wereld is de applicatie een HTTP-Server en
de lokale software een browser.
Maar je hoeft niet persé een webbrowser te gebruiken als je met HTTP
werkt, er zijn bijvoorbeeld proxy mogelijkheden in HTTP, natuurlijk
ook HTTP proxy, maar ook via de CONNECT methode kun je een
"doorlopend" gesprek voeren met een Server over een HTTP-Server, zo
kun je FTPof IRC tunnelen over HTTP en heb je dus een FTP, IRC of wat
dan ook proxy.
Voor deze tutorial blijven we echter wel bij de bekendere HTTP
methode's GET, maar voornamelijk, POST. POST is de methode om grotere
hoeveelheden data te versturen, die vooral bekend is van de
webformulieren. Maar via POST hoef je niet de bekende urlencoded data
(bekend van $_POST) of multi-part data (bekend van $_FILES) te
versturen. Je kan ook dingen als XML in de body sectie van je request
stoppen, dan kom je bij XML-RPC. XML-RPC is de manier om vanuit een
speciale blogging tool naar je weblog te posten.
En daar komt deze tutorial dan. Hoe maak je een PHP script voor op je
Server dat als een soort Server fungeert en hoe maak je een daarbij
behorende client. In deze tutorial toon ik het basis idee van de
interactie, dus ik ga me niet heel erg bezighouden met het formaat van
de data. Als je echter voor iets gaat dat voor productie is is het aan
te raden je data formaat goed te kiezen, XML is vaak een goede keuze
voor het formaat van je request.
Pagina 2

HTTP requests en responses

HTTP werkt met requests en responses, dat komt er simpelweg op neer
dat de Client in z'n request een aanvraag doet en de Server hier naar
kijkt en reageert in een response.
Vergelijk het met normaal communiceren, stel, jij bent de Client en je
vriend is de Server. Nu wil jij weten hoe laat het is, dus dat vraag
je:
Jij (Client): Hoe laat is het?
Vriend (Server): 19:17
Nu gaan we dit overbrengen naar een HTTP voorbeeld. We zeggen dat het
Server script draait als "http://www.example.org/example.php";. De
Client noemt zichzelf in dit voorbeeld "LegolasWeb TimeRequestDemo
1.0".
Request (Client):

POST /example.php HTTP/1.0
Host: www.example.org
User-Agent: LegolasWeb TimeRequestDemo 1.0
Content-Length: 5
Connection: close

time?

Response (Server):

HTTP/1.0 200 OK
Server: Apache/1.3.33 (Win32) PHP/4.4.3-dev
Content-Length: 5
Connection: close

19:24

We weten nu dus wat we moeten sturen en ook wat we zo ongeveer kunnen
verwachten. Tijd om dit eens in code om te gaan zetten.
Pagina 3

Server

Het body gedeelte van een request kun je in PHP vrij simpel uitlezen.
Het enige wat je hoeft te doen is php://input uitlezen via een fopen
wrapper. De meest simpele manier is:

<?php

$data = file_get_contents('php://input');

?>

In ons voorbeeldje hoeven we vervolgens alleen te kijken of er om de
tijd wordt gevraagd en anders kunnen we een foutmelding terug sturen.
Het totale script wordt dus zoiets:

<?php

header('Content-Type: text/plain');

$data = file_get_contents('php://input');

if ($data == 'time?') {
       echo date('H:s');
}
else {
       echo 'Unknown';
}

?>

Die Content-Type header heb ik hier enkel aan toegevoegd omdat dat wel
zo net is, zonder werkt het in princiepe ook, maar dan zegt de HTTP
Server er wel bij dat het HTML is bij een standaard configuratie.
Als ik deze pagina nu het request stuur dat we al hadden opgestelt
krijg ik als response:

HTTP/1.1 200 OK
Date: Sun, 21 May 2006 17:38:54 GMT
Server: Apache/1.3.33 (Win32) PHP/4.4.3-dev
X-Powered-By: PHP/4.4.3-dev
Connection: close
Content-Type: text/plain

19:54

Oftewel, het werkt.
Pagina 4

Client

Het is het makkelijkste voor mij om het client voorbeeld gewoon in PHP
te geven. In praktijk zal dit echter weinig zo voorkomen dat je er ook
PHP voor gebruikt. Om die reden ga ik nu eens aan de slag in C++.
C++ heeft ook een goede ondersteuning voor sockets, al is die wel net
een stapje krachtiger dan die van PHP. Als je echter ervaring hebt met
PHP sockets zal C++ je ook wel lukken. Let wel op dat ik het hier over
sockets heb en niet over fsockets!
Aangezien ik hier achter een Windows kast zit is de code daarvoor
gemaakt, converteren naar Linux is echter niet zo'n heel groot
probleem. Denk er wel aan dat je je Client linkt met het winsock2
library!
Tijd voor wat code, met natuurlijk het nodige commentaar.

// Includes
#include <cstdlib>
#include <iostream>

// Sockets
#include <sys/types.h>
#include <winsock2.h>

// Defines
#define VERSION "1.0"

// STD namespace
using namespace std;

// Korte afsluit procedure
int quitMain()
{
       system("pause");
       return EXIT_SUCCESS;
}

// Hoofd functie
int main(int argc, char *argv[])
{
       // Start winsock (2.2 indien beschikbaar, anders de hoogst mogelijke versie)
       WORD wVersionRequested;
       WSADATA wsaData;
       wVersionRequested = MAKEWORD(2, 2);
       int err = WSAStartup(wVersionRequested, &wsaData);
       if (err != 0) {
               cout << "Fatal error: Couldn't startup WinSock" << endl;
               return quitMain();
       }
       // Dit winsock stuk hoeft niet onder Linux

       // Toon een versie regel en vraag informatie over de Server
       string server;
       string port;
       string path;
       cout << "LegolasWeb TimeRequestDemo " << VERSION << endl;
       cout << endl;
       cout << "Server: ";
       cin >> server;
       cout << "Port: ";
       cin >> port;
       cout << "Path: ";
       cin >> path;

       // Maak een socket
       int sock = socket(PF_INET, SOCK_STREAM, 0);
       if (sock == -1)
       {
               cout << "Fatal error: Couldn't create socket" << endl;
               return quitMain();
       }

       // Maak verbinding met de Server (converteert eerst het Server adress
naar een IP-adress)
       struct sockaddr_in dest_addr;
       dest_addr.sin_family = AF_INET;
       dest_addr.sin_port = htons(atoi(port.c_str()));
       hostent* h = gethostbyname(server.c_str());
       dest_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)h->h_addr));
       memset(&(dest_addr.sin_zero), '\0', 8);
       int cerr = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct
sockaddr));
       if (cerr != 0)
       {
               cout << "Fatal error: Couldn't connect" << endl;
               return quitMain();
       }

       // Vertel eens wat
       cout << "Connected" << endl;
       cout << endl;
       cout << "Requesting \"time?\"" << endl;

       // Bouw een request
       string request;
       string ver = VERSION;
       request += "POST " + path + " HTTP/1.0\r\n";
       request += "Host: " + server + "\r\n";
       request += "User-Agent: LegolasWeb TimeRequestDemo " + ver + "\r\n";
       request += "Content-Length: 5\r\n";
       request += "Connection: close\r\n";
       request += "\r\n";
       request += "time?\r\n";

       // Verstuur het request en set nog eens een enter op het scherm
       send(sock, request.c_str(), strlen(request.c_str()), 0);
       cout << endl;

       // Haal de response op
       string out;
       char output[1025];
       while (recv(sock, output, 1024, 0) != 0)
       {
               out += output;
               sprintf(output, "");
       }

       // Isoleer het body gedeelte en toon het
       size_t location = out.find("\r\n\r\n") + 4;
       cout << out.substr(location, 5) << endl;
       cout << endl;

       // Sluit af
       return quitMain();
}

Om het dan toch compleet te maken voor de PHP'ers ook een klein PHP voorbeeldje.

<?php

// Nodige informatie
define('SERVER', 'www.example.org');
define('PORT', 80);
define('PATH', '/example.php');

// Versie
define('VERSION', '1.0');

// End of line
define('_EOL', "\r\n");

// Maak een socket
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
if (!$sock) {
       echo 'Fatal error: Couldn\'t create socket';
       exit;
}

// Maak verbinding
if (!socket_connect($sock, SERVER, PORT)) {
       echo 'Fatal error: Couldn\'t connect';
       exit;
}

// Maak het request
$request = null;
$request .= 'POST ' . PATH . ' HTTP/1.0' . _EOL;
$request .= 'Host: ' . SERVER . _EOL;
$request .= 'User-Agent: LegolasWeb TimeRequestDemo ' . VERSION . _EOL;
$request .= 'Content-Length: 5' . _EOL;
$request .= 'Connection: close' . _EOL;
$request .= _EOL;
$request .= 'time?' . _EOL;

// En verstuur het
if (!socket_send($sock, $request, strlen($request), 0)) {
       echo 'Fatal error: Couldn\'t send request';
       exit;
}

// Haal de response op
$output = null;
$buffer = null;
while (socket_recv($sock, $buffer, 1024, 0) != 0) {
       $output .= $buffer;
}

// Isoleer het body gedeelte en toon het
$output = explode(_EOL . _EOL, $output);
$output = substr($output[1], 0, 5);
echo $output;

?>
Pagina 5

En nu?

Met deze basis kennis kun je op weg gaan om je eigen Client - Server
script uit te werken. Mogelijkheden zijn bijvoorbeeld: een tool om
direct naar je weblog te posten, een simpele chat client of misschien
iets als een zoek-tool. Wees creatief! =)

Reacties

0
Nog geen reacties.