一次收到多組資料封包,如何處理?

0

請問,若一次收到多組的資料封包,在 dealInput 函數上如何處理?


例如: 在 dealInput 函數上預期收到27byte但實際已收到87Byte,是否是回傳0,然後在dealProcess 自行使用 Buffer 切割封包,若是這樣,dealInput 的函數就無意義了。
請問如何處理?

已邀请:

walkor

赞同来自: fuzhufang

在一个请求开始时dealInput($recv_str) 中的$recv_str长度不会超过你设定的 preread_length,后面也不会超过你在 dealInput($recv_str) return 的长度,所以不存在收到的$recv_str长度大于单一资料的长度。


虽然有可能在服务端的socket缓冲区堆积多笔资料,但是workerman还是严格按照 preread_length + dealInput($recv_str) 的return值来从socket缓冲区截取数据,只有当前单笔数据处理完毕后,才会去socket缓冲区按照同样的规则截取下一笔数据资料。所以只要你的 preread_length 和 dealInput($recv_str) 的return值是正确的,$recv_str不会超过单笔资料的长度。

walkor

赞同来自:

在配置 例如 conf/conf.d/xxx.conf 中 有个 preread_length 字段,这个字段表明当有数据到来时,你不会全部读取,而是读取preread_length长度。


将preread_length设置成你预期读取的数据长度,一般设置为你协议包头的长度。例如你的协议包头长度为27byte,则preread_length=27。dealInput在一个请求数据到来时,会预先读取27字节,一般根据头部这27字节能够计算出整体包长,例如整个包包长是1027,那么你还差1000byte没收到,则dealnput返回1000,那么workerman会再次等待1000byte数据到来并读取1000byte

flimulus - Hello~

赞同来自:

因為測試主機在新加坡,所以封包資料會 Delay,並且同時收到多筆。


我的問題是連續傳送多筆封包資料,其中最後一筆尚未收完,若是以現在您設計的架構,則無法準確處理單獨的每一筆封包。


我現在的解決方法,是在 dealInput 計算封包長度時,若傳回超過單筆封包長度時,便傳回負數,讓dealProcess 能得到完整的封包,剩下的封包等接收完畢後,在下一次傳給dealInput 檢查。


初步修改的程式碼如下:


public function dealInput($recv_str)
{
1. 未達一個完整資料
return 單一筆資料的長度 - strlen($recv_st); // 正數
2. 剛好收到一筆資料
return 單一筆資料的長度-strlen($recv_st); // 0
3. 收到多筆資料(溢收)
return 單一筆資料的長度 - strlen($recv_str); // 負數
}

//========================================================
public function dealInputBase($connection, $flag, $fd = null)
{
$this->currentDealFd = $fd;
$buffer = stream_socket_recvfrom($connection, $this->recvBuffers);
// 出错了
if('' == $buffer && '' == ($buffer = fread($connection, $this->recvBuffers)))
{
if(!feof($connection))
{
return;
}
// 客户端提前断开链接
$this->statusInfo++;
// 如果该链接对应的buffer有数据,说明发生错误
if(!empty($this->recvBuffers))
{
$this->statusInfo++;
$this->notice("CLIENT_CLOSE\nCLIENT_IP:".$this->getRemoteIp()."\nBUFFER:\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) // 負數 再分拆
{
$this->recvBuffers = substr($this->recvBuffers,$remain_len); // 複製新資料
}else{
if($this->isPersistentConnection)
{
$this->recvBuffers = array('buf'=>'', 'remain_len'=>$this->prereadLength);
}else{
// 关闭链接
if(empty($this->sendBuffers))
{
$this->closeClient($fd);
}
}
break;
}
}
else if(false === $remain_len)
{
// 出错
$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;
}
}

// 检查是否是关闭状态或者是否到达请求上限
if($this->workerStatus == self::STATUS_SHUTDOWN || $this->statusInfo >= $this->maxRequests)
{
// 停止服务
$this->stop();
// EXIT_WAIT_TIME秒后退出进程
pcntl_alarm(self::EXIT_WAIT_TIME);
}
}

這樣不知有沒有問題?


謝謝。

flimulus - Hello~

赞同来自:

可能我設定錯誤,我再試試看。
謝謝您的回答。

flimulus - Hello~

赞同来自:

我找到問題了,因為我是使用 workerman-chat-master 這個專案去修改。


而 Gateway.conf 的 preread_length 設定為 65535 所以才會出現多筆資料一次傳送,修改為正確值後


就正常的, 謝謝您的回應。

walkor

赞同来自:

好的,不客气。

要回复问题请先登录注册