Skip to content

10. PHP sunucuları

PHP programları bir WEB sunucusu tarafından çalıştırılabildiğinden, bu tür bir program birden fazla istemciye hizmet verebilen bir sunucu programı haline gelir. İstemci açısından, bir WEB hizmetini çağırmak, bu hizmetin URL adresini istemekle eşdeğerdir. İstemci, herhangi bir dilde, özellikle de PHP dilinde yazılabilir. Bu son durumda, az önce incelediğimiz ağ işlevleri kullanılır. Ayrıca, bir WEB hizmetiyle “iletişim kurabilmemiz”, yani bir web sunucusu ile istemcileri arasındaki http iletişim protokolünü anlamamız gerekir. Aşağıdaki programların amacı da budur.

9.2. paragrafta açıklanan web istemcisi, HTTP protokolünün bir kısmını keşfetmemizi sağladı.

Image

En basit haliyle, istemci-sunucu arasındaki veri alışverişi şu şekildedir:

  • istemci, web sunucusunun 80 numaralı bağlantı noktasına bir bağlantı açar
  • bir belgeyle ilgili bir istek gönderir
  • web sunucusu istenen belgeyi gönderir ve bağlantıyı kapatır
  • istemci de bağlantıyı kapatır

İstemci çeşitli türlerde olabilir: HTML formatında bir metin, bir resim, bir video... Bu, mevcut bir belge (statik belge) veya bir komut dosyası tarafından anında oluşturulan bir belge (dinamik belge) olabilir. İkinci durumda, web programlamasından söz edilir. Dinamik belge oluşturma komut dosyası çeşitli dillerde yazılabilir: PHP, Python, Perl, Java, Ruby, C#, VB.net, ...

Burada, metin belgelerini dinamik olarak oluşturmak için PHP kullanıyoruz.

  • [1]'te, istemci sunucuyla bir bağlantı kurar, PHP komut dosyasını ister ve bu komut dosyasına parametre gönderir ya da göndermez
  • [2]'te, web sunucusu PHP komut dosyasını PHP yorumlayıcısı aracılığıyla çalıştırır. Bu komut dosyası, [3] istemcisine gönderilen bir belge oluşturur
  • sunucu bağlantıyı sonlandırır. İstemci de aynısını yapar.

Web sunucusu aynı anda birden fazla istemciyi işleyebilir. WampServer yazılım paketi ile web sunucusu, Apache Vakfı'nın (http://www.apache.org/) açık kaynaklı bir sunucusu olan Apache sunucusudur. Aşağıdaki uygulamalarda WampServer çalıştırılmalıdır. Bu, üç yazılımı etkinleştirir: Apache web sunucusu, SGBD ve MySQL, PHP yorumlayıcısı.

Web sunucusu tarafından çalıştırılan komut dosyaları NetBeans aracıyla yazılacaktır. Şimdiye kadar konsol ortamında çalıştırılan PHP komut dosyalarını yazdık:

Kullanıcı, konsolu kullanarak bir PHP betiğinin çalıştırılmasını talep eder ve sonuçları alır.

Aşağıda ele alınacak istemci/sunucu uygulamalarında,

  • istemci komut dosyası konsol ortamında çalıştırılır
  • sunucu komut dosyası ise web ortamında çalıştırılır

