วิธีการกำหนดโปรโตคอล

การกำหนดโปรโตคอลของตัวเองนั้นมีความง่ายมากเสมอ โปรโตคอลที่ง่ายที่สุดปกติมีส่วนประกอบสองประการ:

  • สัญลักษณ์ที่ใช้แยกแยะขอบเขตของข้อมูล
  • การกำหนดรูปแบบของข้อมูล

    ตัวอย่าง

การกำหนดโปรโตคอล

ที่นี่เราสมมติว่าสัญลักษณ์ที่แยกระหว่างขอบเขตข้อมูลคือเครื่องหมายขึ้นบรรทัดใหม่ "\n" (โปรดทราบว่าข้อมูลคำขอเองไม่สามารถมีเครื่องหมายขึ้นบรรทัดในตัวเอง) และรูปแบบข้อมูลคือ Json ตัวอย่างเช่น ด้านล่างนี้คือแพ็คของคำขอที่เป็นไปตามกฎข้อบังคับดังกล่าว

{"type":"message","content":"hello"}

โปรดทราบว่าตัวข้อมูลคำขอด้านบนมีตัวแทนการขึ้นบรรทัดใหม่ที่ตามหลัง (ใน PHP ใช้ข้อความสองจุดหลักเป็น "\n") เพื่อแทนการจบคำขอ

ขั้นตอนการดำเนินการ

หากต้องการดำเนินการโปรโตคอลดังกล่าวใน Workerman โดยสมมติให้แบ่งโปรโตคอลที่กล่าวถึงว่า JsonNL และโปรเจคที่เรียกว่า MyApp จะต้องทำตามขั้นตอนต่อไปนี้

  1. วางไฟล์โปรโตคอลในโฟลเดอร์โปรเจค เช่น ไฟล์ MyApp/Protocols/JsonNL.php
  2. การดำเนินการคราวลงที่คลาส JsonNL ด้วยการใช้ namespace Protocols; จะต้องดำเนินการสามวิธีคือ input encode และ decode โปรดทราบว่า Workerman จะเรียกใช้สามวิธีครั้งคือ input encode และ decode เพื่อดำเนินการทำการแบ่งแพค แกะแพค และส่งแพค การดำเนินการโปรดดูการกำหนดขั้นตอนดังต่อไปนี้

การโต้ตอบระหว่าง Workerman และคลาสโปรโตคอล

  1. สมมติว่าไคลเอนต์ส่งแพคข้อมูลไปที่เซิร์ฟเวอร์ เมื่อเซิร์ฟเวอร์ได้รับข้อมูล (อาจเป็นข้อมูลบางส่วน) หลังจากนั้นจะมีการเรียกใช้วิธี input สำหรับการตรวจสอบความยาวของแพคนั้นๆ วิธีนี้จะคืนค่าความยาว $length ไปยังกรอบการทำงานของ Workerman
  2. Workerman ที่ได้รับค่า $length จะตรวจสอบกระจายของข้อมูลที่มีความยาวเท่ากับ $length หากไม่มีก็จะโต้ตอบได้ด้วยการรอข้อมูลต่อไปจนกว่ามีข้อมูลในกรอบการรอข้อมูลมีความยาวไม่น้อยกว่า $length
  3. เมื่อข้อมูลในกรอบมีความยาวพอจะมีการตัดข้อมูลออกจากกรอบเป็นมาตรฐานมีความยาว $length (คือการแบ่งแพค)และเรียกใช้วิธี decode ด้วยข้อมูลที่ถูกแบ่งแพคหลังเรียบร้อยแล้วเก็บไว้ในตัวแปร $data
  4. เมื่อที่ฝั่งประมวลผล ต้องการที่จะส่งข้อมูลไปที่ไคลเอนต์ด้วยการเรียกใช้ $connection->send($buffer) Workerman จะมีการใช้วิธี encode เพื่อการจัดแพค (pack) ข้อมูลในตัวแปร $buffer ก่อนจะส่งไปยังไคลเอนต์

ดำเนินการจริง

การดำเนินการของ MyApp/Protocols/JsonNL.php

