swoole 安装及双协议使用(数据库采用pdo连接池)
//swoole 安装
/* * git clone https://github.com/swoole/swoole-src.git * * cd swoole-src * * phpize * * ./configure --with-php-config=/www/server/php/71/bin/php-config --enable-openssl * * make && sudo make install * * php --ini -----/www/server/php/71/etc/php.ini * * /www/server/php/71/etc/php.ini 中加入 extension=swoole.so * * 重启php * */
<?php declare(strict_types=1); use Swoole\Coroutine; use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Runtime; use JPush\Client as JPush; //激光推送 class WebSocketServer{ protected $serv = null; //WebSocket protected $server = null; //tcp 监听 protected $pool = null; //pdo链接池 protected $host = '0.0.0.0'; //监听对应外网的IP 0.0.0.0监听所有ip protected $port = 9306; //端口号 protected $server_port = 9307; //监听端口号 protected $Push = null; //苹果 / 安卓 推送 public function __construct(){ //创建websocket服务器对象 $this->serv = new Swoole\WebSocket\Server($this->host, $this->port,SWOOLE_PROCESS); //设置参数 //如果业务代码是全异步 IO 的,worker_num设置为 CPU 核数的 1-4 倍最合理 //如果业务代码为同步 IO,worker_num需要根据请求响应时间和系统负载来调整,例如:100-500 //假设每个进程占用 40M 内存,100 个进程就需要占用 4G 内存 $this->serv->set(array( 'worker_num' => 8, //设置启动的worker进程数。【默认值:CPU 核数】 'max_request' => 100000, //设置每个worker进程的最大任务数。【默认值:0 即不会退出进程】 'daemonize' => false, //开启守护进程化【默认值:0】 'heartbeat_idle_time' => 600, // 表示一个连接如果60秒内未向服务器发送任何数据,此连接将被强制关闭 'heartbeat_check_interval' => 60, // 表示每20秒遍历一次 'tcp_fastopen' => true //快速握手 )); $this->server = $this->serv->listen($this->host, $this->server_port,SWOOLE_SOCK_TCP); $this->server->set([ 'open_eof_split' => true, 'package_eof' => "\n", 'buffer_output_size'=>32 * 1024 * 1024, 'tcp_fastopen' => true, //快速握手 ]); // 创建连接池对象,默认创建64个连接 $this->pool = new PDOPool((new PDOConfig) ->withHost('0.0.0.0') ->withPort(3306) ->withDbName('***') ->withCharset('utf8mb4') ->withUsername('***') ->withPassword('***') ); $this->serv->on('WorkerStart', function ($request, $response) { $pdo = $this->pool->get(); //清空 web fd if ($pdo->query("select * from ***")->fetchAll()){ $pdo->exec("DELETE FROM **"); } //清空 tcp fd if ($pdo->query("select * from **")->fetchAll()){ $pdo->exec("DELETE FROM ***"); } $this->pool->put($pdo); var_dump("启动进程(".$response.")").PHP_EOL; }); //监听WebSocket连接打开事件 $this->serv->on('open', function ($serv, $open) { echo "WebSocket 建立连接: {$open->fd}" . PHP_EOL; }); //监听WebSocket消息事件 //客户端向服务器端发送信息时,服务器端触发 onMessage 事件回调 //服务器端可以调用 $server->push() 向某个客户端(使用 $fd 标识符)发送消息,长度最大不得超过 2M $this->serv->on('message', function ($ws, $request) {
try {
}catch (Exception $e){
$this->DeBugLog(array('title'=>'web:上线(异常)','fd'=>$request->fd,'err'=>$e));
}
}); //监听WebSocket连接关闭事件 $this->serv->on('close', function ($serv, $fd) { echo "Websocket: close - {$fd}" . PHP_EOL; try { //下线处理 }catch (Exception $e){ $this->DeBugLog(array('title'=>'web:下线(异常)','fd'=>$fd,'err'=>$e)); } }); //------------------------- tcp -------------------------------------------------- tcp ------------------------------------------------------------------------------- //设置每个port的回调函数 $this->server->on('connect', function ($serv, $fd){ echo "Tcp 建立连接: {$fd}" . PHP_EOL; }); $this->server->on('receive', function ($serv, $fd, $from_id, $data) { try { //数据处理 }catch (Exception $e){ $this->DeBugLog(array('title'=>'tcp:上线(异常)','mac'=>$mac,'err'=>$e)); } }); $this->server->on('close', function ($serv, $fd) {
try { //下线处理 }catch (Exception $e){ $this->DeBugLog(array('title'=>'tcp:下线(异常)','fd'=>$fd,'err'=>$e)); } }); //启动服务 $this->serv->start(); }
//错误记录 function DeBugLog($content = []){ $pdo = $this->pool->get(); $pdo->exec("insert into *** (addtime,content) values ('".time()."','".json_encode($content,JSON_OBJECT_AS_ARRAY)."')"); $this->pool->put($pdo); } } new WebSocketServer();
<!--服务操作--> <script> var key = 'ç∂ßZ6Zu36Zu¿-.-^*^/@/sll'; var wsServer = 'wss://域名:端口'; var lockReconnect = false; //避免wss重复连接 var ws = null; // 判断当前浏览器是否支持WebSocket var wsUrl = wsServer; //https设置wss,http设置ws createWebSocket(wsUrl); //连接ws //验证浏览器是否支持 function createWebSocket(url) { try{ if('WebSocket' in window){ ws = new WebSocket(url); }else if('MozWebSocket' in window){ ws = new MozWebSocket(url); }else{ layui.use(['layer'],function(){ var layer = layui.layer; layer.alert("您的浏览器不支持websocket协议,建议使用新版谷歌、火狐等浏览器,请勿使用IE10以下浏览器,360浏览器请使用极速模式,不要使用兼容模式!"); }); } initEventHandle(); }catch(e){ reconnect(url); console.log(e); } } // 服务连接及数据处理 function initEventHandle() { ws.onclose = function () { reconnect(wsUrl); console.log("ws连接关闭!" + new Date().toUTCString()); alert('服务器连接已关闭'); }; ws.onerror = function () { reconnect(wsUrl); console.log("ws连接错误!"); alert('服务器失去连接'); }; ws.onopen = function () { //心跳检测重置 heartCheck.reset().start(); //获取用户id var id = $('#id').val(); //首次连接成功把id发送过去保存 ws.send(key+id); alert('已成功连接服务器'); //连接时间:new Date().toUTCString() 国际标准时间 GMT 时间 console.log("ws连接成功!" + new Date().toUTCString()); }; ws.onmessage = function (event) { //如果获取到消息,心跳检测重置 heartCheck.reset().start(); //拿到任何消息都说明当前连接是正常的 console.log("ws收到消息啦:" + event.data); if (event.data != key) { //上线:失败 if (event.data == 'error'){ alert('上线失败,请刷新页面重试'); } //消息发送失败 if (event.data == 'timeout'){ alert('发送失败'); } //接收好友发送的数据 if (event.data.substr(0,4) == 'send'){ //接收到的消息 //获取好友id var FriendId = $('#FriendId').val(); //获取内容 var info = event.data.substr(5); if (info != ''){ //解析json数据 var datas = $.parseJSON(info); console.log(datas); } // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function () { ws.close(); } } } // 重连 function reconnect(url) { if (lockReconnect) return; lockReconnect = true; setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多 createWebSocket(url); lockReconnect = false; }, 2000); } //心跳检测 var heartCheck = { timeout: 30000, //半分钟发一次心跳 timeoutObj: null, serverTimeoutObj: null, reset: function () { clearTimeout(this.timeoutObj); clearTimeout(this.serverTimeoutObj); return this; }, start: function () { var self = this; this.timeoutObj = setTimeout(function () { //这里发送一个心跳,后端收到后,返回一个心跳消息, //onmessage拿到返回的心跳就说明连接正常 ws.send(key); console.log(new Date().toUTCString()+':兄弟出来嗨!!!'); //如果超过一定时间还没重置,说明后端主动断开了 self.serverTimeoutObj = setTimeout(function () { //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次 ws.close(); }, self.timeout) }, this.timeout) } } //发送消息 ws.send('send-'+data.value.fid+':'+content); //发送消息 </script>