Sunucunun PHP komut dosyası, dosya sisteminde herhangi bir yerde bulunamaz. Çünkü web sunucusu, kendisine istenen statik ve dinamik belgeleri yapılandırmada belirtilen konumlarda arar. WampServer'in varsayılan yapılandırması, belgelerin <WampServer>/www klasöründe aranmasını sağlar; burada <WampServer>, WampServer'in kurulum klasörüdür. Dolayısıyla, bir web istemcisi URL [http://localhost/D] ile D belgesini talep ederse, web sunucusu [<WampServer>/www/D] yolundaki D belgesini sunacaktır.

Aşağıdaki örneklerde, sunucu komut dosyalarını [www/exemples-web] klasörüne yerleştireceğiz. Bir sunucu komut dosyasının adı S.php ise, web sunucusuna URL ve [http://localhost/exemples-web/S.php] ile talep edilecektir. Böylece, [<WampServer>/www/exemples-web/S.php] adlı belge sunulacaktır.

'i NetBeans ile bir sunucu komut dosyası olarak oluşturmak için şu adımları izleyeceğiz:

  • [1]'te yeni bir proje oluşturuyoruz
  • [2]'te, [PHP] kategorisini ve [PHP Application] projesini seçiyoruz
  • [3]'te projeye bir ad veriyoruz
  • [4]'te proje için bir klasör seçiyoruz
  • [5]'te, komut dosyasının yerel bir web sunucusu tarafından çalıştırılması gerektiğini belirtiyoruz (komut dosyasının URL'i http://localhost/... şeklinde olacaktır). Yerel web sunucusu, WampServer'teki Apache web sunucusu olacaktır.
  • [6]'te, projenin URL adresini giriyoruz. Burada, projenin S.php komut dosyasının, URL ve [http://localhost/exemples-web/S.php] ile birlikte çağrılacağına karar veriyoruz. Yukarıda belirtilenlere göre, bu, dosya sistemindeki S.php betiğinin yolunun [<WampServer>/www/exemples-web/S.php] olacağı anlamına gelir. Bu, [7]'te belirtilmiştir. Burada, projedeki tüm S.php komut dosyalarının Apache web sunucusunun dizin yapısına kopyalanmasını istiyoruz.
  • [8] adlı yeni projeye.

Bir test komut dosyası yazalım:

  • [1]'te, [exemples-web] projesinde ilk komut dosyasını oluşturuyoruz: PHP
  • [2]'te ona bir isim veriyoruz
  • [3]'te, oluşturduktan sonra ona aşağıdaki içeriği giriyoruz

Devam etmek için WampServer'in çalıştırılması gerekiyor.

  • [4]'te, [exemple1.php] web betiğini çalıştırıyoruz. NetBeans daha sonra bilgisayarın varsayılan tarayıcısını başlatacak ve ona URL, [http://localhost/exemples-web/exemple1.php] ve [5] dosyalarını görüntülemesini isteyecektir
  • dosyalarını görüntülemesini isteyecektir; tarayıcı, sunucu betiğinin istemciye yazdıklarını görüntüler.

Bundan sonra, iki tür web istemcisiyle karşılaşacağız:

  • yukarıdaki gibi bir tarayıcı. Web sunucusunun şu biçimde bir yanıt gönderdiğini belirtmiştik: HTTP başlıkları, boş satır, metin. Tarayıcı yalnızca texte'i görüntüler.
  • PHP komut dosyası ise yanıtın tamamını (HTTP başlıkları, boş satır, metin) tarayıcıya görüntüler.

Bundan sonra,

  • sunucu komut dosyaları yukarıdaki [exemple1.php] gibi yazılacaktır
  • istemci komut dosyaları ise şimdiye kadar yazdığımız konsol komut dosyaları gibi yazılacaktır.

10.1. Tarih/saat istemci/sunucu uygulaması

10.1.1. Sunucu (web_01)


<?php
// zaman: 01/01/1970'den bu yana geçen milisaniye sayısı
// tarih-saat görüntüleme biçimi
// d: 2 basamaklı gün
// m: 2 basamaklı ay
// y: 2 basamaklı yıl
// H: saat 0,23
// i: dakika
// s: saniye
print date("d/m/y H:i:s",time());

Temel olarak, yukarıdaki PHP komut dosyası, ekran üzerine geçerli saati yazar. Ancak, bir web sunucusu tarafından çalıştırıldığında, genellikle ekranla ilişkilendirilen 1 numaralı akış, sunucuyu istemcisiyle bağlayan bağlantıya yönlendirilir. Dolayısıyla, bir web ortamında yukarıdaki komut dosyası, geçerli saati metin biçiminde istemciye gönderir.

Bu betiği NetBeans içinde çalıştıralım:

  • [1]'te komut dosyası çalıştırılır. Ardından bir web tarayıcısı açılır.
  • [2]'te, web tarayıcısı tarafından istenen URL
  • [3]'te, sunucu komut dosyası tarafından gönderilen metin

İstemci tarayıcısı, web sunucusuyla iletişim kurmak için HTTP protokolünü kullanır. Bu protokolün yapısını daha önce açıklamıştık.

İstemci, üç bölüme ayrılabilen metin satırları gönderir: HTTP başlıkları, boş satır ve belge. Web sunucusuna gönderilen belge genellikle boştur ya da bir dizi parami=vali parametresinden oluşur; burada vali, kullanıcının bir HTML formuna girdiği bir değerdir.

Sunucunun yanıtı da aynı biçime sahiptir: HTTP başlıkları, boş satır, belge; burada document, bu sefer istemci tarayıcısı tarafından istenen belgedir. İstemci parametreler göndermişse, gönderilen belge genellikle bu parametrelere bağlıdır.

Firefox tarayıcısıyla, istemci ile web sunucusu arasındaki gerçek veri alışverişini inceleyebilirsiniz. Bu veri alışverişini izlemeye olanak tanıyan, Firebug adlı bir eklenti mevcuttur. Firebug, URL [https://addons.mozilla.org/fr/firefox/addon/firebug/] adresinden edinilebilir. Bu URL’yi ziyaret etmek için Firefox tarayıcısını kullanıyorsanız, Firebug eklentisini indirebilirsiniz. Aşağıdaki açıklamalarda, Firebug eklentisinin indirilip kurulduğunu varsayıyoruz. Eklentiye, Firefox menüsündeki bir seçenek aracılığıyla erişilebilir:

 

Firefox tarayıcı penceresinde bir Firebug penceresi açılır. Bu pencerenin kendi menüsü de vardır:

 

Bir istek sırasında istemci/sunucu arasındaki veri alışverişini incelemek için HTTP, Firefox tarayıcısıyla URL [http://localhost/exemples-web/web_01.php] isteğini gönderiyoruz. Bunun üzerine Firebug penceresi bilgilerle doluyor:

Yukarıda, istemci-sunucu arasındaki veri alışverişinin bir özeti yer almaktadır:

  • [1]: istemci şu komutu gönderdi HTTP: GET /exemples-web/web_01.php HTTP/1.1 komutunu göndererek [web01.php] belgesini talep etti
  • [2]: sunucu, istenen belgeyi bulduğunu belirten şu yanıtı gönderdi: HTTP/1.1 200 OK.

Firebug ile tüm iletişim verilerini elde edebilirsiniz. Bunun için URL'i "açmanız" yeterlidir:

Yukarıda, istemci (İstek) ile sunucu (Yanıt) arasında alışverişi yapılan HTTP başlıklarını görüyoruz. İletişimin kaynak kodunu (c.a.d) ve gerçekten alışverişi yapılan metin satırlarını ([1]) elde etmek mümkündür. Böylece şu kaynak kodu elde ederiz:

 

Web sunucusu için bir istemci komut dosyası yazmak için, tarayıcının davranışını taklit etmemiz yeterlidir. Sunucuyla bağlantı kurduktan sonra, istemci komut dosyası yukarıdaki isteğin 8 satırını gönderebilir. Aslında hepsi gerekli değildir ve biz sadece aşağıdaki üç satırı göndereceğiz:

1
2
3
GET /exemples-web/web01.php HTTP/1.1
Host: localhost
Connection: close
  • 1. satır: İstenen belgeyi ve kullanılan HTTP protokolünü belirtir
  • 2. satır: istemci komut dosyasının bulunduğu makinenin adını verir
  • 3. satır: Alışverişin ardından istemcinin sunucuyla olan bağlantısını kapatacağını belirtir

Şimdi sunucunun yanıtına bakalım. Bu yanıtın PHP [web_01.php] betiği tarafından üretildiğini biliyoruz. Yukarıda, yanıtın HTTP başlıklarını görüyoruz. [web01.php] betiğinin kodu, bunların bu betik tarafından oluşturulmadığını gösteriyor. Sunucu betiğinin yapılandırmasını hatırlayalım:

Yanıtın HTTP başlıklarını oluşturan web sunucusudur. Sunucu komut dosyası bunları kendisi oluşturabilir. Biraz ileride bunun bir örneğini göreceğiz.

Web sunucusunun yanıtının şu biçimde olduğunu söylemiştik: HTTP başlıkları, boş satır, belge. Belge bir metin belgesi ise, bunu Firebug’un [Réponse] sekmesinde görebiliriz:

 

Bu yanıt, [web_01.php] komut dosyası tarafından oluşturulmuştur.

10.1.2. Bir istemci (client1_web_01)

Şimdi önceki hizmet için bir istemci komut dosyası yazıyoruz. İstemcinin şunları yapması gerektiğini biliyoruz:

  • web sunucusuyla bir bağlantı kurmalı
  • şu metni göndermek: HTTP başlıkları, boş satır
  • sunucu, istemciyle olan bağlantısını kapatana kadar sunucudan gelen yanıtın tamamını okumak
  • sunucuyla olan bağlantıyı kapatmak

İstemci betiği, NetBeans konsol ortamında çalıştırılır:

  • [1]'te, [client1_web_01.php] istemci betiği, [exemples] adlı NetBeans projesine dahil edilmiştir
  • [2]'te, NetBeans projesi [exemples]'in özellikleri
  • [3]'te, NetBeans projesi [exemples] "komut satırı" modunda çalışır; buna "konsol" modu da diyoruz.

İstemci komut dosyasının kodu şöyledir:


<?php

// veriler
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_01.php";
// $HOTE'in 80 numaralı bağlantı noktasında bir bağlantı açıldı
$connexion = fsockopen($HOTE, $PORT);
// hata mı?
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}
// HTTP protokolünün başlıkları (headers) boş bir satırla bitmelidir
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// Ana Bilgisayar
fputs($connexion, "Host: localhost\n");
// Bağlantı
fputs($connexion,"Connection: close\n");
// boş satır
fputs($connexion,"\n");
// sunucu artık $connexion kanalında yanıt verecektir. Tüm
// verilerini gönderecek ve ardından kanalı kapatacaktır. Dolayısıyla istemci, kanal kapatılana kadar $connexion
// kanal kapanana kadar gelen tüm verileri okur
while ($ligne = fgets($connexion, 1000)) {
  print "$ligne";
}// süresince
// istemci de sırayla bağlantıyı kapatana kadar
fclose($connexion);
// son
exit;

Açıklamalar

  • 8. satır: sunucuya bağlantı kuruluyor
  • 16. satır: HTTP GET komutu
  • 18. satır: HTTP Host komutu
  • 20. satır: HTTP komutu Bağlantı
  • 22. satır: boş satır
  • satır 26-28: sunucu bağlantıyı kapatana kadar sunucu tarafından gönderilen tüm metin satırlarının okunması.
  • satır 30: istemci de bağlantıyı kapatır

Sonuçlar

İstemci komut dosyasının çalıştırılması aşağıdaki sonuçları verir:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Date: Wed, 17 Aug 2011 13:35:00 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 17
Connection: close
Content-Type: text/html

17/08/11 13:35:00

Açıklamalar

  • 1-7. satırlar: Web sunucusundan gelen HTTP yanıtı.
  • 8. satır: HTTP başlıklarının sonunu belirten boş satır
  • 9. satır ve sonrası: belge. Burada, geçerli tarih ve saati gösteren basit bir metin yer almaktadır. Bu metin, PHP komut dosyası tarafından 1 numaralı çıkışa yazılan metindir.
  • 1. satır: Sunucu, istenen belgeyi bulduğunu bildirir.
  • 2. satır: sunucunun geçerli tarihi ve saati
  • 3. satır: web sunucusunun kimliği
  • 4. satır: Aşağıdaki belgenin PHP komut dosyası tarafından oluşturulan bir belge olduğunu belirtir
  • 5. satır: belgenin karakter sayısı
  • 6. satır: Sunucu, belgeyi gönderdikten sonra bağlantıyı kapatacağını belirtir
  • 7. satır: Sunucu tarafından gönderilen belgenin HTML biçiminde bir metin olduğunu belirtir. Bu bilgi burada yanlıştır. Belge, belirli bir biçime sahip olmayan metindir. Belge, HTML biçiminde bir metin olmadığında, bunu belirtmek PHP betiğinin görevidir. Burada bunu yapmadık.

10.1.3. İkinci bir istemci (client2_web_01)

Önceki istemci, web sunucusunun gönderdiği her şeyi görüntülüyordu. Uygulamada, genellikle yanıtın HTTP başlıkları göz ardı edilir ve belgenin içeriği kullanılır. Burada, PHP komut dosyası tarafından sunucudan gönderilen tarih ve saati almaya çalışıyoruz. Bu bilgileri bir düzenli ifade kullanarak alacağız.


<?php

// bir web sunucusu tarafından gönderilen bilgileri almak
// veriler
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_01.php";
// $HOTE'in 80 numaralı bağlantı noktasında bir bağlantı açılıyor
$connexion = fsockopen($HOTE, $PORT);
// hata mı?
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}
// HTTP protokolünün başlıkları (headers) boş bir satırla bitmelidir
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// Ana Bilgisayar
fputs($connexion, "Host: localhost\n");
// Bağlantı
fputs($connexion,"Connection: close\n");
// boş satır
fputs($connexion,"\n");
// sunucu artık $connexion kanalında yanıt verecektir. Bu verilerin tümünü
// bu verileri gönderecek ve ardından kanalı kapatacaktır. Dolayısıyla istemci, aradığı jj/mm/aa hh:mm:ss biçimindeki satırı bulana kadar $connexion
// kanalından gelen tüm verileri okur ve aradığı jj/mm/aa hh:mm:ss biçimindeki satırı bulana kadar devam eder
while ($ligne = fgets($connexion, 1000)) {
  print "$ligne";
  if (preg_match("/(\d\d)\/(\d\d)\/(\d\d) (\d\d):(\d\d):(\d\d)/", $ligne, $champs)) {
     // şeklindeki satırı bulana kadar okur; # alanları alınır
    array_shift($champs); // alanlar dizisinin ilk elemanı kaldırılır
     // 6 alanı 6 değişkene aktarır
    list($j, $m, $a, $h, $i, $s) = $champs;
     // sonucun görüntülenmesi
    print "\ndateheure=[$j,$m,$a,$h,$i,$s]\n";
  }//if
}//while
// sırayla istemci bağlantıyı kapatır
fclose($connexion);
// son
exit;

Sonuçlar

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2011 14:14:51 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 17
Connection: close
Content-Type: text/html

17/08/11 14:14:51
dateheure=[17,08,11,14,14,51]

10.2. Sunucu tarafından istemci tarafından gönderilen parametrelerin alınması

HTTP protokolünde, bir istemcinin web sunucusuna parametreleri iletmek için iki yöntemi vardır:

  • hizmetin URL'ini şu biçimde talep eder

GET url?param1=val1&param2=val2&param3=val3… HTTP/1.0

şeklinde hizmetin URL kodunu talep eder; burada vali değerleri, belirli ayrılmış karakterlerin onaltılık değerleriyle değiştirilmesi için önceden kodlanmalıdır.

  • hizmetin URL kodunu şu biçimde talep eder:

POST url HTTP/1.0

ardından sunucuya gönderilen HTTP başlıkları arasına şu başlığı ekler:

Content-length=N

İstemci tarafından gönderilen diğer başlıklar boş bir satırla sona erer. Ardından verilerini şu biçimde gönderebilir:

val1&param2=val2&param3=val3…

şeklinde gönderebilir; burada vali değerleri, GET yönteminde olduğu gibi önceden kodlanmalıdır. Sunucuya gönderilen karakter sayısı N olmalıdır; burada N,

Content-length=N

Müşteri tarafından gönderilen önceki parami parametrelerini alan PHP komut dosyası, bu parametrelerin değerlerini aşağıdaki tablodan alır:

  • $_GET["parami"] komutu için GET
  • $_POST["parami"], POST komutu için

10.2.1. GET (client1_web_02) müşterisi

Aşağıdaki PHP komut dosyası, sunucuya üç [nom, prenom, age] parametresi gönderir.


<?php

// istemci: GET yöntemi ile sunucuya ad, soyad ve yaş bilgilerini gönderir
// veriler
$HOTE = "localhost";
$PORT = 80;
$URL = "/exemples-web/web_02.php";
list($prenom, $nom, $age) = array("jean-paul", "de la hûche", 45);
// web sunucusuna bağlantı
$connexion = fsockopen($HOTE, $PORT);
// hata durumunda geri dönüş
if (!$connexion) {
  print "Echec de la connexion au site ($HOTE,$PORT) : $erreur";
  exit;
}//if
// bilgilerin sunucuya gönderilmesi PHP
// bilgilerin kodlanması
$infos = "prenom=" . urlencode(utf8_decode($prenom)) . "&nom=" . urlencode(utf8_decode($nom)) . "&age=" . urlencode("$age");
// konsol izleme
print "infos envoyées au serveur (GET)=$infos\n";
print "URL demandée=[$URL?$infos]\n\n";
// HTTP protokolünün başlıkları (headers) boş bir satırla bitmelidir
// GET
fputs($connexion, "GET $URL?$infos HTTP/1.1\n");
// Ana Bilgisayar
fputs($connexion, "Host: localhost\n");
// Bağlantı
fputs($connexion,"Connection: close\n");
// boş satır
fputs($connexion,"\n");
// sunucu artık $connexion kanalında yanıt verecektir. Tüm
// verilerini alır ve ardından kanalı kapatır. İstemci, kanal kapanana kadar $connexion'ten gelen tüm verileri okur
while ($ligne = fgets($connexion, 1000))
  print "$ligne";
// istemci de bağlantıyı kapatır
fclose($connexion);

Açıklamalar

  • 7. satır: Sunucu betiğinin URL'si
  • 8. satır: 3 parametrenin değerleri
  • 10. satır: Web sunucusuyla bağlantı kurulması
  • 18. satır: 3 parametrenin kodlanması. NetBeans ile yazılmış ve karakter kodlaması UTF-8 olan bir komut dosyası kullanıyoruz. Dolayısıyla, 8. satırdaki 3 parametre değeri UTF-8 olarak kodlanmıştır. utf8_decode işlevi, bu kodlamayı ISO-8859-1'e dönüştürür. Bu işlem tamamlandıktan sonra, değerler URL için kodlanabilir. Alfabetik olmayan tüm karakterler, xx'in karakterin onaltılık değeri olduğu %xx ile değiştirilir. Boşluklar ise + işaretiyle değiştirilir.
  • 24. satır: İstenen URL, $URL?$infos şeklindedir; burada $infos, nom=val1&prenom=val2&age=val3 biçimindedir.

10.2.2. Sunucu (web_02)

Sunucu, aldığı veriyi olduğu gibi görüntüler.


<?php

// hata yönetimi
ini_set("display_errors", "off");

// sunucu, istemci tarafından gönderilen bilgileri alır
// burada ad=P&soyad=N&yaş=A
// bu bilgiler otomatik olarak değişkenlerde kullanılabilir hale gelir
// $_GET['prenom'], $_GET['nom'], $_GET['age']
// bu bilgiler müşteriye geri gönderilir

// başlık UTF-8
header("Content-Type: text/plain; charset=utf-8");

// sunucuya gönderilen parametreler
$prenom = isset($_GET['prenom']) ? $_GET['prenom'] : "";
$nom = isset($_GET['nom']) ? $_GET['nom'] : "";
$age = isset($_GET['age']) ? $_GET['age'] : "";

// müşteriye yanıt
$réponse = "informations reçues du client [" .
        utf8_encode(htmlspecialchars($prenom, ENT_QUOTES)) .
        "," . utf8_encode(htmlspecialchars($nom, ENT_QUOTES)) .
        "," . utf8_encode(htmlspecialchars($age, ENT_QUOTES)) . "]\n";
print $réponse;

Açıklamalar

  • 13. satır: HTTP "Content-Type" başlığını ayarlar. Varsayılan olarak, web sunucusu şu başlığı gönderir
Content-Type: text/html

başlığını gönderir; bu, yanıtın HTML biçiminde metin olduğunu belirtir. Burada ise yanıt, UTF-8 ile kodlanmış karakterler içeren biçimlendirilmemiş metin olacaktır:

Content-Type: text/plain; charset=utf-8

HTTP başlıkları, sunucunun yanıtından önce gönderilmelidir. Dolayısıyla yukarıda, header işlevinin çağrılması, herhangi bir print komutundan önce yer alacaktır.

  • 16-18. satırlar: $_GET tablosundan 3 parametre alınır.
  • 21. satır: İstemciye yanıt olarak gönderilecek karakter dizisi oluşturulur. Bazı karakterlerin HTML'te özel anlamları vardır ve görüntülenebilmeleri için HTML varlıklarıyla değiştirilmeleri gerekir. htmlspecialchars($string) işlevi, tüm bu karakterleri $string dizisindeki karşılıklarıyla değiştirir. Örneğin, $ karakteri &amp olarak değiştirilir. Ardından, 13. satırda yanıtın UTF-8 metni olacağını belirttiğimiz için, alınan değerleri UTF-8 olarak kodlarız.
  • 25. satır: Yanıt, istemciye gönderilir

Test 1

[web_02] komut dosyasını NetBeans'ten çalıştıralım. Ardından, URL [http://localhost/exemples-web/web_02.php]'i görüntülemek için bir tarayıcı açılır:

  • [1]'te, tarayıcı URL [http://localhost/exemples-web/web_02.php]'i görüntüler. Bu URL'ye parametre eklemediğimiz için, sunucu boş parametrelerle yanıt vermiştir. Sunucunun yanıtının, print komutuyla yazılan yanıt olduğunu hatırlayalım.
  • [2] örneğinde, URL adresinin sonuna parametreler ekliyoruz. Bu sefer sunucu komut dosyası parametreleri doğru bir şekilde geri gönderiyor.

Sunucu komut dosyasını çalıştırmak için NetBeans'e gerek olmadığını belirtelim. Sunucu komut dosyasının URL adresini bir tarayıcıya yazmak, bu komut dosyasının çalıştırılması için yeterlidir.

Test 2

NetBeans'te [client1_web_02.php] istemcisini çalıştırıyoruz. Aşağıdaki yanıtı alıyoruz:

infos envoyées au serveur (GET)=prenom=jean-paul&nom=de+la+h%FBche&age=45
URL demandée=[/exemples-web/web_02.php?prenom=jean-paul&nom=de+la+h%FBche&age=45]

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2011 14:31:01 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 59
Connection: close
Content-Type: text/plain; charset=utf-8

informations reçues du client [jean-paul,de la hûche,45]
  • 1. satır: 3 parametrenin kodlanması. û karakterinin %FB haline geldiği görülüyor.
  • 12. satır: sunucunun yanıtı

10.2.3. POST (client2_web_03) istemcisi

Bir HTTP istemcisi, web sunucusuna şu metin dizisini gönderir: HTTP başlıkları, boş satır, belge. Önceki istemcide bu dizi şöyleydi:

GET /url?paramètres HTTP/1.1
… autres entêtes HTTP
ligne vide

Belge yoktu. Parametreleri iletmenin başka bir yolu daha vardır; buna POST yöntemi denir. Bu durumda, web sunucusuna gönderilen metin dizisi şöyledir:

POST /url HTTP/1.1
… autres entêtes HTTP
ligne vide
paramètres

Bu sefer, GET istemcisi için HTTP başlıklarında yer alan parametreler, POST istemcisinde başlıkların arkasında gönderilen belgenin bir parçasıdır.

POST istemcisinin komut dosyası şöyledir:


<?php

// müşteri: POST yöntemi ile sunucuya ad, soyad ve yaş bilgilerini gönderir
// veriler
$HOTE = "localhost";
$PORT = 80;
$URL = "/exemples-web/web_03.php";
list($prenom, $nom, $age) = array("jean-paul", "de la hûche", 45);
// web sunucusuna bağlantı
$connexion = fsockopen($HOTE, $PORT);
// hata durumunda geri dönüş
if (!$connexion) {
  print "Echec de la connexion au site ($HOTE,$PORT) : $erreur";
  exit;
}//if
// bilgilerin sunucuya gönderilmesi PHP
// bilgilerin kodlanması
$infos = "prenom=" . urlencode(utf8_decode($prenom)) . "&nom=" . urlencode(utf8_decode($nom)) . "&age=" . urlencode("$age");
print "client : infos envoyées au serveur (POST) : $infos\n";
// URL $URL sunucusuna, ona (POST) parametreleri göndererek bağlanılır
// HTTP protokolünün başlıkları (headers) boş bir satırla bitmelidir
// POST
fputs($connexion, "POST $URL HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Bağlantı
fputs($connexion,"Connection: close\n");
// Content-type
fputs($connexion, "Content-type: application/x-www-form-urlencoded\n");
// İçerik Uzunluğu
// Gönderilecek bilgilerin boyutu (karakter sayısı) gönderilir
fputs($connexion, "Content-length: " . strlen($infos) . "\n");
// boş bir satır gönderilir
fputs($connexion, "\n");
// bilgiler gönderilir
fputs($connexion, $infos);
// sunucu şimdi $connexion kanalında yanıt verecek. Tüm
// verilerini gönderecek ve ardından kanalı kapatacaktır. İstemci, $connexion
// kanal kapanana kadar gelen tüm verileri okur
while ($ligne = fgets($connexion, 1000))
  print "$ligne";
// sonra istemci de bağlantıyı kapatır
fclose($connexion);

Açıklamalar

  • 7. satır: POST istemcisinin bağlanacağı web hizmetinin URL başlığı. Bu web hizmeti yakında açıklanacaktır.
  • 8. satır: Web hizmetine iletilecek parametreler
  • 10. satır: web sunucusuna bağlantı
  • 18. satır: web hizmetine gönderilecek parametrelerin kodlaması
  • 23. satır: Http POST komutu
  • 25. satır: Http Host komutu
  • 27. satır: Http Connection komutu
  • 29. satır: Http Content-type komutu. Bu HTTP başlığıyla daha önce karşılaşmıştık. Bir belge gönderildiğinde her seferinde bu başlık bulunur. Bir HTML belgesi gönderen bir web sunucusu, HTTP başlığını kullanır
Content-type : text/html

Biçimsiz metin gönderiyorsa, HTTP başlığını kullanır

Content-type : text/plain

POST istemcimiz, param1=val1&param2=val2&... biçiminde bir metin içeren bir belge gönderir. Bu belge türü, application/x-www-form-urlencoded türündedir. Nedenini açıklamayacağız, çünkü bu, web formunun ne olduğunu açıklamamızı gerektirecektir.

  • 32. satır: Content-length komutu. Bu HTTP başlığıyla daha önce karşılaşmıştık. Bir belge gönderildiğinde her zaman bulunur. Belgenin bayt sayısını belirtir.
  • 34. satır: Başlıkların sonunu belirten boş satır HTTP
  • 36. satır: Parametrelerin gönderilmesi
  • satır 40-41: sunucudan gelen tam yanıtın okunması
  • satır 43: bağlantının kapatılması

10.2.4. Sunucu (web_03)

[web_03] web hizmeti, [web_02] web hizmeti ile aynı işlevi görür. POST istemcisi tarafından gönderilen parametreleri okur ve bunları istemciye geri gönderir. Kod şöyledir:


<?php

// hata yönetimi
ini_set("display_errors", "off");
// başlık UTF-8
header("Content-Type: text/plain; charset=utf-8");

// sunucu tarafından istemci tarafından gönderilen bilgilerin alınması
// burada ad=P&soyad=N&yaş=A
// bu bilgiler otomatik olarak değişkenlerde kullanılabilir hale gelir
// $_POST['prenom'], $_POST['nom'], $_POST['age']
// değişkenlerinde otomatik olarak kullanılabilir hale gelir ve
// sunucuya gönderilen parametreler
$prenom = isset($_POST['prenom']) ? $_POST['prenom'] : "";
$nom = isset($_POST['nom']) ? $_POST['nom'] : "";
$age = isset($_POST['age']) ? $_POST['age'] : "";
// istemciye yanıt
$réponse = "informations reçues du client [" .
        utf8_encode(htmlspecialchars($prenom, ENT_QUOTES)) .
        "," . utf8_encode(htmlspecialchars($nom, ENT_QUOTES)) .
        "," . utf8_encode(htmlspecialchars($age, ENT_QUOTES)) . "]\n";
print $réponse;

Açıklamalar

  • 14-16. satırlar: POST istemcisi tarafından gönderilen parametreler, bunları alan web hizmeti için $_POST dizisinde kullanılabilir hale gelir.
  • 6. satır: HTTP Content-Type başlığı. HTTP başlıklarında, müşteriye geri gönderilen belgenin boyutunu belirten HTTP Content-Length başlığının bulunmaması şaşırtıcı olabilir. Web sunucusunun varsayılan olarak HTTP başlıklarını gönderdiğini görmüştük. Content-Length başlığı da bunlardan biridir.

Sonuçlar

NetBeans'te sunucu komut dosyası yazıldıktan sonra, WampServer Apache sunucusu üzerinden hemen kullanılabilir hale gelir. Bunun yapılandırma yoluyla sağlandığını hatırlayalım (bkz. paragraf 10). Sunucuya sorgu gönderen istemciyi çalıştırdığımızda şu yanıtı alırız:

client : infos envoyées au serveur (POST) : prenom=jean-paul&nom=de+la+h%FBche&age=45
HTTP/1.1 200 OK
Date: Sat, 20 Aug 2011 12:55:19 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 59
Connection: close
Content-Type: text/plain; charset=utf-8

informations reçues du client [jean-paul,de la hûche,45]
  • 2-10. satırlar: sunucunun yanıtı
  • 2-8. satırlar: HTTP başlıkları
  • 10. satır: belge
  • 6. satır: HTTP Content-Length başlığı. Bu başlık sunucu komut dosyası tarafından oluşturulmadığından, web sunucusu tarafından oluşturulmuştur.
  • 8. satır: Sunucu betiği tarafından oluşturulan tek başlık

10.3. WEB sunucusunun ortam değişkenlerinin alınması

Bir sunucu betiği, tanıyabileceği bir web ortamında çalışır. Bu ortam, $_SERVER sözlüğünde saklanır. Öncelikle, müşterilerine bu sözlüğün içeriğini gönderen bir sunucu uygulaması yazıyoruz.

10.3.1. Sunucu (web_04)


<?php

// hata yönetimi
ini_set("display_errors", "off");
// başlık UTF-8
header("Content-Type: text/plain; charset=utf-8");

// sunucu ortamında mevcut değişkenlerin listesi müşteriye geri gönderilir
foreach ($_SERVER as $clé => $valeur) {
  print "[$clé,$valeur]\n";
}
  • $_SERVER sözlüğündeki (anahtar, değer) çiftleri istemcilere gönderilir.

İstemci bir web tarayıcısı olduğunda elde edilen sonuç şöyledir:

Image

İşte bazı değişkenlerin anlamları (Windows için. Linux'ta farklı olurlar):

HTTP_CMDE
CMDE, istemci tarafından gönderilen HTTP başlığını temsil eder. Tüm bu başlıklara erişim sağlanabilir.
PATH
sunucu betiğinin çalıştığı makinedeki yürütülebilir dosyaların yolu
COMSPEC
DOS komut yorumlayıcısının yolu
PATHEXT
yürütülebilir dosyaların uzantıları
WINDIR
Windows kurulum klasörü
SERVER_SIGNATURE
web sunucusunun imzası. Burada bir şey yok.
SERVER_SOFTWARE
web sunucusunun türü
SERVER_NAME 
web sunucusu makinesinin İnternet adı
SERVER_PORT
web sunucusunun dinleme bağlantı noktası
SERVER_ADDR
web sunucusu makinesinin adresi IP
REMOTE_ADDR
IP istemcinin adresi. Burada istemci, sunucuyla aynı makinedeydi.
REMOTE_PORT
istemcinin iletişim bağlantı noktası
DOCUMENT_ROOT
web sunucusu tarafından sunulan belgelerin dizin yapısının kökü
SERVER_ADMIN
web sunucusu yöneticisinin e-posta adresi
SCRIPT_FILENAME
sunucu betiğinin tam yolu
SERVER_PROTOCOL
web sunucusu tarafından kullanılan protokol sürümü HTTP
REQUEST_METHOD
İstemci tarafından kullanılan HTTP komutu. Dört tane vardır: GET, POST, PUT, DELETE
QUERY_STRING
GET komutuyla gönderilen parametreler /url?parametreler
REQUEST_URI
istemci tarafından talep edilen URL. Tarayıcı http://machine[:port]/uri adresindeki URL'i talep ederse, REQUEST_URI=uri elde edilir
SCRIPT_NAME
$_SERVER['SCRIPT_FILENAME']=$_SERVER['DOCUMENT_ROOT'].$_SERVER['SCRIPT_NAME']

10.3.2. İstemci (client1_web_04)

Müşteri, sunucunun kendisine gönderdiği her şeyi olduğu gibi görüntüler.


<?php

// veriler
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_04.php";
// $HOTE'in 80 numaralı bağlantı noktasında bir bağlantı açılıyor
$connexion = fsockopen($HOTE, $PORT);
// hata mı?
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}

// URL web sunucusuna bağlanılıyor
// HTTP protokolünün başlıkları (headers) boş bir satırla bitmelidir
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// Ana Bilgisayar
fputs($connexion, "Host: localhost\n");
// Bağlantı
fputs($connexion,"Connection: close\n");
// boş satır
fputs($connexion,"\n");
// sunucu artık $connexion kanalında yanıt verecektir. Tüm
// verilerini gönderecek ve ardından kanalı kapatacaktır. Dolayısıyla istemci, kanal kapatılana kadar $connexion
// kanal kapanana kadar gelen tüm verileri okur
while ($ligne = fgets($connexion, 1000)) {
  print "$ligne";
}// süresince
// istemci de sırayla bağlantıyı kapatana kadar
fclose($connexion);
// son
exit;

Sonuçlar

HTTP/1.1 200 OK
Date: Sat, 20 Aug 2011 14:11:58 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 1353
Connection: close
Content-Type: text/plain; charset=utf-8

[HTTP_HOST,localhost]
[HTTP_CONNECTION,close]
[PATH,C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Microsoft SQL Server\90\Tools\binn\;...;]
[SystemRoot,C:\Windows]
[COMSPEC,C:\Windows\system32\cmd.exe]
[PATHEXT,.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC]
[WINDIR,C:\Windows]
[SERVER_SIGNATURE,]
[SERVER_SOFTWARE,Apache/2.2.17 (Win32) PHP/5.3.5]
[SERVER_NAME,localhost]
[SERVER_ADDR,127.0.0.1]
[SERVER_PORT,80]
[REMOTE_ADDR,127.0.0.1]
[DOCUMENT_ROOT,C:/serveursSGBD/wamp21/www/]
[SERVER_ADMIN,admin@localhost]
[SCRIPT_FILENAME,C:/serveursSGBD/wamp21/www/exemples-web/web_04.php]
[REMOTE_PORT,54552]
[GATEWAY_INTERFACE,CGI/1.1]
[SERVER_PROTOCOL,HTTP/1.1]
[REQUEST_METHOD,GET]
[QUERY_STRING,]
[REQUEST_URI,/exemples-web/web_04.php]
[SCRIPT_NAME,/exemples-web/web_04.php]
[PHP_SELF,/exemples-web/web_04.php]
[REQUEST_TIME,1313849518]

10.4. Oturum yönetimi WEB

Önceki istemci/sunucu örneklerinde şu şekilde çalışıyordu:

  • istemci, web hizmeti sunucusunun 80 numaralı bağlantı noktasına bir bağlantı açar
  • Http başlıkları, boş satır, [document] metin dizisini gönderir
  • yanıt olarak sunucu da aynı türde bir dizi gönderir
  • sunucu, istemciyle olan bağlantıyı sonlandırır
  • istemci, sunucuya olan bağlantıyı sonlandırır

Aynı istemci kısa bir süre sonra web sunucusuna yeni bir istek gönderirse, istemci ile sunucu arasında yeni bir bağlantı kurulur. Sunucu, bağlanan istemcinin daha önce gelip gelmediğini veya bunun ilk istek olup olmadığını bilemez. İki bağlantı arasında sunucu istemcisini "unutur". Bu nedenle, HTTP protokolünün durum bilgisi içermeyen bir protokol olduğu söylenir. Oysa sunucunun müşterilerini hatırlaması yararlıdır. Örneğin, bir uygulama güvenliyse, müşteri kimliğini doğrulamak için sunucuya bir kullanıcı adı ve şifre gönderir. Sunucu iki bağlantı arasında müşterisini "unutursa", müşteri her yeni bağlantıda kimliğini doğrulamak zorunda kalır ve bu da mümkün değildir.

Bir müşteriyi takip etmek için sunucu şu şekilde hareket eder: Bir müşteriden gelen ilk istek sırasında, yanıtına bir tanımlayıcı ekler ve müşteri daha sonra her yeni istekte bu tanımlayıcıyı sunucuya geri göndermelidir. Her müşteri için farklı olan bu tanımlayıcı sayesinde sunucu, bir müşteriyi tanıyabilir. Böylece, müşterinin tanımlayıcısına benzersiz bir şekilde eşleştirilmiş bir dosya biçiminde bu müşteri için bir bellek yönetebilir.

Teknik olarak süreç şu şekildedir:

  • yeni bir müşteriye verdiği yanıtta sunucu, HTTP Set-Cookie: MotClé=Kimlik başlığını ekler. Bunu yalnızca ilk istekte yapar.
  • Müşteri, sonraki isteklerinde sunucunun kendisini tanıması için HTTP başlığı aracılığıyla kimlik bilgisini şu şekilde gönderecektir: MotClé=Kimlik.

Sunucunun, karşısındaki kişinin daha önce ziyaret etmiş bir müşteri değil de yeni bir müşteri olduğunu nasıl anladığı merak edilebilir. Bunu, müşterinin HTTP başlıklarında bulunan HTTP ve Cookie başlıklarının varlığı gösterir. Yeni bir müşteri için bu başlık mevcut değildir.

Belirli bir müşterinin tüm bağlantıları bir oturum olarak adlandırılır.

10.4.1. Yapılandırma dosyası

PHP ile oturum yönetiminin düzgün çalışması için, sistemin doğru şekilde yapılandırılmış olması gerekir. Windows'ta yapılandırma dosyası PHP.ini'tir. Çalıştırma ortamına (konsol, web) göre, [PHP.ini] yapılandırma dosyası farklı klasörlerde aranmalıdır. Bu klasörleri bulmak için aşağıdaki komut dosyası kullanılır:

1
2
3
4
<?php

// bilgiler PHP
phpinfo();
  1. satırda, phpinfo işlevi, komut dosyasını çalıştıran PHP yorumlayıcısı hakkında bilgi verir. Özellikle, kullanılan [PHP.ini] yapılandırma dosyasının yolunu gösterir.

Konsol ortamında, aşağıdakine benzer bir sonuç elde edilir:

1
2
3
4
5
6
Configuration File (PHP.ini) Path => C:\Windows
Loaded Configuration File => C:\serveursSGBD\wamp21\bin\PHP\php5.3.5\PHP.ini
Scan this dir for additional .ini files => (none)
Additional .ini files parsed => (none)
  1. satır: ana yapılandırma dosyası c:\windows\PHP.ini

  2. satır: ikincil yapılandırma dosyası C:\serveursSGBD\wamp21\bin\PHP\php5.3.5\PHP.ini'tir. Bu dosya, ana yapılandırma dosyasındaki belirli yapılandırma seçeneklerini değiştirmeye olanak tanır.

Bir web ortamında şu sonuç elde edilir:

Image

Buradaki ikincil yapılandırma dosyası, konsol ortamındakiyle aynı değildir. Biz konsol ortamındaki dosyayı inceleyeceğiz. Bu dosyada bir session bölümü bulunur:

[Session]
session.save_handler = files
session.save_path = "C:/serveursSGBD/wamp21/tmp"
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.serialize_handler = PHP
session.cache_expire = 180
session.hash_function = 0
  • 1. satır: bir istemci oturumunun verileri bir dosyaya kaydedilir
  • 3. satır: Oturum verilerinin kaydedildiği klasör. Bu klasör yoksa, herhangi bir hata bildirilir ve oturum yönetimi çalışmaz.
  • 4-5. satırlar: Oturum kimliğinin Set-Cookie ve Cookie HTTP başlıkları tarafından yönetildiğini gösterir
  • 6. satır: Set-Cookie başlığı, Set-Cookie: PHPSESSID=identifiant_de_session biçiminde olacaktır
  • 7. satır: Bir istemci oturumu otomatik olarak başlatılmaz. Sunucu komut dosyası, session_start() komutuyla bunu açıkça talep etmelidir.

10.4.2. Sunucu 1 (web_05)

Oturum kimliğinin yönetimi, bir web hizmeti için şeffaftır. Bu kimlik, web sunucusu tarafından yönetilir. Bir web hizmeti, session_start() komutu aracılığıyla istemcinin oturumuna erişebilir. Bu andan itibaren web hizmeti, $_SESSION sözlüğü aracılığıyla istemcinin oturumundaki verileri okuyabilir ve yazabilir. Aşağıdaki kod, üç sayacın oturum içinde yönetilmesini göstermektedir.


<?php

// hata yönetimi
ini_set("display_errors", "off");
// başlık UTF-8
header("Content-Type: text/plain; charset=utf-8");

// oturum açılıyor
session_start();
// 3 değişken oturuma ekleniyor
if (!isset($_SESSION['N1'])) {
  $_SESSION['N1'] = 0;
}
if (!isset($_SESSION['N2'])) {
  $_SESSION['N2'] = 10;
}
if (!isset($_SESSION['N3'])) {
  $_SESSION['N3'] = 100;
}
// 3 değişkenin artırılması
$_SESSION['N1']++;
$_SESSION['N2']++;
$_SESSION['N3']++;
// bilgilerin istemciye gönderilmesi
print "N1=".$_SESSION['N1']."\n";
print "N2=".$_SESSION['N2']."\n";
print "N3=".$_SESSION['N3']."\n";
// oturumun sonlandırılması
session_close();
  • 9. satır: bir müşteri oturumunun başlatılması
  • satır 11-13: $_SESSION dizisi, (anahtar, değer) çiftlerinden oluşan bir sözlüktür. Bu sözlüğe eklenen veriler, aynı müşterinin istekleri boyunca kalıcıdır. Bu, sunucudaki müşterinin belleğidir.
  • 11-19. satırlar: N1, N2 ve N3 sayacı oturumda değilse, bu sayacılar oturuma eklenir.
  • 21-23. satırlar: Sayılar bir birim artırılır
  • 25-27. satırlar: Değerleri istemciye gönderilir

İstemci-sunucu ilişkisinde, sunucu üzerindeki istemci oturumunun yönetimi hem istemciye hem de sunucuya bağlıdır:

  • sunucu, ilk istek sırasında müşterisine bir kimlik gönderme sorumluluğuna sahiptir
  • İstemcinin görevi ise her yeni istekte bu kimlik numarasını geri göndermektir. Bunu yapmazsa, sunucu bunun yeni bir istemci olduğunu varsayacak ve yeni bir oturum için yeni bir kimlik numarası oluşturacaktır.

Sonuçlar

İstemci olarak bir web tarayıcısı kullanıyoruz. Varsayılan olarak (aslında yapılandırma gereği), tarayıcı sunucunun kendisine gönderdiği oturum tanımlayıcılarını sunucuya geri gönderir. İstekler ilerledikçe, tarayıcı sunucu tarafından gönderilen üç sayacı alacak ve bunların değerlerinin arttığını görecektir.

  • [1], web hizmetine yapılan ilk istek [web_05]
  • [2]'e, üçüncü istek ise sayaçların doğru şekilde artırıldığını gösterir. İstekler boyunca sayaç değerleri gerçekten de hatırlanmaktadır.

Firebug'u kullanarak sunucu ile istemci arasında alışverişi yapılan HTTP başlıklarını inceleyelim. Sunucu ile mevcut oturumu sonlandırmak için Firefox'u kapatıp yeniden açıyoruz ve Firebug'u etkinleştiriyoruz. [web_05] hizmetini çağırıyoruz:

Yukarıda, sunucunun müşterinin ilk isteğine yanıt olarak gönderdiği oturum kimliğini görüyoruz. HTTP Set-Cookie başlığını kullanıyor.

Web tarayıcısında sayfayı yenileyerek (F5) yeni bir istek gönderelim:

Yukarıda iki husus dikkat çekmektedir:

  • Web tarayıcısı, HTTP Cookie başlığıyla oturum kimliğini geri gönderir.
  • Web hizmeti, yanıtında artık bu kimlik numarasını dahil etmemektedir. Artık her isteğinde bu kimlik numarasını göndermek istemcinin sorumluluğundadır.

10.4.3. İstemci 1 (client1_web_05)

Şimdi, önceki sunucu betiğinin bir istemci betiğini yazıyoruz. Oturum yönetiminde, bir web tarayıcısı gibi davranmalıdır:

  • Sunucunun ilk isteğine verdiği yanıtta, sunucunun kendisine gönderdiği oturum kimliğini bulması gerekir. Bu kimliği HTTP Set-Cookie başlığında bulacağını bilir.
  • Sonraki her isteğinde, aldığı kimlik numarasını sunucuya geri göndermelidir. Bunu HTTP Cookie başlığıyla yapacaktır.

Müşterinin kodu şöyledir:


<?php

// veriler
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_05.php";
// testler
$cookie = "";
for ($i = 0; $i < 5; $i++) {
  list($erreur, $cookie, $N1, $N2, $N3) = connecte($HOTE, $PORT, $urlServeur, $cookie);
  print "----------------------------\n";
  print "client(erreur,cookie,N1,N2,N3)=[$erreur,$cookie,$N1,$N2,$N3]\n";
  print "----------------------------\n";
}
// son
exit;

function connecte($HOTE, $PORT, $urlServeur, $cookie) {
  // müşteriyi ($HOTE,$PORT,$urlServeur) adreslerine bağlar
  // çerez $cookie boş değilse gönderir
  // yanıt olarak alınan tüm satırları görüntüler
  // $HOTE'in 80 numaralı bağlantı noktasında bir bağlantı açar
  $connexion = fsockopen($HOTE, $PORT);
  // hata mı?
  if (!$connexion)
    return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
  // $urlserveur'e bağlanılıyor
  // HTTP protokolünün başlıkları (headers) boş bir satırla bitmelidir
  // GET
  fputs($connexion, "GET $urlServeur HTTP/1.1\n");
  // Ana Bilgisayar
  fputs($connexion, "Host: localhost\n");
  // Bağlantı
  fputs($connexion, "Connection: close\n");
  // çerez boş değilse gönderilir
  if ($cookie) {
    fputs($connexion, "Cookie: $cookie\n");
  }//if
  // boş satır gönderilir
  fputs($connexion, "\n");
  // web sunucusunun yanıtını görüntüler
  // ve varsa çerezi ve Ni değerlerini almaya özen gösterilir
  $N = "";
  while ($ligne = fgets($connexion, 1000)) {
    print "$ligne";
    // çerez - yalnızca ilk yanıtta
    if (!$cookie) {
      if (preg_match("/^Set-Cookie: (.*?)\s*$/", $ligne, $champs)) {
        $cookie = $champs[1];
      }
    }
    // N1 değeri
    if (preg_match("/^N1=(.*?)\s*$/", $ligne, $champs))
      $N1 = $champs[1];
    // N2 değeri
    if (preg_match("/^N2=(.*?)\s*$/", $ligne, $champs))
      $N2 = $champs[1];
    // N3'in değeri
    if (preg_match("/^N3=(.*?)\s*$/", $ligne, $champs))
      $N3 = $champs[1];
  }//while
  // bağlantı kapatılır
  fclose($connexion);
  // geri dönüş
  return array("", $cookie, $N1, $N2, $N3);
}

Açıklamalar

  • 3-16. satırlar: ana program
  • 18-67. satırlar: connecte işlevi
  • 9-14. satırlar: İstemci, sunucuyu 5 kez çağırır ve N1, N2 ve N3 sayaçlarının ardışık değerlerini görüntüler. Oturum doğru şekilde yönetiliyorsa, bu sayaçlar her yeni istekte 1 artmalıdır.
  • 10. satır: connecte işlevi, $HOTE, $PORT ve $urlServeur parametrelerini kullanarak istemciyi web hizmetine bağlar. $cookie parametresi oturum kimliğini temsil eder. İlk çağrıda bu parametre boş bir dizedir. Sonraki çağrılarda ise, istemcinin ilk çağrısına yanıt olarak sunucu tarafından gönderilen oturum kimliği bu parametreye atanır. connecte işlevi, sonuç olarak $N1, $N2, $N3 sayacı değerlerini, $cookie oturum kimliğini ve olası bir hata olan $erreur'i döndürür.
  • 18. satır: connecte işlevi, klasik bir HTTP istemcisinin özelliklerini taşır. Yalnızca yeni özellikler hakkında açıklama yapacağız.
  • 30-40. satırlar: HTTP başlıklarının gönderilmesi.
  • 36-38. satırlar: Oturum kimliği biliniyorsa, sunucuya gönderilir
  • 44-66. satırlar: Sunucu tarafından gönderilen tüm metin satırlarının işlenmesi
  • 47-51. satırlar: Oturum kimliği henüz alınmamışsa, HTTP Set-Cookie başlığından bir düzenli ifade kullanılarak alınır.
  • 53-54. satırlar: N1 sayacı da bir düzenli ifade kullanılarak elde edilir
  • 56-57, 59-60. satırlar: N2 ve N3 sayaçları için de aynı işlem yapılır
  • 63. satır: Sunucu ile olan bağlantı sonlandırılır.
  • 65. satır: Sonuçların bir tablo biçiminde döndürülmesi.

Sonuçlar

İstemci komut dosyasının çalıştırılması, NetBeans konsolunda aşağıdaki çıktının görüntülenmesine neden olur:

HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:17 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Set-Cookie: PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=1
N2=11
N3=101
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,1,11,101]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=2
N2=12
N3=102
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,2,12,102]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=3
N2=13
N3=103
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,3,13,103]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=4
N2=14
N3=104
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,4,14,104]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=5
N2=15
N3=105
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,5,15,105]
  • 5. satır: Sunucu, ilk yanıtında oturum kimliğini gönderir. Sonraki yanıtlarda ise bunu göndermez.
  • Web sunucusunun, istemcinin istekleri boyunca (N1, N2, N3) değerlerini koruduğu açıkça görülmektedir. Buna oturum izleme denir.