namespace Protocols;
class JsonNL
{
    /**
     * ใช้สำหรับตรวจสอบความสมบูรณ์ของแพค
     * หากสามารถรับข้อมูลของแพคจะคืนค่าความยาวของแพคใน buffer มิฉะนั้นจะคืนค่า 0 เพื่อรอข้อมูลเพิ่มเติม
     * หรือจะคืนค่า false หรือค่าติดลบเมื่อข้อบกพร่องในการขอข้อมูล หากเป็นเช่นนั้นการเชื่อมต่อของไคลเอนต์จะถูกตัด
     * 
     * @param string $buffer
     * @return int
     */
    public static function input($buffer)
    {
        // รับตำแหน่งข้อความขึ้นบรรทัด "\n"
        $pos = strpos($buffer, "\n");
        // หากระหว่างไม่มีเครื่องหมายขึ้นบรรทัด ไม่สามารถรับค่าของแพค จะคืนค่า 0 เพื่อรอข้อมูลเพิ่มเติม
        if($pos === false)
        {
            return 0;
        }
        // หากมีเครื่องสายขึ้นบรรทัด จะคืนค่าความยาวปัจจุบันของแพค (รวมถึงเครื่องขึ้นบรรทัดเข้าไปด้วย)
        return $pos+1;
    }

    /**
     * การจัดแพค เมื่อต้องการส่งข้อมูลไปยังไคลเอนต์จะเรียกโดยอัตโนมัติ
     * @param string $buffer
     * @return string
     */
    public static function encode($buffer)
    {
        // การทำ json serialize และเพิ่มเครื่องหมายขึ้นบรรทัดเพื่อเป็นเครื่องหมายจบของคำขอ
        return json_encode($buffer)."\n";
    }

    /**
     * การแปลข้อมูลเมื่อความยาวของข้อมูลที่รับมาตรงกับค่าที่คืนจาก input (มีค่ามากกว่า 0) จะเรียกโดยอัตโนมัติ
     * และครั้งที่ได้รับจะถูกส่งไปที่ตัวแปร $data ในข้อความการ recall สองของ onMessage
     * @param string $buffer
     * @return string
     */
    public static function decode($buffer)
    {
        // ลบเครื่องหมายขึ้นบรรทัดก่อน แล้วคืนค่ากลับเป็นอาร์เรย์
        return json_decode(trim($buffer), true);
    }
}

จนถึงตรงนี้ โปรโตคอล JsonNL นี้จะสามารถใช้งานในโปรเจค MyApp อย่างสมบูรณ์ การใช้งานสามารถดูได้ตามตัวอย่างด้านล่าง

ไฟล์: 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 คือข้อมูลที่ไคลเอนต์ส่งมา เมื่อมีการแปลโดย JsonNL::decode แล้ว
    echo $data;

    // ข้อมูลที่ส่งทาง $connection->send จะถูกใช้งานในการจัดแพคโดย auto อีกครั้งด้วยวิธี JsonNL::encode ก่อนจะส่งไปยังไคลเอนต์
    $connection->send(array('code'=>0, 'msg'=>'ok'));

};
Worker::runAll();
...

โปรดทราบ
Workerman จะพยายามในการสั่งให้โหลดโปรโตคอลภายใต้ namespace Protocols ตัวอย่างเช่น new Worker('JsonNL://0.0.0.0:1234') จะพยายามในการสั่งให้โปรโตคอล Protocols\JsonNL
หากมีข้อผิดพลาด เช่น Class 'Protocols\JsonNL' not found โปรดอ่านแหล่งให้การโหลดอัตโนมัติ แลับขอมูลประกอบที่ (../faq/autoload.html)

ข้อควรระวัง:

Workerman ไม่บังคับให้คลาสโปรโตคอลต้องมีการimplement ProtocolInterface โดยเข้มงวด โดยทางจริงแล้วคลาสโปรโตคอลจำเป็นเพียงคลาสที่มีเมทอดสถานะสามอย่างคือ input, encode, และ decode ก็เพียงพอแล้ว