# Swoole編程指南-EOF協議
[原文鏈接](http://www.catplanet.me/?id=12)
[TOC]
## 什么是EOF協議
EOF(End of File)是一個結束標記,意思為在逐個讀取數據流中的數據時,如果發現讀到EOF標記,就代表已經讀到了數據末尾。在TCP的數據流中,使用EOF協議的數據流特征如下:
> | 數據 | EOF | 數據 |EOF|
在每一串正常數據的末尾,添加一個預先規定的、絕對不會出現在數據中的字符串作為結束標記。這樣接收到的數據就可以根據這個EOF標記來切分數據。
## 開啟EOF支持
在Swoole中,可以使用如下配置選項來開啟EOF功能:
```
$server->set([
'open_eof_split' => true, // 開啟EOF檢測
'package_eof' => '/r/n' , // 設置EOF標記
]);
```
其中,open_eof_split選項會開啟Swoole底層對接收到的數據從頭開始依次掃描檢查,當找到第一個EOF標記時,將已經掃描過的數據作為一個完整的數據包通過onReceive回調發送給PHP層處理。這里需要注意的是,package_eof只允許設置長度不超過8的字符串。
這里要注意到,open_eof_split選項是依次掃描數據中的EOF標記的,這樣雖然保證每次回調都只會收到一個完整的數據包,但是性能較差。因此Swoole還提供了另外一個不同的選項:
```
$server->set([
'open_eof_check' => true, // 開啟EOF檢測
]);
```
open_eof_check同樣會開啟EOF檢測。不同的是,open_eof_check只檢查接收數據的末尾是否為EOF標記。相比于open_eof_split,這種方式性能最好,幾乎沒有損耗。但是如果同時收到了N條帶有EOF標記的數據,這種方式會同時將N個數據包合并為一個回調給PHP層處理,因此需要在PHP層通過EOF標記對數據做二次拆分。
## 實戰
為了演示效果,我將直接使用open_eof_check選項,并演示如何拆分數據包。 首先是服務器端代碼,如下:
```
class Server
{
private $serv;
public function __construct()
{
$this->serv = new swoole_server("0.0.0.0", 9501);
$this->serv->set(array(
'worker_num' => 1,
'open_eof_check' => true,
'package_eof' => "\r\n",
));
$this->serv->on('Connect', array($this, 'onConnect'));
$this->serv->on('Receive', array($this, 'onReceive'));
$this->serv->on('Close', array($this, 'onClose'));
$this->serv->start();
}
public function onConnect( $serv, $fd, $from_id )
{
echo "Client {$fd} connect\n";
}
public function onReceive( swoole_server $serv, $fd, $from_id, $data )
{
$data_list = explode("\r\n", $data);
foreach ($data_list as $item)
{
if(empty($item))
continue;
var_dump($item);
$serv->send($fd, $item . "\r\n");
}
}
public function onClose( $serv, $fd, $from_id )
{
echo "Client {$fd} close connection\n";
}
}
new Server();
```
可以看到,在接收到數據后,我先將接收到的數據用EOF標記作為分隔符做了一次拆分,這樣就能得到一個個獨立的完整數據包,然后依次處理即可。
- SD3.X簡介
- 捐贈SD項目
- VIP服務
- 基礎篇
- 搭建環境
- 使用Composer安裝/更新SD框架
- 啟動命令
- 開發注意事項
- 框架配置
- 配置文件夾
- server.php
- ports.php
- business.php
- mysql.php
- redis.php
- timerTask.php
- log.php
- consul.php
- catCache.php
- client.php
- 自定義配置
- 框架入口
- MVC架構
- 加載器-Loader
- 控制器-Controller
- 模型-Model
- 視圖-View
- 同步任務-Task
- 封裝器
- Swoole編程指南-EOF協議
- Swoole編程指南-固定包頭協議
- 封裝器-Pack
- 路由器
- TCP相關
- 綁定UID
- Send系列
- Sub/Pub
- 獲取服務器信息
- Http相關
- HttpInput
- HttpOutput
- 默認路由規則
- WebSocket相關
- 使用SSL
- 公共函數
- 進階篇
- 內核優化
- 封裝器路由器原理剖析
- 對象池
- 上下文-Context
- 中間件
- 進程管理
- 創建自定義進程
- 進程間RPC
- 自定義進程如何使用連接池
- 異步連接池
- Redis
- Mysql
- Mqtt
- HttpClient
- Client
- AMQP
- RPC
- 日志工具-GrayLog
- 微服務-Consul
- Consul基礎
- 搭建Consul服務器
- SD中Consul配置
- 微服務
- 選舉-Leader
- Consul動態配置定時任務
- 熔斷與降級
- 集群-Cluster
- 高速緩存-CatCache
- 萬物-Actor
- Actor原型
- Actor的創建
- Actor間的通訊
- 消息派發-EventDispatcher
- 延遲隊列-TimerCallBack
- 協程
- 訂閱與發布
- MQTT簡易服務器
- AMQP異步任務調度
- 自定義命令-Console
- 調試工具Channel
- 特別注意事項
- 日常問題總結
- 實踐案例
- 物聯網自定義協議
- Actor在游戲的應用
- Mongodb以及一些同步擴展的使用
- 自定義進程使用MQTT客戶端
- 開發者工具
- SDHelper