Protokolü Özelleştirme
Aslında kendi protokolünüzü oluşturmak oldukça basit bir şeydir. Basit protokoller genellikle iki bölümden oluşur:
- Veri sınırını tanımlayan bir işaret
- Veri formatı tanımı
Bir Örnek
Protokol Tanımı
Burada veri sınırını tanımlayan işareti yeni bir satır karakteri "\n" olarak varsayıyoruz (dikkat: istek verisi içinde yeni bir satır karakteri bulunmamalıdır), veri formatı ise Json'dur. Aşağıda bu kurallara uyan bir istek paketi örneği verilmiştir.
{"type":"message","content":"hello"}
Yukarıdaki istek verisinin sonunda bir yeni bir satır karakteri bulunmaktadır (PHP'de çift tırnak dizesi "\n" ile temsil edilir), bu da bir isteğin sonunu temsil eder.
Uygulama Adımları
Workerman'da yukarıdaki protokolü uygulamak için, protokole JsonNL adını verdiğimizi varsayalım ve proje MyApp olsun, şu adımları izlememiz gerekir:
-
Protokol dosyasını projenin Protocols klasörüne koyun, örneğin dosya MyApp/Protocols/JsonNL.php
-
JsonNL sınıfını oluşturun ve
namespace Protocols;ile ad alanını belirleyin. Aşağıdaki üç statik yöntemi uygulamanız gerekir: input, encode, decode.
Dikkat: workerman, parçalama, çözme ve paketleme işlemlerini gerçekleştirmek için bu üç statik yöntemi otomatik olarak çağırır. Ayrıntılı işlem için aşağıdaki iş akışı açıklamasına bakabilirsiniz.
Workerman ve Protokol Sınıfı Arasındaki Etkileşim Süreci
- Varsayalım ki istemci sunucuya bir veri paketi gönderir, sunucu veriyi aldıktan sonra (bu kısmi bir veri olabilir) hemen protokolün
inputyöntemini çağırır. Bu yöntem, bu paketin uzunluğunu kontrol etmek için kullanılır veinputyöntemi, workerman çerçevesine bir uzunluk değeri$lengthdöndürür. - Workerman çerçevesi bu
$lengthdeğerini aldıktan sonra, mevcut veri tamponunda$lengthuzunluğundaki verinin alınıp alınmadığını kontrol eder. Eğer alınmadıysa, veri uzunluğu$length'in altına düşene kadar veri beklemeye devam edecektir. - Veri tamponu yeterince uzun olduğunda, workerman, tampondan
$lengthuzunluğundaki veriyi keser (yani parçalar) ve protokolündecodeyöntemini çağırarak çözer, çözülen veri$dataolacaktır. - Verinin çözülmesi sonrasında workerman, veriyi
$datadeğişkeni ileonMessage($connection, $data)geri çağırma biçiminde iş mantığına iletecektir. İş mantığı, onMessage içinde$datadeğişkenini kullanarak istemciden gelen tam ve çözülmüş veriyi alabilir. onMessageiçerisinde iş mantığı, istemciye veri göndermek için$connection->send($buffer)yöntemini çağırdığında, workerman otomatik olarak protokolünencodeyöntemini kullanarak$buffer'ı paketler ve ardından istemciye gönderir.
Ayrıntılı Uygulama
MyApp/Protocols/JsonNL.php Uygulaması
namespace Protocols;
class JsonNL
{
/**
* Paketin bütünlüğünü kontrol eder
* Eğer paket uzunluğunu elde edebiliyorsak, buffer'daki uzunluğunu döndürür, aksi takdirde 0 döndürerek veri almaya devam eder
* Eğer protokolde bir sorun varsa, -1 döndürebilir, bu durumda mevcut istemci bağlantısı kesilecektir
* @param string $buffer
* @return int
*/
public static function input($buffer)
{
// Yeni satır karakteri "\n" pozisyonunu al
$pos = strpos($buffer, "\n");
// Yeni satır karakteri yoksa, paket uzunluğunu bilemeyiz, 0 döndür ve veri beklemeye devam et
if($pos === false)
{
return 0;
}
// Yeni satır karakteri varsa, mevcut paket uzunluğunu döndür (yeni satır karakterini dahil edin)
return $pos+1;
}
/**
* Paketler, istemciye veri göndermek için otomatik olarak çağrılır
* @param string $buffer
* @return string
*/
public static function encode($buffer)
{
// Json seri hale getir ve istek sonunu belirten yeni bir satır karakteri ekle
return json_encode($buffer)."\n";
}
/**
* Çözer, input tarafından döndürülen byte sayısı (0'dan büyük bir değer) ile eşit olduğunda otomatik olarak çağrılır
* ve onMessage geri çağırma fonksiyonundaki $data parametresine iletilir
* @param string $buffer
* @return string
*/
public static function decode($buffer)
{
// Yeni satırdan çıkar, dizi olarak geri döndür
return json_decode(trim($buffer), true);
}
}
Böylece JsonNL protokolü tamamlandı, MyApp projesinde kullanılabilir; kullanım şekli aşağıda verilmiştir.
Dosya: MyApp\start.php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$json_worker = new Worker('JsonNL://0.0.0.0:1234');
$json_worker->onMessage = function(TcpConnection $connection, $data) {
// $data, istemciden gelen veridir, veri JsonNL::decode ile işlenmiştir
echo $data;
// $connection->send ile gönderilen veriler otomatik olarak JsonNL::encode ile paketlenir ve istemciye iletilir
$connection->send(array('code'=>0, 'msg'=>'ok'));
};
Worker::runAll();
...
İpucu
Workerman,Protocolsad alanındaki protokolleri yüklemeye çalışacaktır, örneğinnew Worker('JsonNL://0.0.0.0:1234')Protocols\JsonNLprotokolünü yüklemeye çalışacaktır.
EğerClass 'Protocols\JsonNL' not foundhatası alırsanız, lütfen otomatik yükleme ile ilgili bilgileri inceleyin.
Protokol Arayüzü Açıklaması
Workerman'da geliştirilen protokol sınıfları üç statik yöntemi, input, encode, decode uygulamak zorundadır. Protokol arayüzü açıklaması Workerman/Protocols/ProtocolInterface.php içinde tanımlanmıştır:
namespace Workerman\Protocols;
use \Workerman\Connection\ConnectionInterface;
/**
* Protokol arayüzü
* @author walkor <walkor@workerman.net>
*/
interface ProtocolInterface
{
/**
* Alınan recv_buffer içinde parçalamak için kullanılır
*
* Eğer $recv_buffer'da istek paketinin uzunluğunu bulabiliyorsanız, toplam paketin uzunluğunu döndürün
* Eğer dönerse 0, mevcut istek paketinin uzunluğunu almak için daha fazla veriye ihtiyaç var demektir
* Eğer -1 dönerse, bu bir yanlış isteği temsil eder, bağlantı kesilecektir
*
* @param ConnectionInterface $connection
* @param string $recv_buffer
* @return int
*/
public static function input($recv_buffer, ConnectionInterface $connection);
/**
* İstek çözümlemek için kullanılır
*
* input döndürülen değer 0'dan büyükse ve Workerman yeterli veriyi aldıysa, otomatik olarak decode çağrılır
* Ardından onMessage geri çağrısına tetiklenir ve decode edilen veriler onMessage geri çağrısının ikinci parametresine aktarılır
* Yani tamamlanmış istemci isteği alındığında decode otomatik olarak çağrılır, iş kodunda manuel çağırmaya gerek yoktur
* @param ConnectionInterface $connection
* @param string $recv_buffer
*/
public static function decode($recv_buffer, ConnectionInterface $connection);
/**
* İstek paketlemek için kullanılır
*
* İstemciye veri göndermek gerektiğinde yani $connection->send($data); çağrıldığında
* $data otomatik olarak encode ile paketlenir, protokole uygun veri formatında iletilir ve ardından istemciye gönderilir
* Yani istemciye gönderilen veriler otomatik olarak encode ile paketlenecektir, iş kodunda manuel çağırmaya gerek yoktur
* @param ConnectionInterface $connection
* @param mixed $data
*/
public static function encode($data, ConnectionInterface $connection);
}
Dikkat:
Workerman'da protokol sınıfının ProtocolInterface'i uygulaması kesinlikle zorunlu değildir, aslında protokol sınıfı, yalnızca input, encode ve decode üç statik yöntemini içermesi yeterlidir.