วิธีปรับแต่งโปรโตคอล
จริงๆ แล้วการสร้างโปรโตคอลของตัวเองเป็นสิ่งที่ค่อนข้างง่าย โปรโตคอลที่ง่ายมักประกอบด้วยสองส่วน:
- ตัวระบุที่แยกขอบเขตข้อมูล
- การกำหนดรูปแบบข้อมูล
ตัวอย่างหนึ่ง
การระบุโปรโตคอล
ที่นี่สมมติว่าตัวระบุที่แยกขอบเขตข้อมูลคืออักขระขึ้นบรรทัดใหม่ "\n" (โปรดทราบว่าข้อมูลในคำร้องไม่สามารถมีอักขระขึ้นบรรทัดใหม่ภายในได้) และรูปแบบข้อมูลคือ Json เช่น ต่อไปนี้คือตัวอย่างของแพ็กเกจคำร้องที่เป็นไปตามกฎนี้
{"type":"message","content":"hello"}
โปรดทราบว่าจุดสิ้นสุดของข้อมูลคำร้องข้างต้นมีอักขระขึ้นบรรทัดใหม่ (ใน PHP ใช้ สตริงในเครื่องหมายคำพูดคู่ "\n" แทน) หมายถึงการสิ้นสุดของคำร้อง
ขั้นตอนการดำเนินการ
ใน Workerman หากต้องการดำเนินการตามโปรโตคอลข้างต้น สมมติว่าชื่อโปรโตคอลคือ JsonNL และโปรเจ็กต์คือ MyApp จึงต้องดำเนินการตามขั้นตอนดังต่อไปนี้
-
ไฟล์โปรโตคอลควรอยู่ในโฟลเดอร์ Protocols ของโปรเจ็กต์ เช่น ไฟล์ MyApp/Protocols/JsonNL.php
-
สร้างคลาส JsonNL โดยใช้
namespace Protocols;เป็น namespace และต้องสร้างสาม static methods ได้แก่ input, encode, decode
โปรดทราบ: Workerman จะเรียกใช้สาม static methods นี้โดยอัตโนมัติ เพื่อทำการแยกแพ็กเกจ แพ็กเกจ และการบีบอัด รายละเอียดกระบวนการดูจากคำอธิบายด้านล่าง
ขั้นตอนการโต้ตอบระหว่าง Workerman และคลาสโปรโตคอล
- สมมติว่าลูกค้าได้ส่งแพ็กเกจข้อมูลไปยังเซิร์ฟเวอร์ เมื่อเซิร์ฟเวอร์ได้รับข้อมูล (อาจเป็นข้อมูลบางส่วน) จะแม่นยำจะเรียกใช้ method
inputของโปรโตคอลเพื่อทำการตรวจสอบความยาวของแพ็กเกจ โดย methodinputจะส่งค่าความยาว$lengthกลับไปยัง framework Workerman - framework Workerman จะตรวจสอบว่าค่าความยาว
$lengthได้รับภายใน buffer หรือไม่ หากยังไม่ได้รับ จะรอสัญญาณข้อมูลต่อไปจนกว่าความยาวข้อมูลใน buffer จะไม่น้อยกว่า$length - เมื่อความยาวข้อมูลใน buffer เพียงพอแล้ว Workerman จะตัดข้อมูลช่วงความยาว
$lengthออกจาก buffer (ซึ่งเรียกว่า การแยกแพ็กเกจ) และจะเรียกใช้ methoddecodeของโปรโตคอลเพื่อทำการ บีบอัด ข้อมูลที่บีบอัดแล้วจะเป็น$data - หลังจากบีบอัดแล้ว Workerman จะส่งข้อมูล
$dataให้กับธุรกิจอย่างฟังก์ชัน callbackonMessage($connection, $data)ซึ่งธุรกิจสามารถใช้ตัวแปร$dataใน onMessage เพื่อจัดการข้อมูลที่ส่งมาจากลูกค้าและได้ข้อมูลที่ครบถ้วนและทำการบีบอัดแล้ว - เมื่อธุรกิจใน
onMessageเรียกใช้ method$connection->send($buffer)เพื่อส่งข้อมูลกลับไปยังลูกค้า Workerman จะเรียกใช้ methodencodeของโปรโตคอลโดยอัตโนมัติเพื่อทำการบีบอัด$bufferก่อนส่งกลับไปยังลูกค้า
การดำเนินการเฉพาะ
การดำเนินการ MyApp/Protocols/JsonNL.php
namespace Protocols;
class JsonNL
{
/**
* ตรวจสอบความสมบูรณ์ของแพ็กเกจ
* หากสามารถได้รับความยาวของแพ็กเกจ จะส่งกลับความยาวของแพ็กเกจใน buffer มิฉะนั้นจะส่งกลับ 0 เพื่อรอสัญญาณข้อมูลต่อไป
* หากโปรโตคอลมีปัญหาจะสามารถส่งกลับ -1 และการเชื่อมต่อของลูกค้าจะถูกตัดขาด
* @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 serialization และเพิ่มอักขระขึ้นบรรทัดใหม่เป็นสัญลักษณ์ของการสิ้นสุดคำร้อง
return json_encode($buffer)."\n";
}
/**
* บีบอัด เมื่อจำนวนไบต์ที่ได้รับเท่ากับค่าที่ส่งกลับจาก input (ค่ามากกว่า 0) จะเรียกใช้โดยอัตโนมัติ
* และส่งไปยังพารามิเตอร์ $data ของฟังก์ชัน callback 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 จะเรียกใช้ method JsonNL::encode โดยอัตโนมัติเพื่อบีบอัดแล้วส่งไปยังลูกค้า
$connection->send(array('code'=>0, 'msg'=>'ok'));
};
Worker::runAll();
...
คำแนะนำ
Workerman จะพยายามโหลดโปรโตคอลภายใต้ namespaceProtocolsเช่นnew Worker('JsonNL://0.0.0.0:1234')จะพยายามโหลดโปรโตคอลProtocols\JsonNL
หากเกิดข้อผิดพลาดClass 'Protocols\JsonNL' not foundโปรดดูที่ การโหลดอัตโนมัติ
คำอธิบายอินเทอร์เฟซโปรโตคอล
คลาสโปรโตคอลที่พัฒนาใน Workerman ต้องมีการ implement สาม static methods ได้แก่ input, encode, decode คำอธิบายอินเทอร์เฟซโปรโตคอลดูได้ที่ Workerman/Protocols/ProtocolInterface.php โดยระบุไว้ดังนี้:
namespace Workerman\Protocols;
use \Workerman\Connection\ConnectionInterface;
/**
* โปรโตคอลอินเทอร์เฟซ
* @author walkor <walkor@workerman.net>
*/
interface ProtocolInterface
{
/**
* ใช้ในการแยกแพ็กเกจใน recv_buffer ที่ได้รับ
*
* หากสามารถทราบความยาวของแพ็กเกจใน $recv_buffer จะส่งกลับความยาวทั้งหมดของแพ็กเกจ
* หากไม่เช่นนั้นจะส่งกลับ 0 หมายความว่าต้องการข้อมูลเพิ่มเติมเพื่อทราบความยาวแพ็กเกจ
* หากส่งกลับ -1 จะหมายถึงคำร้องที่ไม่ถูกต้อง ซึ่งจะทำให้การเชื่อมต่อถูกตัดออก
*
* @param ConnectionInterface $connection
* @param string $recv_buffer
* @return int
*/
public static function input($recv_buffer, ConnectionInterface $connection);
/**
* ใช้ในการบีบอัดคำร้อง
*
* เมื่อค่าที่ส่งกลับจาก input มากกว่า 0 และ Workerman ได้รับข้อมูลมากพอแล้ว จะเรียกใช้ decode โดยอัตโนมัติ
* จากนั้นจะทำการเรียกใช้ callback onMessage และจะส่งข้อมูลที่ถูก decode ไปยังพารามิเตอร์ที่สองของฟังก์ชัน callback onMessage
* กล่าวคือเมื่อได้รับคำร้องจากลูกค้าอย่างครบถ้วน จะเรียกใช้ decode โดยอัตโนมัติ ไม่ต้องเรียกใช้ด้วยตนเองในโค้ดทางธุรกิจ
* @param ConnectionInterface $connection
* @param string $recv_buffer
*/
public static function decode($recv_buffer, ConnectionInterface $connection);
/**
* ใช้ในการบีบอัดคำร้อง
*
* เมื่อจำเป็นต้องส่งข้อมูลไปยังลูกค้าโดยการเรียกใช้ $connection->send($data);
* จะทำการบีบอัด $data โดยใช้ encode one ครั้งหนึ่ง ทำให้เป็นรูปแบบข้อมูลตามโปรโตคอล และส่งไปยังลูกค้า
* กล่าวคือ ข้อมูลที่ส่งไปยังลูกค้าจะบีบอัดด้วย encode โดยอัตโนมัติ ไม่ต้องเรียกใช้ด้วยตนเองในโค้ดทางธุรกิจ
* @param ConnectionInterface $connection
* @param mixed $data
*/
public static function encode($data, ConnectionInterface $connection);
}
หมายเหตุ:
ใน Workerman ไม่มีข้อกำหนดอย่างเข้มงวดว่าคลาสโปรโตคอลจะต้อง implement ProtocolInterface จริงๆ แล้ว คลาสโปรโตคอลเพียงมี static methods input, encode, decode ทั้งสามเท่านั้น