Aşağıdaki iki örnek, bir dizinin veya nesnenin değerlerinin de saklanabileceğini göstermektedir.

10.4.4. Sunucu 2 (web_06)

Aşağıdaki sunucu komut dosyası, bir diziyi veya sözlüğü oturuma aktarabileceğimizi göstermektedir.


<?php

// hata yönetimi
ini_set("display_errors", "off");
// başlık UTF-8
header("Content-Type: text/plain; charset=utf-8");

// oturum açılıyor
session_start();
// bir dizi ve bir sözlük kaydediliyor
// diziyi başlatma veya değiştirme
if (isset($_SESSION['tableau'])) {
  for ($i = 0; $i < count($_SESSION['tableau']); $i++) {
    $_SESSION['tableau'][$i]++;
  }
} else {
  for ($i = 0; $i < 10; $i++) {
    $_SESSION['tableau'][$i] = $i * 10;
  }
}
// sözlüğü başlatma veya değiştirme
if (isset($_SESSION['dico'])) {
  foreach (array_keys($_SESSION['dico']) as $clé) {
    $_SESSION['dico'][$clé]++;
  }
} else {
  $_SESSION['dico'] = array("zéro" => 0, "dix" => 10, "vingt" => 20);
}
// müşteriye bilgi gönderiliyor
print "tableau=" . join(",", $_SESSION['tableau']) . "\n";
print "dico=";
foreach ($_SESSION['dico'] as $clé => $valeur) {
  print "($clé,$valeur) ";
}
print "\n";

