請(qǐng)問(wèn),若一次收到多組的資料封包,在 dealInput 函數(shù)上如何處理?
例如: 在 dealInput 函數(shù)上預(yù)期收到27byte但實(shí)際已收到87Byte,是否是回傳0,然後在dealProcess 自行使用 Buffer 切割封包,若是這樣,dealInput 的函數(shù)就無(wú)意義了。
請(qǐng)問(wèn)如何處理?
在配置 例如 conf/conf.d/xxx.conf 中 有個(gè) preread_length 字段,這個(gè)字段表明當(dāng)有數(shù)據(jù)到來(lái)時(shí),你不會(huì)全部讀取,而是讀取preread_length長(zhǎng)度。
將preread_length設(shè)置成你預(yù)期讀取的數(shù)據(jù)長(zhǎng)度,一般設(shè)置為你協(xié)議包頭的長(zhǎng)度。例如你的協(xié)議包頭長(zhǎng)度為27byte,則preread_length=27。dealInput在一個(gè)請(qǐng)求數(shù)據(jù)到來(lái)時(shí),會(huì)預(yù)先讀取27字節(jié),一般根據(jù)頭部這27字節(jié)能夠計(jì)算出整體包長(zhǎng),例如整個(gè)包包長(zhǎng)是1027,那么你還差1000byte沒(méi)收到,則dealnput返回1000,那么workerman會(huì)再次等待1000byte數(shù)據(jù)到來(lái)并讀取1000byte
因?yàn)闇y(cè)試主機(jī)在新加坡,所以封包資料會(huì) Delay,並且同時(shí)收到多筆。
我的問(wèn)題是連續(xù)傳送多筆封包資料,其中最後一筆尚未收完,若是以現(xiàn)在您設(shè)計(jì)的架構(gòu),則無(wú)法準(zhǔn)確處理單獨(dú)的每一筆封包。
我現(xiàn)在的解決方法,是在 dealInput 計(jì)算封包長(zhǎng)度時(shí),若傳回超過(guò)單筆封包長(zhǎng)度時(shí),便傳回負(fù)數(shù),讓dealProcess 能得到完整的封包,剩下的封包等接收完畢後,在下一次傳給dealInput 檢查。
初步修改的程式碼如下:
public function dealInput($recv_str)
{
1. 未達(dá)一個(gè)完整資料
return 單一筆資料的長(zhǎng)度 - strlen($recv_st); // 正數(shù)
2. 剛好收到一筆資料
return 單一筆資料的長(zhǎng)度-strlen($recv_st); // 0
3. 收到多筆資料(溢收)
return 單一筆資料的長(zhǎng)度 - strlen($recv_str); // 負(fù)數(shù)
}
//========================================================
public function dealInputBase($connection, $flag, $fd = null)
{
$this->currentDealFd = $fd;
$buffer = stream_socket_recvfrom($connection, $this->recvBuffers);
// 出錯(cuò)了
if('' == $buffer && '' == ($buffer = fread($connection, $this->recvBuffers)))
{
if(!feof($connection))
{
return;
}
// 客戶(hù)端提前斷開(kāi)鏈接
$this->statusInfo++;
// 如果該鏈接對(duì)應(yīng)的buffer有數(shù)據(jù),說(shuō)明發(fā)生錯(cuò)誤
if(!empty($this->recvBuffers))
{
$this->statusInfo++;
$this->notice("CLIENT_CLOSE\nCLIENT_IP:".$this->getRemoteIp()."\nBUFFER:\n");
}
// 關(guān)閉鏈接
$this->closeClient($fd);
if($this->workerStatus == self::STATUS_SHUTDOWN)
{
$this->stop();
}
return;
}
$this->recvBuffers .= $buffer;
// 分拆資料封包
while(true)
{
$remain_len = $this->dealInput($this->recvBuffers);
if ($remain_len<=0)
{
$data = $this->recvBuffers;
if ($remain_len<0) // 只傳一筆資料至dealProcess
{
$data = substr($data,0,strlen($data)+$remain_len);
}
$this->dealProcess($data);
if ($remain_len<0) // 負(fù)數(shù) 再分拆
{
$this->recvBuffers = substr($this->recvBuffers,$remain_len); // 複製新資料
}else{
if($this->isPersistentConnection)
{
$this->recvBuffers = array('buf'=>'', 'remain_len'=>$this->prereadLength);
}else{
// 關(guān)閉鏈接
if(empty($this->sendBuffers))
{
$this->closeClient($fd);
}
}
break;
}
}
else if(false === $remain_len)
{
// 出錯(cuò)
$this->statusInfo++;
$this->sendToClient('packet_err:'.$this->recvBuffers);
$this->notice("PACKET_ERROR\nCLIENT_IP:".$this->getRemoteIp()."\nBUFFER:\n");
$this->closeClient($fd);
break;
}
else
{
$this->recvBuffers = $remain_len;
break;
}
}
// 檢查是否是關(guān)閉狀態(tài)或者是否到達(dá)請(qǐng)求上限
if($this->workerStatus == self::STATUS_SHUTDOWN || $this->statusInfo >= $this->maxRequests)
{
// 停止服務(wù)
$this->stop();
// EXIT_WAIT_TIME秒后退出進(jìn)程
pcntl_alarm(self::EXIT_WAIT_TIME);
}
}
這樣不知有沒(méi)有問(wèn)題?
謝謝。
在一個(gè)請(qǐng)求開(kāi)始時(shí)dealInput($recv_str) 中的$recv_str長(zhǎng)度不會(huì)超過(guò)你設(shè)定的 preread_length,后面也不會(huì)超過(guò)你在 dealInput($recv_str) return 的長(zhǎng)度,所以不存在收到的$recv_str長(zhǎng)度大于單一資料的長(zhǎng)度。
雖然有可能在服務(wù)端的socket緩沖區(qū)堆積多筆資料,但是workerman還是嚴(yán)格按照 preread_length + dealInput($recv_str) 的return值來(lái)從socket緩沖區(qū)截取數(shù)據(jù),只有當(dāng)前單筆數(shù)據(jù)處理完畢后,才會(huì)去socket緩沖區(qū)按照同樣的規(guī)則截取下一筆數(shù)據(jù)資料。所以只要你的 preread_length 和 dealInput($recv_str) 的return值是正確的,$recv_str不會(huì)超過(guò)單筆資料的長(zhǎng)度。
我找到問(wèn)題了,因?yàn)槲沂鞘褂?workerman-chat-master 這個(gè)專(zhuān)案去修改。
而 Gateway.conf 的 preread_length 設(shè)定為 65535 所以才會(huì)出現(xiàn)多筆資料一次傳送,修改為正確值後
就正常的, 謝謝您的回應(yīng)。