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