Açıklamalar

  • 17-19. satırlar: Bir dizi, henüz oturumda yoksa başlangıçta oturuma eklenir
  • 12-15. satırlar: Zaten oturumda ise, elemanlarının değerleri 1 artırılır
  • 27. satır: Sayısal değerlere sahip bir sözlük, henüz oturumda değilse oturuma eklenir
  • 22-25. satırlar: Eğer zaten oturumda varsa, sayısal değerleri 1 artırılır
  • satır 30-35: dizi ve sözlük müşteriye gönderilir

10.4.5. İstemci 2 (client1_web_06)


<?php

// veriler
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_06.php";
// testler
$cookie = "";
for ($i = 0; $i < 5; $i++) {
  connecte($HOTE, $PORT, $urlServeur, $cookie);
}
// son
exit;

function connecte($HOTE, $PORT, $urlServeur, &$cookie) {
  // müşteriyi ($HOTE,$PORT,$urlServeur) adreslerine bağlar
  // çerez $cookie boş değilse gönderir
  // yanıt olarak alınan tüm satırları görüntüler
  // çerez, paylaşılabilmesi için referans yoluyla aktarılır
  // çağırılan program ile çağıran program arasında paylaşılmak üzere referans yoluyla aktarılır
  // $PORT bağlantı noktasında bir bağlantı açılıyor $HOTE
  $connexion = fsockopen($HOTE, $PORT);
  // hata mı?
  if (!$connexion)
    return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
  // HTTP protokolünün başlıkları (headers) boş bir satırla bitmelidir
  // GET
  fputs($connexion, "GET $urlServeur HTTP/1.1\n");
  // Ana Bilgisayar
  fputs($connexion, "Host: localhost\n");
  // Bağlantı
  fputs($connexion, "Connection: close\n");
  // Çerez boş değilse gönderilir
  if ($cookie) {
    fputs($connexion, "Cookie: $cookie\n");
  }
  // boş satır gönderilir
  fputs($connexion, "\n");
  // web sunucusunun yanıtı görüntülenir
  // ve varsa çerezi almaya özen gösterilir
  while ($ligne = fgets($connexion, 1000)) {
    print "$ligne";
    // çerez - yalnızca ilk yanıtta
    if (!$cookie) {
      if (preg_match("/^Set-Cookie: (.*?)\s*$/", $ligne, $champs)) {
        $cookie = $champs[1];
      }
    }
  }
  // bağlantıyı kapatıyoruz
  fclose($connexion);
  // geri dönüş
  return "";
}

