# 定制通訊協議
## 如何定制協議
實際上制定自己的協議是比較簡單的事情。簡單的協議一般包含兩部分:
- 區分數據邊界的標識
- 數據格式定義
## 一個例子
### 協議定義
這里假設區分數據邊界的標識為換行符"\\n"(注意請求數據本身內部不能包含換行符),數據格式為Json,例如下面是一個符合這個規則的請求包。
```
{"type":"message","content":"hello"}
```
注意上面的請求數據末尾有一個換行字符(在PHP中用**雙引號**字符串"\\n"表示),代表一個請求的結束。
### 實現步驟
在WorkerMan中如果要實現上面的協議,假設協議的名字叫JsonNL,所在項目為MyApp,則需要以下步驟
1、協議文件放到項目的Protocols文件夾,例如文件MyApp/Protocols/JsonNL.php
2、實現JsonNL類,以```namespace Protocols;```為命名空間,必須實現三個靜態方法分別為 input、encode、decode
### 具體實現
**MyApp/Protocols/JsonNL.php的實現**
```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序列化,并加上換行符作為請求結束的標記
return json_encode($buffer)."\n";
}
/**
* 解包,當接收到的數據字節數等于input返回的值(大于0的值)自動調用
* 并傳遞給onMessage回調函數的$data參數
* @param string $buffer
* @return string
*/
public static function decode($buffer)
{
// 去掉換行,還原成數組
return json_decode(trim($buffer), true);
}
}
```
至此,JsonNL協議實現完畢,可以在MyApp項目中使用,使用方法例如下面
文件:MyApp\\start.php
```php
use Workerman\Worker;
require_once '/your/path/Workerman/Autoloader.php'
$json_worker = new Worker('JsonNL://0.0.0.0:1234');
$json_worker->onMessage = ...
...
```
### 協議接口說明
在WorkerMan中開發的協議類必須實現三個靜態方法,input、encode、decode,協議接口說明見Workerman/Protocols/ProtocolInterface.php,定義如下:
```php
namespace Workerman\Protocols;
use \Workerman\Connection\ConnectionInterface;
/**
* Protocol interface
* @author walkor <walkor@workerman.net>
*/
interface ProtocolInterface
{
/**
* 用于在接收到的recv_buffer中分包
*
* 如果可以在$recv_buffer中得到請求包的長度則返回整個包的長度
* 否則返回0,表示需要更多的數據才能得到當前請求包的長度
* 如果返回false或者負數,則代表錯誤的請求,則連接會斷開
*
* @param ConnectionInterface $connection
* @param string $recv_buffer
* @return int|false
*/
public static function input($recv_buffer, ConnectionInterface $connection);
/**
* 用于請求解包
*
* input返回值大于0,并且WorkerMan收到了足夠的數據,則自動調用decode
* 然后觸發onMessage回調,并將decode解碼后的數據傳遞給onMessage回調的第二個參數
* 也就是說當收到完整的客戶端請求時,會自動調用decode解碼,無需業務代碼中手動調用
* @param ConnectionInterface $connection
* @param string $recv_buffer
*/
public static function decode($recv_buffer, ConnectionInterface $connection);
/**
* 用于請求打包
*
* 當需要向客戶端發送數據即調用$connection->send($data);時
* 會自動把$data用encode打包一次,變成符合協議的數據格式,然后再發送給客戶端
* 也就是說發送給客戶端的數據會自動encode打包,無需業務代碼中手動調用
* @param ConnectionInterface $connection
* @param mixed $data
*/
public static function encode($data, ConnectionInterface $connection);
}
```
## 注意:
Workerman中沒有嚴格要求協議類必須基于ProtocolInterface實現,實際上協議類只要類包含了input、encode、decode三個靜態方法即可。
- 序言
- 原理
- 開發必讀
- 入門指引
- 特性
- 簡單的開發示例
- 安裝
- 環境要求
- 下載安裝
- 啟動停止
- 開發流程
- 開發前必讀
- 目錄結構
- 開發規范
- 基本流程
- 通訊協議
- 通訊協議作用
- 定制通訊協議
- 一些例子
- Worker類
- 構造函數
- 屬性
- id
- count
- name
- protocol
- transport
- reusePort
- connections
- stdoutFile
- pidFile
- logFile
- user
- reloadable
- daemonize
- globalEvent
- 回調屬性
- onWorkerStart
- onWorkerReload
- onConnect
- onMessage
- onClose
- onBufferFull
- onBufferDrain
- onError
- 接口
- runAll
- stopAll
- listen
- TcpConnection類
- 屬性
- id
- protocol
- worker
- maxSendBufferSize
- defaultMaxSendBufferSize
- maxPackageSize
- 回調屬性
- onMessage
- onClose
- onBufferFull
- onBufferDrain
- onError
- 接口
- send
- getRemoteIp
- getRemotePort
- close
- destroy
- pauseRecv
- resumeRecv
- pipe
- AsyncTcpConnection類
- 構造函數
- connect
- reconnect
- transport
- Timer定時器類
- add
- del
- 定時器注意事項
- WebServer
- 調試
- 基本調試
- 查看運行狀態
- 網絡抓包
- 跟蹤系統調用
- 常用組件
- GlobalData數據共享組件
- GlobalDataServer
- GlobalDataClient
- Channel分布式通訊組件
- ChannelServer
- channelClient
- 例子-集群推送
- 例子-分組發送
- FileMonitor文件監控組件
- MySQL組件
- workerman/mysql
- swoole/mysql(異步)
- redis組件
- swoole/redis
- 異步http組件
- swoole/http-client
- 異步消息隊列組件
- react/zmq
- react/stomp
- 異步dns組件
- swoole/dns
- 常見問題
- 心跳
- 客戶端鏈接失敗原因
- 是否支持多線程
- 與其它框架整合
- 運行多個workerman
- 支持哪些協議
- 如何設置進程數
- 查看客戶端連接數
- 對象和資源的持久化
- 例子無法工作
- 啟動失敗
- 停止失敗
- 支持多少并發
- 更改代碼不生效
- 向指定客戶端發送數據
- 如何主動推送消息
- 在其它項目中推送
- 如何實現異步任務
- status里send_fail的原因
- Windows下開發Linux下部署
- 是否支持socket.io
- 終端關閉導致workerman關閉
- 與nginx apache的關系
- 禁用函數檢查
- 平滑重啟原理
- 為Flash開843端口
- 如何廣播數據
- 如何建立udp服務
- 監聽ipv6
- 關閉未認證的鏈接
- 傳輸加密-ssl/tsl
- 創建wss服務
- 創建https服務
- workerman作為客戶端
- 作為ws/wss客戶端
- PHP的幾種回調寫法
- 附錄
- Linux內核調優
- 壓力測試
- 安裝擴展
- websocket協議
- ws協議
- text協議
- frame協議
- 不支持的函數/特性
- 版權信息