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:

  1. Protokol dosyasını projenin Protocols klasörüne koyun, örneğin dosya MyApp/Protocols/JsonNL.php

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

  1. Varsayalım ki istemci sunucuya bir veri paketi gönderir, sunucu veriyi aldıktan sonra (bu kısmi bir veri olabilir) hemen protokolün input yöntemini çağırır. Bu yöntem, bu paketin uzunluğunu kontrol etmek için kullanılır ve input yöntemi, workerman çerçevesine bir uzunluk değeri $length döndürür.
  2. Workerman çerçevesi bu $length değerini aldıktan sonra, mevcut veri tamponunda $length uzunluğ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.
  3. Veri tamponu yeterince uzun olduğunda, workerman, tampondan $length uzunluğundaki veriyi keser (yani parçalar) ve protokolün decode yöntemini çağırarak çözer, çözülen veri $data olacaktır.
  4. Verinin çözülmesi sonrasında workerman, veriyi $data değişkeni ile onMessage($connection, $data) geri çağırma biçiminde iş mantığına iletecektir. İş mantığı, onMessage içinde $data değişkenini kullanarak istemciden gelen tam ve çözülmüş veriyi alabilir.
  5. onMessage içerisinde iş mantığı, istemciye veri göndermek için $connection->send($buffer) yöntemini çağırdığında, workerman otomatik olarak protokolün encode yöntemini kullanarak $bufferpaketler 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, Protocols ad alanındaki protokolleri yüklemeye çalışacaktır, örneğin new Worker('JsonNL://0.0.0.0:1234') Protocols\JsonNL protokolünü yüklemeye çalışacaktır.
Eğer Class 'Protocols\JsonNL' not found hatası 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.