Müşterinin kodu, daha önce açıklanan müşterinin koduna benzerdir.

Sonuçlar

HTTP/1.1 200 OK
...
Set-Cookie: PHPSESSID=6lvttr0uhpj5q3sl91h4h7p322; path=/
...

tableau=0,10,20,30,40,50,60,70,80,90
dico=(zéro,0) (dix,10) (vingt,20) 

HTTP/1.1 200 OK
...

tableau=1,11,21,31,41,51,61,71,81,91
dico=(zéro,1) (dix,11) (vingt,21) 

HTTP/1.1 200 OK
...

tableau=2,12,22,32,42,52,62,72,82,92
dico=(zéro,2) (dix,12) (vingt,22) 

HTTP/1.1 200 OK
...

tableau=3,13,23,33,43,53,63,73,83,93
dico=(zéro,3) (dix,13) (vingt,23) 

HTTP/1.1 200 OK
...

tableau=4,14,24,34,44,54,64,74,84,94
dico=(zéro,4) (dix,14) (vingt,24) 

10.4.6. Sunucu 3 (web_07)

Aşağıdaki sunucu komut dosyası, bir nesneyi oturuma alabileceğimizi göstermektedir.


<?php

// hata yönetimi
ini_set("display_errors", "off");
// UTF-8 başlığı
header("Content-Type: text/plain; charset=utf-8");

