請問,若一次收到多組的資料封包,在 dealInput 函數上如何處理?
例如: 在 dealInput 函數上預期收到27byte但實際已收到87Byte,是否是回傳0,然後在dealProcess 自行使用 Buffer 切割封包,若是這樣,dealInput 的函數就無意義了。 請問如何處理?
在配置 例如 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
因為測試主機在新加坡,所以封包資料會 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); } }
這樣不知有沒有問題?
謝謝。
在一个请求开始时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不会超过单笔资料的长度。
可能我設定錯誤,我再試試看。 謝謝您的回答。
我找到問題了,因為我是使用 workerman-chat-master 這個專案去修改。
而 Gateway.conf 的 preread_length 設定為 65535 所以才會出現多筆資料一次傳送,修改為正確值後
就正常的, 謝謝您的回應。
好的,不客气。
在配置 例如 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
因為測試主機在新加坡,所以封包資料會 Delay,並且同時收到多筆。
我的問題是連續傳送多筆封包資料,其中最後一筆尚未收完,若是以現在您設計的架構,則無法準確處理單獨的每一筆封包。
我現在的解決方法,是在 dealInput 計算封包長度時,若傳回超過單筆封包長度時,便傳回負數,讓dealProcess 能得到完整的封包,剩下的封包等接收完畢後,在下一次傳給dealInput 檢查。
初步修改的程式碼如下:
這樣不知有沒有問題?
謝謝。
在一个请求开始时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不会超过单笔资料的长度。
可能我設定錯誤,我再試試看。
謝謝您的回答。
我找到問題了,因為我是使用 workerman-chat-master 這個專案去修改。
而 Gateway.conf 的 preread_length 設定為 65535 所以才會出現多筆資料一次傳送,修改為正確值後
就正常的, 謝謝您的回應。
好的,不客气。