@walkor大神,非常感謝大神這么晚還這么快速的回復(fù),謝謝了!也特別感謝大神們開(kāi)發(fā)出workerman這么優(yōu)秀的框架,給我們廣大開(kāi)發(fā)者帶來(lái)的極大便利,是碼農(nóng)們的福音!
我對(duì)問(wèn)題重新做了下編輯,同時(shí)也把代碼demo直接發(fā)上來(lái),還請(qǐng)大神能給予指導(dǎo),問(wèn)題還是onmessage回調(diào)里去實(shí)現(xiàn)服務(wù)端給客戶端發(fā)送消息時(shí)候,無(wú)法觸達(dá),代碼如下:
use \Workerman\Worker;
use \Workerman\Timer;
use \Workerman\Connection\UdpConnection;
// 自動(dòng)加載類
require_once __DIR__ . '/../../vendor/autoload.php';
$worker = new Worker('udp://0.0.0.0:6000');
// worker名稱
$worker->name = 'XMC-UDP-Worker';
// Worker進(jìn)程數(shù)量
/*
* 注意這里進(jìn)程數(shù)必須設(shè)置為1,否則會(huì)報(bào)端口占用錯(cuò)誤
* (php 7可以設(shè)置進(jìn)程數(shù)大于1,前提是$inner_text_worker->reusePort=true)
*/
$worker->count = 1;
$worker->uids = [];
//啟動(dòng)內(nèi)部通訊
$worker->onWorkerStart = function ($worker) {
// 開(kāi)啟一個(gè)內(nèi)部端口,方便內(nèi)部系統(tǒng)推送數(shù)據(jù),Text協(xié)議格式 文本+換行符
$inner_text_worker = new Worker('text://0.0.0.0:5678');
$inner_text_worker->onMessage = function ($connection, $buffer) {
// $data數(shù)組格式,里面有uid,表示向那個(gè)uid的頁(yè)面推送數(shù)據(jù)
$data = json_decode($buffer, true);
$uid = intval($data['uid']);
// 通過(guò)workerman,向uid的頁(yè)面推送數(shù)據(jù)
$ret = sendMessageByUid($uid, $data['msg']);
// 返回推送結(jié)果
$connection->send($ret ? 'ok' : 'fail');
};
// ## 執(zhí)行監(jiān)聽(tīng) ##
$inner_text_worker->listen();
};
//監(jiān)聽(tīng)接收消息
$worker->onMessage = function ($connection, $message) {
global $worker;
if (!isset($connection->uid)) {
$ip_arr = explode('.', $connection->getRemoteIp());
///$port = $connection->getRemotePort();
$ip = intval(end($ip_arr));
$connection->uid = $ip;
$worker->uids[$connection->uid] = $connection;
}
//$client_id這里為了測(cè)試直接寫死為一個(gè)客戶端IP
$client_id=109;
$client = stream_socket_client('tcp://0.0.0.0:5678', $errno, $errmsg, 1);
// 推送的數(shù)據(jù),包含uid字段,表示是給這個(gè)uid推送
$data = json_encode(['uid' => $client_id, 'msg' => 'test']);
// 發(fā)送數(shù)據(jù),注意5678端口是Text協(xié)議的端口,Text協(xié)議需要在數(shù)據(jù)末尾加上換行符
fwrite($client, $data . "\n");
//echo fread($client, 8192);
};
// 向所有驗(yàn)證的用戶推送數(shù)據(jù)
function broadcast($message)
{
global $worker;
foreach ($worker->uids as $connection) {
$connection->send($message);
}
};
// 針對(duì)uid推送數(shù)據(jù)
function sendMessageByUid($uid, $message)
{
global $worker;
if (isset($worker->uids[$uid])) {
$connection = $worker->uids[$uid];
$connection->send($message);
}
};
// 如果不是在根目錄啟動(dòng),則運(yùn)行runAll方法
if (!defined('GLOBAL_START')) {
Worker::runAll();
}
require_once 只會(huì)加載一次,下次再次執(zhí)行require_once 時(shí)這個(gè)文件就不會(huì)加載了,所以里面的業(yè)務(wù)邏輯就不會(huì)再觸發(fā)了。
require_once 改成 require 試下
walkor,好像還并不是這問(wèn)題,我直接把push.php推送消息給客戶端的代碼直接寫到onmessage回調(diào)里,客戶端也還是收不到,這個(gè)是和內(nèi)部通訊實(shí)現(xiàn)方式有關(guān)系么?還是需要其他手段來(lái)實(shí)現(xiàn)推送呢?
但是我使用普通的php文件,或直接用push.php去推送,就是可以的,說(shuō)明內(nèi)部端口那應(yīng)該沒(méi)問(wèn)題,問(wèn)題就是onmessage回調(diào)去做推送就存在問(wèn)題!
寫個(gè)能重現(xiàn)問(wèn)題的精簡(jiǎn)的demo吧。沒(méi)代碼沒(méi)辦法定位啊。我又不是神,能預(yù)知你哪里寫了bug ??
$worker->uids[$connection->uid] = $connection;
這里不對(duì),應(yīng)該改成
$worker->uids[$connection->id] = $connection;
還有你的uid寫死了只給109發(fā)數(shù)據(jù),那么
$connection->uid = $ip;
$worker->uids[$connection->uid] = $connection;
也要寫死,$connection->uid = 109;
才對(duì)。
另外uid要做個(gè)定時(shí)器,將長(zhǎng)時(shí)間不活躍的$connection 執(zhí)行關(guān)閉,然后從$worker->uids里刪除,否則這個(gè)connection一直占用內(nèi)存,會(huì)導(dǎo)致內(nèi)存泄漏。
最后,udp是無(wú)連接的并且無(wú)法保證數(shù)據(jù)一定能推送到客戶端,客戶端及客戶端所在網(wǎng)關(guān)可能隨時(shí)會(huì)關(guān)閉udp臨時(shí)端口。一般外網(wǎng)udp超過(guò)1分鐘,udp可能就無(wú)法發(fā)送到客戶端了。
@walkor 又打攪了!
$worker->uids[$connection->uid] = $connection; 這個(gè)寫法是按照官方這里說(shuō)明來(lái)寫的:
http://m.wtbis.cn/doc/workerman/faq/send-data-to-client.html,定義的是uid,不是id哦(您看下是之前官方寫的是不是有變化呢),前后也都是uid來(lái)標(biāo)識(shí)的;另外109客戶端實(shí)際過(guò)程中,是事先就綁定好的,走的是 $connection->uid = 109操作,綁定完了之后,才會(huì)去執(zhí)行發(fā)送消息給客戶端操作,感覺(jué)還是回調(diào)哪里有問(wèn)題,如果是普通后臺(tái)直接使用stream_socket_client去進(jìn)行發(fā)送操作,客戶端都是可以正常收到的。
看錯(cuò)了, $worker->uids[$connection->uid] = $connection; 是對(duì)的。
但是udp沒(méi)有關(guān)閉事件,要加個(gè)判斷,把上一個(gè)connection關(guān)閉
// 將之前的連接關(guān)閉,避免內(nèi)存泄漏
if($worker->uids[$connection->uid]) {
$worker->uids[$connection->uid]->close();
}
$worker->uids[$connection->uid] = $connection;
我寫死 $client_id=1; 了,本地127.0.0.1測(cè)試沒(méi)問(wèn)題。
客戶端代碼
<?php
use \Workerman\Worker;
use \Workerman\Connection\AsyncUdpConnection;
require_once __DIR__ . '/../../vendor/autoload.php';
$worker = new Worker();
$worker->onWorkerStart = function ($worker) {
$con = new AsyncUdpConnection('udp://127.0.0.1:6000');
$con->onMessage = function($con, $data){
var_dump($data);
};
$con->onConnect = function($con){$con->send(1);};
$con->connect();
};
if (!defined('GLOBAL_START')) {
Worker::runAll();
}
結(jié)果:
@walkor,目前測(cè)試是每次重啟服務(wù)后,第1次能發(fā)送消息到客戶端,再次發(fā)送就無(wú)法送達(dá)呢(也就是后面的發(fā)送消息給客戶端執(zhí)行無(wú)效了),這個(gè)是哪需要做特殊處理不?代碼是這樣寫的:
//監(jiān)聽(tīng)接收消息
$worker->onMessage = function (UdpConnection $connection, $message) {
global $worker;
//設(shè)置連接
$connection->uid = 109;
if (isset($worker->uids[$connection->uid])) {
$worker->uids[$connection->uid]->close();
}
$worker->uids[$connection->uid] = $connection;
//發(fā)送消息給客戶端
$client = stream_socket_client('tcp://0.0.0.0:5678', $errno, $errmsg, 1);
$data = json_encode(['uid' => 109, 'msg' => 'TASK[109 001]CRC[2c4c]']);
fwrite($client, $data . "\n");
}
加了個(gè)定時(shí)器,測(cè)試沒(méi)問(wèn)題
<?php
use \Workerman\Worker;
use \Workerman\Timer;
use \Workerman\Connection\AsyncUdpConnection;
require_once __DIR__ . '/../../vendor/autoload.php';
$worker = new Worker();
$worker->onWorkerStart = function ($worker) {
$con = new AsyncUdpConnection('udp://127.0.0.1:6000');
$con->onMessage = function($con, $data){
var_dump($data);
};
$con->onConnect = function($con){
Timer::add(5, function()use($con) {
$con->send(2);
});
$con->send(1);
};
$con->connect();
};
if (!defined('GLOBAL_START')) {
Worker::runAll();
}
大神,這個(gè)是客戶端的代碼么?我寫的只有服務(wù)端程序哦,目前場(chǎng)景是手機(jī)APP里有個(gè)條形碼,然后線下有個(gè)硬件掃碼器,用來(lái)掃碼手機(jī)APP的條形碼,掃碼成功就掃碼器那會(huì)發(fā)送消息給UDP服務(wù)器,服務(wù)器onmessage進(jìn)行回調(diào)后,就啟動(dòng)發(fā)送消息指令給某個(gè)客戶端哈!這個(gè)我實(shí)在不知道要改哪塊的代碼了,因?yàn)檫@個(gè)動(dòng)作是掃碼器客戶端發(fā)送消息給服務(wù)器后觸發(fā)的呢!
現(xiàn)在就是線下掃碼器回傳信息給服務(wù)端,第一次可以觸發(fā),往下就不觸發(fā)發(fā)送消息給指定客戶端了,搞了好幾天,百思不得其解哈,實(shí)在是不知道問(wèn)題出在哪?如果我不走這個(gè)服務(wù)端程序,用普通的php去調(diào)用就完全正常,卡就卡在服務(wù)端onmessage這個(gè)點(diǎn)上來(lái),感謝walkor不厭其煩的回復(fù)和解答哈,再次感謝!
會(huì)不會(huì)跟線下硬件掃碼器有關(guān)系呢?但是掃碼器的UDP數(shù)據(jù)是發(fā)送上來(lái)了,服務(wù)端onmessage里也收到了,但是就是不走觸發(fā)發(fā)送消息給客戶端的指令。
不客氣。建議你用我上面workerman做的udp客戶端代替真實(shí)客戶端來(lái)測(cè)試,沒(méi)問(wèn)題再用真實(shí)客戶端測(cè)試。
另外可以配合抓包工具看下服務(wù)端是否有發(fā)送udp給客戶端。
多打日志看下吧,看下代碼走大哪個(gè)分支。客戶端id都打印出來(lái),看下id對(duì)不對(duì),看下對(duì)應(yīng)的客戶端是否和服務(wù)端發(fā)器過(guò)udp請(qǐng)求,對(duì)應(yīng)的客戶端connection是否是存在的。感覺(jué)這種問(wèn)題打日志抓包很好定位。
呃呃,忘了這塊調(diào)試日志了,我好好按照您說(shuō)的仔細(xì)檢查一下,務(wù)必揪出元兇!感謝大神哈,多次發(fā)問(wèn)實(shí)在打攪了!