// oturum açılıyor
session_start();
// Kişi nesnesini başlatma veya değiştirme
if (isset($_SESSION['personne'])) {
  $personne = $_SESSION['personne'];
  // yaş artırılıyor
  $personne->setAge($personne->getAge() + 1);
} else {
  // kişi tanımlanıyor
  $_SESSION['personne'] = new Personne("paul", "langévin", 10);
}
// müşteriye görüntüleme
print "personne=".$_SESSION['personne']."\n";
// son
exit;

// ----------------------------------------------------------------
class Personne {

// sınıfın özellikleri
  private $prénom;
  private $nom;
  private $âge;

// alıcı ve ayarlayıcılar
  public function getPrénom() {
    return $this->prénom;
  }

  public function getNom() {
    return $this->nom;
  }

  public function getAge() {
    return $this->âge;
  }

  public function setPrénom($prénom) {
    $this->prénom = $prénom;
  }

  public function setNom($nom) {
    $this->nom = $nom;
  }

  public function setAge($age) {
    $this->âge = $age;
  }

// yapıcı
  function __construct($prénom, $nom, $âge) {
    // set yöntemleri üzerinden geçilir
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

// toString yöntemi
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

Açıklamalar

  • 17. satır: Personne türündeki bir nesne, henüz oturumda yoksa oturuma eklenir.
  • 11-15. satırlar: Eğer zaten oturumda ise, yaşı 1 artırılır
  • 20. satır: Personne türündeki nesneye istemciye gönderilir.

10.4.7. Müşteri 3 (client1_web_07)


<?php

// veriler
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_07.php";
// testler
$cookie = "";
for ($i = 0; $i < 5; $i++) {
  connecte($HOTE, $PORT, $urlServeur, $cookie);
}//if
// son
exit;

function connecte($HOTE, $PORT, $urlServeur, &$cookie) {
...
}

Açıklamalar

  • 15. satır: connecte işlevi, önceki istemci betiğindeki ile aynıdır

Sonuçlar

HTTP/1.1 200 OK
...

personne=[paul,langévin,10]

HTTP/1.1 200 OK
...

personne=[paul,langévin,11]

HTTP/1.1 200 OK
...

personne=[paul,langévin,12]

HTTP/1.1 200 OK
...

personne=[paul,langévin,13]

HTTP/1.1 200 OK
...

personne=[paul,langévin,14]