1. 程式人生 > >swoole學習筆記(五)網路通訊協議設計 -- EOF結束符協議和固定包頭+包體協議

swoole學習筆記(五)網路通訊協議設計 -- EOF結束符協議和固定包頭+包體協議

上2篇筆記講述了TCP伺服器端和TCP客戶端,既然他們之間要互相通訊,必須要制定一套通訊協議。 swoole目前支援2種通訊協議:EOF結束符協議和固定包頭+包體協議

一、EOF結束符協議

EOF協議處理的原理是每個資料包結尾加一串特殊字元表示包已結束。如memcache、ftp、stmp都使用\r\n作為結束符。傳送資料時只需要在包末尾增加\r\n即可。使用EOF協議處理,一定要確保資料包中間不會出現EOF,否則會造成分包錯誤。 回顧我們上2篇筆記的例子,修改一下程式碼: 伺服器端示例程式碼:
//建立Server物件,監聽 127.0.0.1:9501埠
$serv = new swoole_server("127.0.0.1", 9501);
$serv->set([
    'open_eof_split' => true,
    'package_eof' => "\r\n",
]);

//監聽連線進入事件
$serv->on('connect', function ($serv, $fd) {
    echo "Client: Connect.\n";
});

//監聽資料接收事件
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
    $serv->send($fd, "Server: ".$data);
});

//監聽連線關閉事件
$serv->on('close', function ($serv, $fd) {
    echo "Client: Close.\n";
});

//啟動伺服器
$serv->start();
客戶端示例程式碼:
$client = new swoole_client(SWOOLE_SOCK_TCP);
$client->set([
    'open_eof_split' => true,
    'package_eof' => "\r\n",
]);
//連線到伺服器
if (!$client->connect('127.0.0.1', 9501, 0.5))
{
    die("connect failed.");
}
//向伺服器傳送資料,注意必須帶有EOF結束符
//服務端接收不到EOF,是不會相應的,即使客戶端呼叫revc,也無法獲取結果
$client->send("hello world!");
$client->send("hello zhibin!\r\n");
//從伺服器接收資料
$data = $client->recv();
if (!$data)
{
    die("recv failed.");
}
echo $data;
//關閉連線
$client->close();
結果輸出: Server: hello world!hello zhibin!

二、固定包頭+包體協議

固定包頭的協議非常通用,在BAT的伺服器程式中經常能看到。這種協議的特點是一個數據包總是由包頭+包體2部分組成。包頭由一個欄位指定了包體或整個包的長度,長度一般是使;用2位元組/4位元組整數來表示。伺服器收到包頭後,可以根據長度值來精確控制需要再接收多少資料就是完整的資料包。Swoole的配置可以很好的支援這種協議,可以靈活地設定4項引數應對所有情況。 Swoole的Server和非同步Client都是在onReceive回撥函式中處理資料包,當設定了協議處理後,只有收到一個完整資料包時才會觸發onReceive事件。同步客戶端在設定了協議處理後,呼叫 $client->recv() 不再需要傳入長度,recv函式在收到完整資料包或發生錯誤後返回。 現在假設要傳輸一組任務資料,結構如下: {"user_id":93283,"gender":1,"user_name":"zhibin"} 伺服器端和客戶端的配置:
$serv->set([
    'open_length_check' => true,
    'package_max_length' => 81920,//該值決定了資料包快取區的大小。如果快取的資料超過了該值,則會引發錯誤。具體錯誤處理由開啟的協議解析的型別決定。
    'package_length_type' => 'N', //指定包長欄位的型別,see php pack()
    'package_length_offset' => 0,//包頭中第幾個位元組開始存放了長度欄位
    'package_body_offset' => 4,//從第幾個位元組開始計算長度。
]);
客戶端傳送資料:
$message = '{"user_id":93283,"gender":1,"user_name":"zhibin"}';
$sendMessage = pack("N", strlen($message)) . $message;
$client->send($sendMessage);

服務端接收並解析資料:
$dataLength = unpack("N", $data)[1];
$message = substr($data, -$length);