İçeriğe geçmek için "Enter"a basın

PHP ile TCP Socket Server Uygulaması

Son güncelleme tarihi 10 Nisan 2022

PHP kullanarak socket uygulamasına ihtiyaç duyan arkadaşlar buraya,

Socket ne ki?” diyen arkadaşlar için ise yazılarım arasında, Linux İşletim sistemi, Sistem Servisleri, Network, TCP, UDP, Client-Server İlişkisi, netcat uygulaması, Yazılım Geliştirme gibi kavramlar için tanım, açıklama, standardizasyon, geliştirme, kullanım senaryoları gibi makaleler mevcut olmalı, eğer yoksa merak etmeyin yazım aşamasındadır.
Burada yukarıda sıraladığım kavramları temel olarak bildiğinizi düşünerek bu yazıyı hazırlıyorum.

Hadi Başalayalım!

PHP ile TCP Socket oluşturmak için socket_create() işlevini kullanabiliriz. Bu işlev ile birlikte Socket İşlevlerinin tam listesine buradan ulaşabilirsiniz.

İlk olarak socket uygulamamızın çalışacağı bir ip ve port ikilisini değişken olarak tanımlayalım, uygulamamız belirtilen ip adresi ve port üzerinden dinleme gerçekleştirecek.

<?php
$address = "127.0.0.1"; // localhost
$port = 1984;

socket_create() işlevi ile ip:port üzerinde dinlemeye başlamak için öncelikle socket oluşturma işlemini yapmalıyız ve bunu yukarıda da belirtildiği gibi socket_create() işlevi ile gerçekleştiriyoruz,

$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

$sock değişkenimize yeni oluşturulmuş bir stream olan TCP protokolünde çalışan bir IPv4 destekli socket tanımlamış olduk, eğer IPv6 için socket oluşturuyorsak AF_INET6 sabitiyle değişkenimizi tanımlamamız gerekiyor bunula birlikte ip adresimizi de güncellemeliyiz, burada IPv4 için socket oluşturma örneğini işleyeceğiz.

Socket oluşturma sırasında bir hata ile karşılaşacak olursak diye değişken tanımlama işlemini koşullandıralım,

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error());
}

Yeni karşılaştığımız iki işlev socket_strerror() ve socket_last_error() işlevleri, eğer socket oluşturma sırasında bir hata oluşursa bize bu hatanın neden kaynaklandığını bildirecektir.

Mesela port kullanımda gibi bir uyarıyla karşılaşırsak uygulamamız için tanımladığımız port o sırada başka bir uygulama tarafından kullanılıyor demektir. Kullanım şeklinden göreceğiniz üzere (linkdeki döküman sayfasında da mevcut) socket_strerror() işlevi parametre olarak socket_last_error() işlevini kullanmakta, socket_last_error() bize hata kodunu sayısal olarak döndürmekte, socket_strerror() ise bu sayısal kodun karşılığı olan anlaşılır metni vermekte. Örneğin:

Socket oluşturma işlemini tamamladık ancak çalıştırabilmemiz için birkaç adım daha var, şimdi bind işlemini gerçekleştireceğiz,

if (!socket_bind($sock, $address, $port)) {
    echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock));
}

socket_bind() işlevi ile oluşturduğumuz socket belirlediğimiz ip:port ikilisi ile birlikte çalışacak şekilde sisteme bildirilmiş oluyor, koşullu olarak işlevi çalıştırdığımız için herhangi bir hata durumunda kod bize gerekli hatayı döndürecektir. Sisteme bağladığımız socket çalışmaya başlamak üzere, dinleme işlemine başlamak için,

if (socket_listen($sock, 5) === false) {
	echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock));
}

socket_listen() komutunu kullanıyoruz, bu komut ile artık socket hazır, sisteme eklenmiş, ve dinleme işlemini gerçekleştirecektir. Server uygulama geliştirme noktasında yazdığımız uygulamanın sürekli olarak bu işi yapması genel olarak kullanılan yöntemdir, bir defa çalıştıktan sonra eğer bir döngü içerisinde değilse kod sonlanacağından buraya kadar yaptığımız işlem tek seferlik çalışan bir kod parçası olmuş oluyor. Bir PHP kodunu sürekli çalışır halde tutabilmek için kullanılan en basit yöntem while ile sonsuz döngü oluşturmaktır, biz de bunu kullanacağız.

while (true) {
	// socket tarafına gelen talepleri işleyen işlemler
}

Sonsuz döngümüz de hazır, bu döngü içerisinde yapacağımız temel işlem aktif sockete gelen bilginin işlenmesi üzerine, yeni tanışacağımız işlev socket_accept() bu işlev aktif sockete gelen bağlantıyı kabul eder.

while (true) {
	// socket tarafına gelen talepleri kabul et
  if (($msgsock = socket_accept($sock)) === false) {
  	echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock));
    break;
  }
  
  // bağlantı gelince okuyabilmemiz için gereken işlemler
}

Bağlantı geldiği zaman gelen mesajı bir değişkende tutmamız gerekiyor, bunu yapabilmek için socket_read() komutu ile gelen bağlantıdaki mesajı değişkene aktaracağız,

while (true) {
	// socket tarafına gelen talepleri kabul et
  if (($msgsock = socket_accept($sock)) === false) {
  	echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock));
    break;
  }
  
  // mesajı değişkene al
  if (false === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
    echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock));
    break;
  }
  // gelen mesajı ekrana yazdıralım
  echo "Gelen mesaj : " . $buf . "\n";
}

Bu aşamada göstermek istediğim şey uygulamamız halen daha tek seferlik çalışmakta, socket doğru şekilde sistem üzerinde bağlantı kabul ediyor ancak birden fazla mesaj alamıyoruz,

Ekran görüntüsünde görüldüğü gibi 2. mesaj ekrana yazdırılamadı, bunun temel sebebi uygulama sürekli bağlantı kabul etmesine rağmen okuma işlemini tek sefer yapmakta, ikinci bir while döngüsü ile sorunumuzu çözelim.

 while (true) {
	// socket tarafına gelen talepleri kabul et
  if (($msgsock = socket_accept($sock)) === false) {
  	echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock));
    break;
  }

  while (true) {
    // mesajı değişkene al
    if (false === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
      echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock));
      break 2;
    }
    
    // gelen mesajı ekrana yazdıralım
    echo "Gelen mesaj : " . $buf . "\n";
  }
}

Son düzeltme ile birlikte ufak bir ayrıntıya dikkat çekmeliyim, 2. while döngümüz içerisinde kalan break işlevi yanında 2 rakamıyla kullanıldı, bunun sebebi 2 level yukarıya doğru çalışmayı durdur, yani bağlantıyı kapatıp uygulamadan çık anlamına geliyor.

Uygulamanın tam hali:

<?php
$address = "127.0.0.1"; // localhost
$port = 1984;

$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error());
}

if (!socket_bind($sock, $address, $port)) {
    echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock));
}

if (socket_listen($sock, 5) === false) {
    echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock));
}

while (true) {
        // socket tarafına gelen talepleri kabul et
  if (($msgsock = socket_accept($sock)) === false) {
    echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock));
    break;
  }

  while (true) {
    // mesajı değişkene al
    if (false === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
      echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock));
      break 2;
    }

    // gelen mesajı ekrana yazdıralım
    echo "Gelen mesaj : " . $buf . "\n";
  }
}

Socket kavramı ve detaylarını incelemek için aşağıdaki yazıyı okuyabilirsiniz.

Bir yorum

Yorumlar kapatıldı ancak, geri izlemeler ve pingback'ler açık.