PHP+Laravel5.8+GatewayWorker实现即时文字聊天,文件互传功能(第二篇:GatewayWorker基类的配置与部署)
第二篇:GatewayWorker基类的配置与部署
本章节将演示:
1.代码的在服务器的部署过程
2.前端是如何将消息发送给后台的并进行处理的。
php端部署
1.上一章节我讲过,这个项目分为3端(客户端、通信端、服务端),他们的设计思路我在这里再次明确一下。
客户端(前端H5页面):负责展示和发送用户之间往来的消息内容。
通信端:负责接受和发送用户的消息内容。
服务端:负责处理发送消息和接收消息的业务逻辑等(如用户的聊天记录存入数据库、用户消息的撤回、用户发送违规图片的审核等一些功能,我们都在这里完成)。
2.前面章节我们在workerman->GatewayWorker手册官网下载的Linux系统快速开始的Demo
现在我把他复制到项目中的根目录中来。目录如下图所示:
3.开始填写配置(在配置之前请开发者一定先阅读手册一下内容,这很重要,因为后面有些坑都在这里)GatewayWorker->开发必读
现在打开 项目名\GatewayWorker\Applications\YourApp\start_gateway.php
根据你的业务需求修改 设置Gateway的协议为websocket因为H5客户端要用这个协议进行通讯。
我的域名有https证书所以我修改了配置项,相关文档请参考官方手册GatewayWorker手册->创建wss服务
$gateway = new Gateway("websocket://0.0.0.0:8282",$context);//这里的8282端口就是上一章节提前在服务器安全组中放行的。这个‘websocket:’协议是H5用的,这里的配置必须和H5前端的配置一致
// 服务注册地址
$gateway->registerAddress = '127.0.0.1:1238';//这个1238端口自己在服务器安全组中放行即可。代码里无需改动。
代码如下:
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';
// gateway 进程,这里使用Text协议,可以用telnet测试
/**
* https用我
*/
$context = array(
'ssl' => array(
'local_cert' => '/www/server/panel/vhost/cert/im_dev.liutong.pro/4539259_im.liutong.pro.pem', // 或者crt文件域名证书以及秘钥文件路径)
'local_pk' => '/www/server/panel/vhost/cert/im_dev.liutong.pro/4539259_im.liutong.pro.key',//(域名证书以及秘钥文件路径)
'verify_peer' => false
)
);
$gateway = new Gateway("websocket://0.0.0.0:8282",$context);//https用这个
// 设置transport开启ssl,websocket+ssl即wss
$gateway->transport = 'ssl';//https用这个
#****** https向上面一样配置 ******* 分割线 ****** http向下面$gatewayy一样配置 **************
/**
* http用我
*/
//$gateway = new Gateway("websocket://0.0.0.0:8282");//http用这个
// gateway名称,status方便查看
$gateway->name = 'YourAppGateway';
// gateway进程数
$gateway->count = 4;
// 本机ip,分布式部署时使用内网ip
$gateway->lanIp = '127.0.0.1';
// 内部通讯起始端口,假如$gateway->count=4,起始端口为4000
// 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口
$gateway->startPort = 2900;
// 服务注册地址
$gateway->registerAddress = '127.0.0.1:1238';
// 心跳间隔(单位:秒,注意这里服务端50s访问前端一次,前端50后返回后端一次,一来一回的时间应为 50s*2 = 100s)
$gateway->pingInterval = 50;
// 心跳数据(标识)
$gateway->pingData = '{"type":"ping"}';
/*
// 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调
$gateway->onConnect = function($connection)
{
$connection->onWebSocketConnect = function($connection , $http_header)
{
// 可以在这里判断连接来源是否合法,不合法就关掉连接
// $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
if($_SERVER['HTTP_ORIGIN'] != 'http://kedou.workerman.net')
{
$connection->close();
}
// onWebSocketConnect 里面$_GET $_SERVER是可用的
// var_dump($_GET, $_SERVER);
};
};
*/
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}
自此你的workerman服务端就配置好了。但是现在还不能用,我们现在开始编写前端代码。
前往Workerman服务端开启socket服务。
1.登录你的阿里云服务器控制台,使用cd命令进入项目根目录中,然后进入GatewayWorker目录中找到start.php使用php命令php start.php start
回车看到下图表示服务成功,
参考手册地址:GatewayWorker手册->启动与停止
前端H5页面编写
1.laravel配置一个路由用来访问呢前端页面。
2.页面的内容我们可以直接从GatewayWorker官方手册->ThinkPHP等框架结合->网站页面js片段中提取
网站页面js片段
{{--即时通讯业务逻辑--}}
<script>
/**
* 第一步:与socket建立链接
* 打开一个 web socket (以下链接方式任选一种即可)
* 参数解释:var ws = new WebSocket("参数A://参数B:参数C");
* 参数A:固定写法
ws:表示http连接
wss:表示https连接,有https证书的时候使用
* 参数B:要连接的地址。
可选参数:127.0.0.1 表示:本地地址
可选参数:48.662.156.869 表示:你的云服务器的公网ip (48.662.156.869这个ip是我假设的云服务器公网ip)
可选参数:im_dev.liutong.pro 表示:这个你的http域名 如(http://im_dev.liutong.pro)
可选参数:im.liutong.pro 表示:这个是你的https网址域名 如(https://im.liutong.pro)
* 参数C:端口号
端口号是可以自定义的(注意取值范围)。记得在服务器->实例安全组中进行放行。
端口号取值范围:端口不能大于65535,请确认端口没有被其它程序占用,否则启动会报错。如果端口小于1024,需要root权限运行GatewayWorker才能有权限监听,否则报错没有权限。(详情参见手册->《Gateway类的使用》->"初始化"->"ip")
注意:这里的端口号必须和你在 "项目名\GatewayWorker\Applications\YourApp\start_gateway.php" 中$gateway = new Gateway("websocket://0.0.0.0:8282");这段代码的端口号保持一致。
*/
// var ws = new WebSocket("ws://127.0.0.1:8282");//进行webSocket的几种方式(方式A:常规操作)
// var ws = new WebSocket("ws://47.104.249.193:8282");//进行webSocket的几种方式(方式B:使用本机公网ip进行连接)
// var ws = new WebSocket("ws://im_dev.liutong.pro:8282");//进行webSocket的几种方式(方式C:使用http://im_dev.liutog.pro的方式进行连接)
var ws = new WebSocket("wss://im.liutong.pro:8282");//进行webSocket的几种方式(方式D:使用HTTPS的方式进行连接,域名:https:im.liutong.pro)
//第二步:webSocketn事件 open连接建立时触发
ws.onopen = function() {
console.log("连接成功");
};
//第三步:webSocketn事件 onmessage客户端接收服务端数据时触发
ws.onmessage = function(evt) {
//console.log(evt);
//console.log(evt.data);
var data = JSON.parse(evt.data);//接受到服务器给我返回的JSON数据并进行解析
switch(data.type) {//验证链接类型(这个类型中的init值是官方定义的,我们无需改动,后续我们还会自定义一些type值。)
case 'init'://表示首次链接,GatewayWorker发现有页面发起连接时,将对应连接的client_id发给网站页面
//进行我方用户id与client_id进行绑定操作,这里我使用ajax请求我方自定义控制器进行处理用户绑定操作
bindingUser("{{Auth::user()->id}}",data.client_id);
break;
case 'binding_success'://用户绑定(用于当前用户刷新页面时,对我方用户id进行重新绑定)
console.log(data.msg + 'A');
break;
case 'content_success'://接受好友发来的用户消息
content_success(data);
break;
case 'ping'://心跳检测
ws.send(JSON.stringify({
'type':"pong"
}));
break;
case 'files_success'://接收好友base64文件
//console.log(data);
restoreFile(data);
break;
default:
console.log('绑定失败');
}
};
ws.onclose = function()
{
// 关闭 websocket
alert("连接已关闭...");
};
/**
* 绑定我方用户id方法
* @param user_id 我方用户id(数据来自“加载页面控制器”传递进来)
* @param client_id GatewayWorker发现有页面发起连接时,将对应连接的client_id发给网站页面,注意:此id是唯一的,每次重新链接都会发送一个新的client_id给我们
* 我方用户id与client_id的关系为一对多,及我方id为一个,可对应多个client_id。
* 通信时已我方用户id作为唯一标识!
*/
function bindingUser(user_id,client_id) {
$.ajax({
type : "POST", //提交方式
url : "{{route('im.bindingUser')}}",//路径
data : {
'_token':'{{csrf_token()}}'//laravel 防止ajax POST请求报419错误方法
,'user_id' : user_id
,'client_id' : client_id
},//数据,这里使用的是Json格式进行传输
success : function(result) {//返回数据根据结果进行相应的处理
var data = JSON.parse(result);//接受到服务器给我返回的JSON数据并进行解析
if (data.success) {
// console.log('绑定结果:' + data.msg);
} else {
alert('绑定失败22222');
}
}
});
}
/**
* 接收处理好友发来的数据
* @param data
*/
function content_success(data) {
console.log(data);
alert(1111);return false;
let class_name = '.chat_ul_' + data.addresser_id;//设定需要显示的class名称
let demo = "<li class=\"his_content chat\">\<n></n>" +
" <span>"+ data.msg +"</span>\<n></n>" +
" </li>";
$(class_name).append(demo);
}
/**
* 将base64文件进行还原展示
* @param data
*/
function restoreFile(data){
console.log(data);
switch (data.file_type) {
case 'mp3':
let chat_id = 'left';//对方发送li classe名
showSendFile(data.msg,data.file_type,data.file_id,data.audio_time,chat_id);
break;
case 'a':
//自定义业务逻辑
break;
case 'b':
//自定义业务逻辑
break;
case 'c':
//自定义业务逻辑
break;
default:
break
}
}
/**
* 发送文件到指定用户
* @param base64_data base64格式的文件
* @param type 指定的文件类型
*/
function sendFile_user(base64_data,file_type){
$.ajax({
url:"{{route('im.sendFile_user')}}",
type:"post",
data: {
'base64_data': base64_data,//base64文件
'file_type': file_type,//文件类型
'_token': '{{csrf_token()}}',//token过CSRF用的
'recipients':"{{$addressee['id']}}",//收件人id
'audio_time':audio_time,//录音时长(全局变量)
},
dataType:"json",
success:function(data){
if(data.success == true){
console.log(data);
alert("上传成功");
let audio_time_2 = data.audio_time;//录音时长
let file_id = data.file_id;//文件唯一id
let chat_id = 'right';//我方发送li classe名
showSendFile(base64_data,file_type,file_id,audio_time_2,chat_id);//显示发送的文件
}else{
alert("上传失败");
}
},
error:function(){
console.log("上传失败");
}
});
}
</script>
注意:1.首先我没把我写的js全拿出来,报错说方法找不到的,可以根据我的注释自己写业务逻辑,或者直接把方法名删掉都可以。
重点:这里有好朋友回报:ws或者wss链接超时、无法连接错误。这可能是你的服务器防火墙引起的,直接关闭防火墙简单粗暴,问题直接解决绝。(但是这个解决方案不推荐,请自行优化解决。)
如果你关闭了服务器防火墙依然无法解决,请你仔细阅读官方手册《GatewayWorker手册->-开发必读》章节的上半章内容中的6条注意事项,耐心排查。
总结
客户端(H5页面)、workerman服务端、php服务端 三者之间的关系
1.通俗易懂的讲法:我们把这其中的关系想象成‘邮差送信服务’
客户端=发件人/收件人。
workerman服务端 = 邮差,负责跑腿的。
php服务端 = 邮局,给邮差安排任务的地方。
发件人A想要发消息,他先去邮局告诉邮局发件人A的基本身份信息,在告诉邮局要邮寄的东西是什么,需要邮寄给谁。
邮局收到了发件人A的诉求。开始安排邮差去送信
邮差根据发件人A提供的信息去收件人的地址执行送信业务。
他们所扮演的角色就是这样。