1,在短時間內資料傳送過快時,會發生粘包現象,比如下面的**,這個現象是雙向的,客戶端,服務端均可能出現此問題
下面只是以客戶端 to 服務端舉例
server.php
<?php
$host = '0.0.0.0';
//建立server物件,監聽 127.0.0.1:9501埠
$serv = new swoole\server($host, 9501);
//監聽資料接收事件
$serv->on('receive', function ($serv, $fd, $from_id, $data) );
//啟動伺服器
$serv->start();
client.php
<?php
$client = new swoole_client(swoole_sock_tcp);
//連線到伺服器
if (!$client->connect('127.0.0.1', 9501, 0.5))
//向伺服器傳送資料
for($i=0;$i<3;$i++)
//關閉連線
$client->close();
感覺應該輸出下面這種格式的資料
但其實,會輸出這種
為什麼呢?
2,tcp資料的傳輸,並不是實時的,傳送端和接收端,都有快取機制,因此,出現上面的情況,有兩個原因
1)客戶端,在短時間內快速的傳送了單次小量的資料,在傳送之前被快取了,一次性傳送到了服務端,上面的就是這個原因
2)服務端,沒有及時的拿走資料,導致資料再服務端堆積,粘連
3,swoole有兩種解決方法
1)在服務端新增設定,在客戶端要傳輸的資料末尾新增上 "\r\n"
官方文件:
其原理就是服務端對傳過來的每條資料用分隔符分隔,就像explode()函式
這方法主要有兩個缺點:1,效能非常差;2,如果需要傳輸的資料裡有設定的分隔符,會導致資料被分隔(官方不推薦這個方法)
$serv->set([
'open_eof_split'=> true,
'package_eof' => "\r\n"
]);
2)在客戶端,用pack把資料的長度打包到資料頭,在服務端用unpack解析到資料的長度,再根據長度擷取(推薦)
//客戶端
$data = 'hello';
$len = pack('n',strlen($data));
for($i=0;$i<30;$i++)
//服務端
$length = substr($data,0,4);
$length = unpack('n',$length)[1];
$data_list = str_split($data,$length+4);
foreach($data_list as $data)
3)swoole,對第二種方法,可以直接在服務端設定,就不用像上面的**那樣解析了(客戶端**不能省,還和上面一樣),如果是服務端 傳送給客戶端粘包,那麼就在客戶端設定
文件:
$serv->set([
'open_length_check' => true,
//設定最大資料報尺寸,單位為位元組
'package_max_length' => 1024 * 1024 * 2,
//客戶端的pack函式用的n打包,所以設定n
'package_length_type' => 'n',
//存長度的值從哪開始
'package_length_offset' => 0,
//n 佔4個長度,所以這裡填4
'package_body_offset' => 4,
]);
處理tcp粘包問題
tcp是位元組流,無邊界,udp是訊息,是有邊界的。就是udp返回的就是乙個訊息。所以tcp會產生粘包問題。如何解決粘包問題,所以我們要在應用層維護訊息與訊息的邊界。比如說定長包,包尾加 r n ftp 包頭加包體長度,更複雜的應用層協議。readn接受確切資料的讀操作 cli include in...
swoole使用tcp的時候粘包問題
文章參考位址使用自定義協議粘包 如何解決swoole中使用tcp的粘包問題 兩種方式 第一種通過在swoole server的set中設定分隔符 1 在你的client端,呼叫send時加 name xiaomi client send name.php eol 2 在server端 server ...
TCP粘包的拆包處理
因為tcp是流式處理的,所以包沒有邊界,必須設計乙個包頭,裡面表示包的長度 一般用位元組表示 根據這個來逐個拆包。如果對於傳送 接收頻率不高的話,一般也就不做拆包處理了,因為不大可能有粘包現象。以下是粘包和拆包的分析 用qt的tcpsocket讀出的資料來拆 1 m imp m thread boo...