swoole學習筆記(五)網路通訊協議設計 -- EOF結束符協議和固定包頭+包體協議
阿新 • • 發佈:2019-02-12
上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();
結果輸出: Server: hello world!hello zhibin!$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();
二、固定包頭+包體協議
固定包頭的協議非常通用,在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);