/** * 获取当前时间戳带微秒 * @return float */ function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); }
/** *获取当前网站域名 */ function getUrlBefore() { $http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://'; return $http_type . $_SERVER['SERVER_NAME']; }
/** * 截取查询结果中的字段 * @param array $data 查询出来的结果集 * @param string $field 需要截取的字段 * @param string $copy_field 存入的新的字段名(不写默认存回原字段) * @param int $length 截取长度,默认10 * @param string $omit 省略的符号,默认 …… * @return array 返回截取成功的结果集 */ function cutField($data=array(),$field="",$copy_field="",$length=10,$omit="…"){ if (empty($data)){ return "请传入正确的数组"; } if (empty($field)){ return "请传入需要截取的字段"; } if (empty($copy_field)){ $copy_field=$field; } foreach ($data as &$v){ if (mb_strlen($v[$field],'utf-8')>$length){ $v[$copy_field]=mb_substr($v[$field],0,$length,'utf-8').$omit; } } return $data; }
/** * 生成随机码(默认六位) * 一般放在common中 * @param unknown $length 生成的随机码的长度 * @return string 返回随机码 */ function GetRand( $length = 6 ) { // 密码字符集,可任意添加你需要的字符 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $password = ''; for ( $i = 0; $i < $length; $i++ ) { // 使用 substr 截取$chars中的任意一位字符; $password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $password; }
/** * 颜色十六进制转化为rgb * 一般放在common中 */ function hColor2RGB($hexColor) { $color = str_replace('#', '', $hexColor); if (strlen($color) > 3) { $rgb = array( 'r' => hexdec(substr($color, 0, 2)), 'g' => hexdec(substr($color, 2, 2)), 'b' => hexdec(substr($color, 4, 2)) ); } else { $color = str_replace('#', '', $hexColor); $r = substr($color, 0, 1) . substr($color, 0, 1); $g = substr($color, 1, 1) . substr($color, 1, 1); $b = substr($color, 2, 1) . substr($color, 2, 1); $rgb = array( 'r' => hexdec($r), 'g' => hexdec($g), 'b' => hexdec($b) ); } return $rgb; }
这个错误一般是数据库断开后不再自动连接导致的。
需要将 config['break_reconnect'] 的值设置为 true, 才会开启断线重连。
一般在database.php 配置文件中:
//断线重连 'break_reconnect' => true,
但有些版本不在此文件中,可以从thinkphp/library/think/db/Connection.php找到,再找不到,请全局搜索!
最后,一般造成这样的原因可能是因为数据库连接次数过多,还是要优化代码,可以考虑添加缓存,减少数据库的连接。
1、环境确认,仅支持 Linux(2.3.32 以上内核)、FreeBSD、MacOS 三种操作系统,低版本 Linux 系统(如 CentOS 6)可以使用 RedHat 提供的 devtools 编译,参考文档, 在 Windows 平台,可使用 WSL(Windows Subsystem for Linux) 或 CygWin。
2、系统软件:
php-7.2 或更高版本 gcc-4.8 或更高版本 make autoconf
3、下载swoole源码(三选一)
https://github.com/swoole/swoole-src/releases https://pecl.php.net/package/swoole https://gitee.com/swoole/swoole/tags
4、下载源代码包后,使用root权限的终端进入源码目录,执行下面的命令进行编译和安装(当前教程安装的是4.8.0版本)
①进入下载的安装目录中:
cd swoole
②执行下面命令进入安装模块:
phpize && \
③设置configure中的安装目录
./configure --with-php-config=/www/server/php/72/bin/php-config
④最后通过以下命令执行安装:
make && make install
⑤执行完成后看看安装目录下有无swoole.so文件,php.ini中有无extension=swoole.so
#我成功安装后,swoole.so所在目录: /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/swoole.so
注:如果不知道第③步中的安装目录,则可以执行下面命令进行全局查找
find / -name php-config
PHP代码部分
//创建WebSocket Server对象,监听0.0.0.0:9502端口 $ws = new \Swoole\WebSocket\Server('0.0.0.0', 9502); //监听WebSocket连接打开事件 $ws->on('Open', function ($ws, $request) { $socket_id = $request->fd; $nowtime = date("Y-m-d H:i:s"); echo "[{$nowtime}] {$socket_id}连接成功\n"; $ws->push($socket_id, "swoole连接成功!\n"); }); //监听WebSocket消息事件 $ws->on('Message', function ($ws, $frame) { $socket_id = $frame->fd; $data = $frame->data; $nowtime = date("Y-m-d H:i:s"); echo "[{$nowtime}] 收到的客户端信息: {$data}\n"; $ws->push($socket_id, "收到的客户端信息: {$frame->data}"); }); //监听WebSocket连接关闭事件 $ws->on('Close', function ($ws, $fd) { $nowtime = date("Y-m-d H:i:s"); echo "[{$nowtime}] socket_id为{$fd}的用户已关闭连接\n"; }); $ws->start();
运行swoole
php swoole.php
客户端JS代码部分:
var wsServer = 'ws://服务器IP地址:9502'; var websocket = new WebSocket(wsServer); websocket.onopen = function (evt) { console.log("swoole连接成功!"); websocket.send('测试数据') }; websocket.onclose = function (evt) { console.log("swoole连接断开!"); }; websocket.onmessage = function (evt) { console.log('服务器返回的数据为: ' + evt.data); }; websocket.onerror = function (evt, e) { console.log('发生错误: ' + evt.data); };
注(踩的坑):
①检查防火墙9502端口是否开放
②以上教程在有ssl证书的情况下会连接失败
想在ssl证书下运行swoole,需要到swoole目录下重新安装程序:
①进入安装目录,执行下面命令进入安装模块:
执行下面命令进入安装模块:
phpize && \
②设置configure中的安装目录,并带有ssl安装,找不到目录参考上面find命令
./configure --enable-openssl --with-php-config=/www/server/php/72/bin/php-config
③清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件
make clean
④最后通过以下命令执行安装
make && make install
⑤查看结果是否包含openssl
php --ri swoole
看结果中是否存在下面这行,时间可能不一致,但前半部分相同
openssl => OpenSSL 1.0.2k-fips 26 Jan 2017
①客户端修改访问地址
var wsServer = 'ws://服务器IP地址:9502'; //上面改为下面,其他代码不动即可 var wsServer = 'wss://域名/自定义模块名(不要和代码中的功能模块名重复)'; //例如 var wsServer = 'wss://shx1024.top/websocket/';
②Nginx配置代理(其实是将指向https的服务重定向到http):
#在代理服务器上的nginx配置文件中的server中增加如下配置项 location /websocket/ { proxy_pass http://服务器IP地址:9502; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
③重新运行swoole
php swoole.php
①客户端修改访问地址
var wsServer = 'ws://服务器IP地址:9502'; //上面改为下面,其他代码不动即可 var wsServer = 'wss://服务器IP地址:9502';
②服务器端为WebSocket服务器增加ssl证书,这样就可以直接通过wss://来请求服务器了
//使用SSL必须在编译swoole时加入--enable-openssl选项 $server = new Swoole\WebSocket\Server("0.0.0.0", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); $server->set( [ 'ssl_cert_file' => '证书cert地址', 'ssl_key_file' => '证书key地址', ] ); //注:宝塔SSL证书所在目录:/www/server/panel/vhost/ssl/你的网站域名
③重新运行swoole
php swoole.php
直接引入此类文件调用即可:
class Jwt { //头部 private static $header = array( 'alg' => 'HS256', //生成signature的算法 'typ' => 'JWT' //类型 ); //使用HMAC生成信息摘要时所使用的密钥 private static $key = 'Shx123456panshi123456'; /** * 获取jwt token * @param array $payload jwt载荷 格式如下非必须 * [ * 'iss'=>'jwt_admin', //该JWT的签发者 * 'iat'=>time(), //签发时间 * 'exp'=>time()+7200, //过期时间 * 'nbf'=>time()+60, //该时间之前不接收处理该Token * 'user_id'=>md5(uniqid('JWT').time()) //该Token唯一标识 * ] * @return bool|string */ public static function getToken(array $payload) { $arr = [ 'iss' => 'shx', //该JWT的签发者 'iat' => time(), //签发时间 'exp' => time() + 3600 * 24 * 30, //过期时间 'nbf' => time(), //该时间之前不接收处理该Token 'user_id' => 0 //该Token唯一标识 ]; $payload = array_merge($arr, $payload); if (is_array($payload)) { $base64header = self::base64UrlEncode(json_encode(self::$header, JSON_UNESCAPED_UNICODE)); $base64payload = self::base64UrlEncode(json_encode($payload, JSON_UNESCAPED_UNICODE)); $token = $base64header . '.' . $base64payload . '.' . self::signature($base64header . '.' . $base64payload, self::$key, self::$header['alg']); return $token; } else { return false; } } /** * 验证token是否有效,默认验证exp,nbf,iat时间 * @param string $Token 需要验证的token * @return bool|string */ public static function verifyToken(string $Token) { $tokens = explode('.', $Token); if (count($tokens) != 3) return false; list($base64header, $base64payload, $sign) = $tokens; //获取jwt算法 $base64decodeheader = json_decode(self::base64UrlDecode($base64header), JSON_OBJECT_AS_ARRAY); if (empty($base64decodeheader['alg'])) return false; //签名验证 if (self::signature($base64header . '.' . $base64payload, self::$key, $base64decodeheader['alg']) !== $sign) return false; $payload = json_decode(self::base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY); //签发时间大于当前服务器时间验证失败 if (isset($payload['iat']) && $payload['iat'] > time()) return false; //过期时间小宇当前服务器时间验证失败 if (isset($payload['exp']) && $payload['exp'] < time()) return false; //该nbf时间之前不接收处理该Token if (isset($payload['nbf']) && $payload['nbf'] > time()) return false; return $payload; } /** * base64UrlEncode https://jwt.io/ 中base64UrlEncode编码实现 * @param string $input 需要编码的字符串 * @return string */ private static function base64UrlEncode(string $input) { return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); } /** * base64UrlEncode https://jwt.io/ 中base64UrlEncode解码实现 * @param string $input 需要解码的字符串 * @return bool|string */ private static function base64UrlDecode(string $input) { $remainder = strlen($input) % 4; if ($remainder) { $addlen = 4 - $remainder; $input .= str_repeat('=', $addlen); } return base64_decode(strtr($input, '-_', '+/')); } /** * HMACSHA256签名 https://jwt.io/ 中HMACSHA256签名实现 * @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload) * @param string $key * @param string $alg 算法方式 * @return mixed */ private static function signature(string $input, string $key, string $alg = 'HS256') { $alg_config = array( 'HS256' => 'sha256' ); return self::base64UrlEncode(hash_hmac($alg_config[$alg], $input, $key, true)); } }
THINK_PATH 框架系统目录 ROOT_PATH 框架应用根目录 APP_PATH 应用目录(默认为application) CONF_PATH 配置目录(默认为APP_PATH) LIB_PATH 系统类库目录(默认为 THINK_PATH.'library/') CORE_PATH 系统核心类库目录 (默认为 LIB_PATH.'think/') TRAIT_PATH 系统trait目录(默认为 LIB_PATH.'traits/') EXTEND_PATH 扩展类库目录(默认为 ROOT_PATH . 'extend/') VENDOR_PATH 第三方类库目录(默认为 ROOT_PATH . 'vendor/') RUNTIME_PATH 应用运行时目录(默认为 ROOT_PATH.'runtime/') LOG_PATH 应用日志目录 (默认为 RUNTIME_PATH.'log/') CACHE_PATH 项目模板缓存目录(默认为 RUNTIME_PATH.'cache/') TEMP_PATH 应用缓存目录(默认为 RUNTIME_PATH.'temp/')
/** * get请求 * @param $url 地址 * @return bool|string */ function get_url($url) { $curl = curl_init(); // header传送格式 $headers = array( "token:1111111111111", "over_time:22222222222", ); //设置访问的url地址 curl_setopt($curl, CURLOPT_URL, $url); // 不验证SSL curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 将 curl_exec()获取的信息以文件流的形式返回,而不是直接输出。 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 添加头信息 //curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); //添加cookie信息 curl_setopt($curl, CURLOPT_COOKIE, 'cookie_name=cookie_value;'); // 执行 $result = curl_exec($curl); //关闭连接 curl_close($curl); return $result; }
/** * POST请求 * @param string $url 地址 * @param string $data 提交的数据 * @return string 返回结果 */ function post_url($url, $data) { $curl = curl_init(); // 启动一个CURL会话 curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); // 对认证证书来源的检查 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); // 从证书中检查SSL加密算法是否存在 curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)'); // 模拟用户使用的浏览器 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转 //curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求 curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包x curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制 防止死循环 curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回 $tmpInfo = curl_exec($curl); // 执行操作 if (curl_errno($curl)) { echo 'Errno' . curl_error($curl);//捕抓异常 } curl_close($curl); // 关闭CURL会话 return $tmpInfo; // 返回数据 }