วิธีการกำหนดโปรโตคอล
การกำหนดโปรโตคอลของตัวเองนั้นมีความง่ายมากเสมอ โปรโตคอลที่ง่ายที่สุดปกติมีส่วนประกอบสองประการ:
- สัญลักษณ์ที่ใช้แยกแยะขอบเขตของข้อมูล
- การกำหนดรูปแบบของข้อมูล
ตัวอย่าง
การกำหนดโปรโตคอล
ที่นี่เราสมมติว่าสัญลักษณ์ที่แยกระหว่างขอบเขตข้อมูลคือเครื่องหมายขึ้นบรรทัดใหม่ "\n" (โปรดทราบว่าข้อมูลคำขอเองไม่สามารถมีเครื่องหมายขึ้นบรรทัดในตัวเอง) และรูปแบบข้อมูลคือ Json ตัวอย่างเช่น ด้านล่างนี้คือแพ็คของคำขอที่เป็นไปตามกฎข้อบังคับดังกล่าว
{"type":"message","content":"hello"}
โปรดทราบว่าตัวข้อมูลคำขอด้านบนมีตัวแทนการขึ้นบรรทัดใหม่ที่ตามหลัง (ใน PHP ใช้ข้อความสองจุดหลักเป็น "\n") เพื่อแทนการจบคำขอ
ขั้นตอนการดำเนินการ
หากต้องการดำเนินการโปรโตคอลดังกล่าวใน Workerman โดยสมมติให้แบ่งโปรโตคอลที่กล่าวถึงว่า JsonNL และโปรเจคที่เรียกว่า MyApp จะต้องทำตามขั้นตอนต่อไปนี้
- วางไฟล์โปรโตคอลในโฟลเดอร์โปรเจค เช่น ไฟล์ MyApp/Protocols/JsonNL.php
- การดำเนินการคราวลงที่คลาส JsonNL ด้วยการใช้
namespace Protocols;
จะต้องดำเนินการสามวิธีคือ input encode และ decode โปรดทราบว่า Workerman จะเรียกใช้สามวิธีครั้งคือ input encode และ decode เพื่อดำเนินการทำการแบ่งแพค แกะแพค และส่งแพค การดำเนินการโปรดดูการกำหนดขั้นตอนดังต่อไปนี้
การโต้ตอบระหว่าง Workerman และคลาสโปรโตคอล
- สมมติว่าไคลเอนต์ส่งแพคข้อมูลไปที่เซิร์ฟเวอร์ เมื่อเซิร์ฟเวอร์ได้รับข้อมูล (อาจเป็นข้อมูลบางส่วน) หลังจากนั้นจะมีการเรียกใช้วิธี
input
สำหรับการตรวจสอบความยาวของแพคนั้นๆ วิธีนี้จะคืนค่าความยาว$length
ไปยังกรอบการทำงานของ Workerman - Workerman ที่ได้รับค่า
$length
จะตรวจสอบกระจายของข้อมูลที่มีความยาวเท่ากับ$length
หากไม่มีก็จะโต้ตอบได้ด้วยการรอข้อมูลต่อไปจนกว่ามีข้อมูลในกรอบการรอข้อมูลมีความยาวไม่น้อยกว่า$length
- เมื่อข้อมูลในกรอบมีความยาวพอจะมีการตัดข้อมูลออกจากกรอบเป็นมาตรฐานมีความยาว
$length
(คือการแบ่งแพค)และเรียกใช้วิธีdecode
ด้วยข้อมูลที่ถูกแบ่งแพคหลังเรียบร้อยแล้วเก็บไว้ในตัวแปร$data
- เมื่อที่ฝั่งประมวลผล ต้องการที่จะส่งข้อมูลไปที่ไคลเอนต์ด้วยการเรียกใช้
$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 จะพยายามในการสั่งให้โหลดโปรโตคอลภายใต้ namespaceProtocols
ตัวอย่างเช่นnew Worker('JsonNL://0.0.0.0:1234')
จะพยายามในการสั่งให้โปรโตคอลProtocols\JsonNL
หากมีข้อผิดพลาด เช่นClass 'Protocols\JsonNL' not found
โปรดอ่านแหล่งให้การโหลดอัตโนมัติ แลับขอมูลประกอบที่ (../faq/autoload.html)ข้อควรระวัง:
Workerman ไม่บังคับให้คลาสโปรโตคอลต้องมีการimplement ProtocolInterface โดยเข้มงวด โดยทางจริงแล้วคลาสโปรโตคอลจำเป็นเพียงคลาสที่มีเมทอดสถานะสามอย่างคือ input, encode, และ decode ก็เพียงพอแล้ว