首页
下载
文档
社区
视频
捐赠
源代码
赞助商
AOT 编译器
AI 助理
商业产品
PHP AOT 原生编译器
Swoole-Compiler 代码加密器
CRMEB 新零售社交电商系统
登录
注册
全部
提问
分享
讨论
建议
公告
开发框架
CodeGalaxy
发表新帖
关于swoole_client同步模式在php-fpm模式下websocket_server不稳定问题
我想实现类似图中的功能。就是通过浏览器A 用http post方式发送给我的业务代码接口中,然后在业务代码接口中用swoole_client同步阻塞的方式发送给websocket_server端,由websocket_server把消息发送给 浏览器B。  浏览器执行包含swoole_client方法的文件,有时以后能走websocket_server中的onMessage方法,有时候不走,导致不能发送消息给浏览器B。大部分情况下是失败的。 命令行执行包含swoole_client方法的文件,有时以后能走websocket_server中的onMessage方法,有时候不走,导致不能发送消息给浏览器B。少部分情况下是失败的(基本上都成功,但也有的时候失败)。 不知道什么原因,有哪位大神走过这条路,麻烦指点一下,我应该错在哪里,为什么有的时候能成功,有的时候会失败。 代码如下: 服务端websocket_server代码(运行在CLI模式下): {{{ class websocket_server { private $serv; private $conn = null; private static $fd = null; public function __construct() { $this->serv = new swoole_websocket_server("0.0.0.0", 9090, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); $this->serv->set(array( 'worker_num' => 8, 'daemonize' => false, 'max_request' => 10000, 'dispatch_mode' => 2, 'debug_mode' => 1, // 'open_tcp_nodelay' => true, 'ssl_cert_file' => '/www/sslzs/fullchain5.pem', 'ssl_key_file' => '/www/sslzs/privkey5.pem', )); $this->serv->on('Open', array($this, 'onOpen')); $this->serv->on('Message', array($this, 'onMessage')); $this->serv->on('Close', array($this, 'onClose')); $this->serv->start(); } function onOpen($server, $req) { echo 'dakai...'; // $server->push($req->fd, json_encode(33)); } public function onMessage($server, $frame) { echo 'youxiaoxi...'; //$server->push($frame->fd, json_encode(["hello", "world"])); $pData = json_decode($frame->data); $data = array(); if (isset($pData->content)) { $tfd = $this->getFd($pData->tid); //获取绑定的fd $data = $this->add($pData->fid, $pData->tid, $pData->content); //保存消息 $server->push($tfd, json_encode($data)); //推送到接收者 } else { $this->unBind(null,$pData->fid); //首次接入,清除绑定数据 if ($this->bind($pData->fid, $frame->fd)) { //绑定fd $data = $this->loadHistory($pData->fid, $pData->tid); //加载历史记录 } else { $data = array("content" => "无法绑定fd"); } } $server->push($frame->fd, json_encode($data)); //推送到发送者 } public function onClose($server, $fd) { echo 'duanle...'; $this->unBind($fd); echo "Connection close: " . $fd . "\n"; } public function add($fid, $tid, $content) { $url = 'http://localhost:8066/api.php/Ershou/sc_add'; $id = $this->curl_post($url, [ 'fid' => $fid, 'tid' => $tid, 'content' => $content, ]); $data = $this->loadHistory($fid, $tid, $id); return $data; } public function bind($uid, $fd) { $url = 'http://localhost:8066/api.php/Ershou/fd_bind'; return !!$this->curl_post($url, [ 'fd' => $fd, 'uid' => $uid, ]); } public function getFd($uid) { echo 'getFd...'; $url = 'http://localhost:8066/api.php/Ershou/fd_get'; return $this->curl_post($url, [ 'uid' => $uid, ]); } public function unBind($fd, $uid = null) { $url = 'http://localhost:8066/api.php/Ershou/fd_unBind'; return !!$this->curl_post($url, [ 'fd' => $fd, 'uid' => $uid, ]); } public function loadHistory($fid, $tid, $id = null) { $url = 'http://localhost:8066/api.php/Ershou/sc_loadHistory'; $res = $this->curl_post($url, [ 'fid' => $fid, 'tid' => $tid, 'id' => $id, ]); return json_decode($res, true); } /*请求方法 * 方式 curl * 参数 $url 请求地址 * 参数 $data 要提交的Json数据 */ public function curl_post($url, $data = []) { //创建一个curl资源 $ch = curl_init(); //设置url和响应选项 curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //住区url并把它传输给浏览器 $result = curl_exec($ch); //关闭curl资源,并且释放系统资源 curl_close($ch); return $result; } } $server = new websocket_server(); }}} PHP客户端 swoole_client 代码(运行在php-fpm下): {{{ // 要发送的数据 $json_data = json_encode([ 'content' => '为什么有的时候发送失败' . date('Y-m-d H:i:s'), 'fid' => '1', 'tid' => '3', ]); // 调起swoole客户端 $client = new \swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL); $client->set(array( 'worker_num' => 8, 'debug_mode' => 1, 'ssl_cert_file' => '/www/sslzs/fullchain5.pem', 'ssl_key_file' => '/www/sslzs/privkey5.pem', // 'bind_address' => '127.0.0.1', // 'bind_port' => 9090, // 'http_proxy_host' => '0.0.0.0', // 'http_proxy_port' => 9090, )); // 客户端连接服务端 $conn = $client->connect('0.0.0.0', 9090, 0.5); // 必须握手成功后发送信息 $header = createHeader(); $woshou = $client->send($header); var_dump($woshou); // 发送消息 $json_data = \swoole_websocket_server::pack($json_data, WEBSOCKET_OPCODE_TEXT); var_dump($json_data); $sended = $client->send($json_data); var_dump($sended); echo '<Hr>'; echo "success\n"; $client->close(); function createHeader() { $UserAgent = 'SwooleWebsocketClient'; $version = '0.1.4'; $host = '0.0.0.0'; $port = 9090; $path = '/'; $key = generateToken(16); if ($host === '127.0.0.1' || $host === '0.0.0.0') { $host = 'localhost'; } return "GET $path HTTP/1.1" . "\r\n" . "Origin: null" . "\r\n" . "Host: {$host}:{$port}" . "\r\n" . "Sec-WebSocket-Key: {$key}" . "\r\n" . "User-Agent: ".$UserAgent."/" . $version . "\r\n" . "Upgrade: Websocket" . "\r\n" . "Connection: Upgrade" . "\r\n" . "Sec-WebSocket-Protocol: lnmp" . "\r\n" . "Sec-WebSocket-Version: 13" . "\r\n" . "\r\n"; } function generateToken($length) { $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"§$%&/()=[]{}'; $useChars = array(); // select some random chars: for ($i = 0; $i < $length; $i++) { $useChars[] = $characters[mt_rand(0, strlen($characters) - 1)]; } // Add numbers array_push($useChars, rand(0, 9), rand(0, 9), rand(0, 9)); shuffle($useChars); $randomString = trim(implode('', $useChars)); $randomString = substr($randomString, 0, $length); return base64_encode($randomString); } }}}
发布于6年前 · 0 次浏览 · 来自
提问
超
超子超
我想实现类似图中的功能。就是通过浏览器A 用http post方式发送给我的业务代码接口中,然后在业务代码接口中用swoole_client同步阻塞的方式发送给websocket_server端,由websocket_server把消息发送给 浏览器B。  浏览器执行包含swoole_client方法的文件,有时以后能走websocket_server中的onMessage方法,有时候不走,导致不能发送消息给浏览器B。大部分情况下是失败的。 命令行执行包含swoole_client方法的文件,有时以后能走websocket_server中的onMessage方法,有时候不走,导致不能发送消息给浏览器B。少部分情况下是失败的(基本上都成功,但也有的时候失败)。 不知道什么原因,有哪位大神走过这条路,麻烦指点一下,我应该错在哪里,为什么有的时候能成功,有的时候会失败。 代码如下: 服务端websocket_server代码(运行在CLI模式下): {{{ class websocket_server { private $serv; private $conn = null; private static $fd = null; public function __construct() { $this->serv = new swoole_websocket_server("0.0.0.0", 9090, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); $this->serv->set(array( 'worker_num' => 8, 'daemonize' => false, 'max_request' => 10000, 'dispatch_mode' => 2, 'debug_mode' => 1, // 'open_tcp_nodelay' => true, 'ssl_cert_file' => '/www/sslzs/fullchain5.pem', 'ssl_key_file' => '/www/sslzs/privkey5.pem', )); $this->serv->on('Open', array($this, 'onOpen')); $this->serv->on('Message', array($this, 'onMessage')); $this->serv->on('Close', array($this, 'onClose')); $this->serv->start(); } function onOpen($server, $req) { echo 'dakai...'; // $server->push($req->fd, json_encode(33)); } public function onMessage($server, $frame) { echo 'youxiaoxi...'; //$server->push($frame->fd, json_encode(["hello", "world"])); $pData = json_decode($frame->data); $data = array(); if (isset($pData->content)) { $tfd = $this->getFd($pData->tid); //获取绑定的fd $data = $this->add($pData->fid, $pData->tid, $pData->content); //保存消息 $server->push($tfd, json_encode($data)); //推送到接收者 } else { $this->unBind(null,$pData->fid); //首次接入,清除绑定数据 if ($this->bind($pData->fid, $frame->fd)) { //绑定fd $data = $this->loadHistory($pData->fid, $pData->tid); //加载历史记录 } else { $data = array("content" => "无法绑定fd"); } } $server->push($frame->fd, json_encode($data)); //推送到发送者 } public function onClose($server, $fd) { echo 'duanle...'; $this->unBind($fd); echo "Connection close: " . $fd . "\n"; } public function add($fid, $tid, $content) { $url = 'http://localhost:8066/api.php/Ershou/sc_add'; $id = $this->curl_post($url, [ 'fid' => $fid, 'tid' => $tid, 'content' => $content, ]); $data = $this->loadHistory($fid, $tid, $id); return $data; } public function bind($uid, $fd) { $url = 'http://localhost:8066/api.php/Ershou/fd_bind'; return !!$this->curl_post($url, [ 'fd' => $fd, 'uid' => $uid, ]); } public function getFd($uid) { echo 'getFd...'; $url = 'http://localhost:8066/api.php/Ershou/fd_get'; return $this->curl_post($url, [ 'uid' => $uid, ]); } public function unBind($fd, $uid = null) { $url = 'http://localhost:8066/api.php/Ershou/fd_unBind'; return !!$this->curl_post($url, [ 'fd' => $fd, 'uid' => $uid, ]); } public function loadHistory($fid, $tid, $id = null) { $url = 'http://localhost:8066/api.php/Ershou/sc_loadHistory'; $res = $this->curl_post($url, [ 'fid' => $fid, 'tid' => $tid, 'id' => $id, ]); return json_decode($res, true); } /*请求方法 * 方式 curl * 参数 $url 请求地址 * 参数 $data 要提交的Json数据 */ public function curl_post($url, $data = []) { //创建一个curl资源 $ch = curl_init(); //设置url和响应选项 curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //住区url并把它传输给浏览器 $result = curl_exec($ch); //关闭curl资源,并且释放系统资源 curl_close($ch); return $result; } } $server = new websocket_server(); }}} PHP客户端 swoole_client 代码(运行在php-fpm下): {{{ // 要发送的数据 $json_data = json_encode([ 'content' => '为什么有的时候发送失败' . date('Y-m-d H:i:s'), 'fid' => '1', 'tid' => '3', ]); // 调起swoole客户端 $client = new \swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL); $client->set(array( 'worker_num' => 8, 'debug_mode' => 1, 'ssl_cert_file' => '/www/sslzs/fullchain5.pem', 'ssl_key_file' => '/www/sslzs/privkey5.pem', // 'bind_address' => '127.0.0.1', // 'bind_port' => 9090, // 'http_proxy_host' => '0.0.0.0', // 'http_proxy_port' => 9090, )); // 客户端连接服务端 $conn = $client->connect('0.0.0.0', 9090, 0.5); // 必须握手成功后发送信息 $header = createHeader(); $woshou = $client->send($header); var_dump($woshou); // 发送消息 $json_data = \swoole_websocket_server::pack($json_data, WEBSOCKET_OPCODE_TEXT); var_dump($json_data); $sended = $client->send($json_data); var_dump($sended); echo '<Hr>'; echo "success\n"; $client->close(); function createHeader() { $UserAgent = 'SwooleWebsocketClient'; $version = '0.1.4'; $host = '0.0.0.0'; $port = 9090; $path = '/'; $key = generateToken(16); if ($host === '127.0.0.1' || $host === '0.0.0.0') { $host = 'localhost'; } return "GET $path HTTP/1.1" . "\r\n" . "Origin: null" . "\r\n" . "Host: {$host}:{$port}" . "\r\n" . "Sec-WebSocket-Key: {$key}" . "\r\n" . "User-Agent: ".$UserAgent."/" . $version . "\r\n" . "Upgrade: Websocket" . "\r\n" . "Connection: Upgrade" . "\r\n" . "Sec-WebSocket-Protocol: lnmp" . "\r\n" . "Sec-WebSocket-Version: 13" . "\r\n" . "\r\n"; } function generateToken($length) { $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"§$%&/()=[]{}'; $useChars = array(); // select some random chars: for ($i = 0; $i < $length; $i++) { $useChars[] = $characters[mt_rand(0, strlen($characters) - 1)]; } // Add numbers array_push($useChars, rand(0, 9), rand(0, 9), rand(0, 9)); shuffle($useChars); $randomString = trim(implode('', $useChars)); $randomString = substr($randomString, 0, $length); return base64_encode($randomString); } }}}
赞
0
收藏
提问
分享
讨论
建议
公告
开发框架
CodeGalaxy
登录
后参与评论
评论
2020-01-10
爱
爱上的地方
默默等待韩老师的到来...
赞
0
回复
2020-01-11
E
Every day
是不是0.5秒超时了啊? 服务器启websocket客户端 感觉不太合理 ,直接用http请求更直接 seoole 的 websocket 服务器是继承http server 的可有监听http请求。 换成常驻内存的框架把 就不需要网络请求了。http请求和websocket请求都在一个服务下面 直接调用 $this->server()->push()就可以发送了
赞
0
回复
2020-01-16
b
benett
非常感谢 探索者的回答,已经使用GatewayWorker实现。
赞
0
回复