需求

  目前项目需要做了实时推送功能,使用webSOCKET 及时向多个用户端推送信息提醒功能

解决办法

  考虑开发成本和维护便捷,考虑使用成熟框架workerman的官方socketIo插件功能

解决步奏1、安装插件

使用composer 下载 workerman的phpsocket.io插件

   命令:

     composer require workerman/phpsocket.io

2、.env(yii2框架根目录)配置文件 添加

// 监听 内网ip INNER_NET=10.46.224.26 // 内网端口1 SOCKET_PORT1=9504 // 内网端口2 SOCKET_PORT2=9506

3、服务命令

  在yii2框架的 console 文件夹下controller里创建控制器WeChatController控制器 创建 start-up 方法,重新定义websocket开启方法 开启socket服务

public function actionStartup(array $name = []){ global $argv; // var_export($argv) ; die(); $action_s = isset($argv[2]) ? $argv[2] : '' ; $argv[0] = $argv[1]; $argv[1] = $action_s; if (isset($argv[3]) &&$argv[3] && in_array($argv[3],$this->use_mode) ) { switch ($action_s){ case 'start': case 'status': if($argv[3] == self::DAEMON){ $argv[2] = '-'.$argv[3] ; } break ; case 'restart': if($argv[3] == self::DAEMON || $argv[3]==self::GRACEFULLY){ $argv[2] = '-'.$argv[3] ; } break ; case 'reload': case 'stop': if($argv[3]==self::GRACEFULLY){ $argv[2] = '-'.$argv[3] ; } break ; } } $ip = getenv('INNER_NET') ; $io = new SocketIO(getenv("SOCKET_PORT2"),[],$ip); $service = (new WorkmanSocketService()) ; $service->listenInit($io); //var_dump($io) ; // 当有客户端连接时打印一行文字 Worker::runAll(); }

  开启服务命令

启动命令 以debug(调试)方式启动 php ./yii websocket/startup start 以daemon(守护进程)方式启动 (注意不是-d 是小写字符d 和官方文档有点差异) php ./yii websocket/startup start d 停止 php ./yii websocket/startup stop 重启 php ./yii websocket/startup restart 平滑重启 php ./yii websocket/startup reload 查看状态 php ./yii websocket/startup status 查看连接状态(需要Workerman版本>=3.5.0) php ./yii websocket/startup connections

4、支持SSL(https wss)

  SSL支持有两种方法,workerman原生和nginx代理

  workerman原生支持  SSL 要求workerman>=3.3.7 phpsocket.io>=1.1.1

<?php require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; use PHPSocketIO\SocketIO; // 传入ssl选项,包含证书的路径 $context = array( 'ssl' => array( 'local_cert' => '/your/path/of/server.pem', 'local_pk' => '/your/path/of/server.key', 'verify_peer' => false, ) ); $io = new SocketIO(2120, $context); $io->on('connection', function($socket)use($io){ echo "new connection coming\n"; }); Worker::runAll();

注意:

  1、证书是要验证域名的,所以客户端链接时要指定域名才能顺利地建立链接。  2、客户端连接时不能再用http方式,要改成https类似下面这样。

<script> var socket = io('https://yoursite.com:2120'); //..... </script>

nginx代理SSL

前提条件及准备工作:1、已经安装nginx,版本不低于1.32、假设phpsocket.io监听的是2120端口3、已经申请了证书(pem/crt文件及key文件)放在了/etc/nginx/conf.d/ssl下4、打算利用nginx开启443端口对外提供ssl代理服务(端口可以根据需要修改)nginx配置类似如下:

server { listen 443; ssl on; ssl_certificate /etc/ssl/server.pem; ssl_certificate_key /etc/ssl/server.key; ssl_session_timeout 5m; ssl_session_cache shared:SSL:50m; ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ALL:!ADH:!EXPORT56:RC4 RSA: HIGH: MEDIUM: LOW: SSLv2: EXP; location /socket.io { proxy_pass http://127.0.0.1:2120; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header X-Real-IP $remote_addr; } # location / {} 站点的其它配置... }

注意:

  1、证书是要验证域名的,所以客户端链接时要指定域名才能顺利地建立链接。  2、客户端连接时不能再用http方式,要改成https类似下面这样。

<script> var socket = io('https://yoursite.com'); //..... </script>

5、业务逻辑

  首先通过php ./yii websocket/startup 开启socketIo 服务。 如图:

yii使用教程(yii2框架融合workman消息推送socketIO)(1)

就是成功开启了一个socketIo 服务 ,在socketIo 连接初始化是创建 监听http 服务 , 后续我可以通过 这个http 实现服务器端向客户端推送信息:

yii使用教程(yii2框架融合workman消息推送socketIO)(2)

服务器推送代码接口如下: 通过curl post 推送接口 经过workerman 转发给每个用户客户端

public function sendContent($content,$dataSetId = 0){ if(isSsl()){ $push_api_url = "https://".getenv("INNER_NET").":".getenv("SOCKET_PORT1")."/"; } else { $push_api_url = "http://".getenv("INNER_NET").":".getenv("SOCKET_PORT1")."/"; } $post_data = array( "type" => self::SocketMessage, "content" => $content, "dataSetId" => $dataSetId, ); $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, $push_api_url ); curl_setopt( $ch, CURLOPT_POST, 1 ); curl_setopt( $ch, CURLOPT_HEADER, 0 ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_data ); curl_setopt($ch, CURLOPT_HTTPHEADER, array("Expect:")); $return = curl_exec( $ch ); $error = curl_error($ch) ; curl_close($ch); var_dump($error); echo $return ; return $return ; }

,