本文有相对完整的集成代码和前端测试代码,以及服务器启动注意事项
一、后端(laravel5.5)
1、composer安装Workerman
composer require workerman/workerman
2、生成执行命令,执行后会在App\Console\Command文件夹下生成一个Workerman.php的文件
php artisan make:command Workerman
3、打开Workerman.php的文件,并修改代码,如下:
argument('action');
$argv[1] = $arg;
$argv[2] = $this->option('daemonize') ? '-d' : '';//该参数是以daemon(守护进程)方式启动
global $text_worker;
// 创建一个Worker监听9991端口,使用websocket协议通讯
$text_worker = new Worker("websocket://0.0.0.0:9991");
$text_worker->uidConnections = array();//在线用户连接对象
$text_worker->uidInfo = array();//在线用户的用户信息
// 启动4个进程对外提供服务
$text_worker->count = 1;
//当启动workerman的时候 触发此方法
$text_worker->onWorkerStart =function(){
//监听一个内部端口,用来接收服务器的消息,转发给浏览器
$inner_text_worker = new Worker('text://127.0.0.1:5678');
$inner_text_worker->onMessage = function($connection_admin, $data)
{
global $text_worker;
// $data数组格式,里面有uid,表示向那个uid的页面推送数据
$buffer = json_decode($data, true);
//var_dump($buffer);
$to_uid = $buffer['to_uid'];
//var_dump($to_uid);
// 通过workerman,向uid的页面推送数据
if(isset($text_worker->uidConnections[$to_uid])){
$connection = $text_worker->uidConnections[$to_uid];
$ret = $connection->send($data);
} else {
var_dump($to_uid . ': not define');
$ret = false;
}
// 返回推送结果
$connection_admin->send($ret ? 'ok' : 'fail');
};
$inner_text_worker->listen();
// 进程启动后设置一个每10秒运行一次的定时器
Timer::add(10, function(){
global $text_worker;
$time_now = time();
foreach($text_worker->connections as $connection) {
// 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
if (empty($connection->lastMessageTime)) {
$connection->lastMessageTime = $time_now;
continue;
}
// 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
var_dump("delete:" . $connection->uid);
unset($text_worker->uidConnections["{$connection->uid}"]);
$connection->close();
}
}
});
};
//当浏览器连接的时候触发此函数
$text_worker->onConnect = function($connection) {
};
//向用户发送信息的时候触发
//$connection 当前连接的人的信息 $data 发送的数据
$text_worker->onMessage = function($connection,$data){
// 给connection临时设置一个lastMessageTime属性,用来记录上次收到消息的时间
$connection->lastMessageTime = time();
// 其它业务逻辑...
$data = json_decode($data, true);
if($data['type']=='login') {
$this->create_uid($connection,$data);
}
if($data['type']=='send_message'){
$this->send_message($connection,$data);
}
};
//浏览器断开链接的时候触发
$text_worker->onClose = function($connection){};
Worker::runAll();
}
//创建uid方法
public function create_uid($connection,$data){
global $text_worker;
$connection->uid = $data['uid'];
//保存用户的uid
$text_worker->uidConnections["{$connection->uid}"] = $connection;
//向自己的浏览器返回创建成功的信息
$connection->send(json_encode([
"uid"=>$connection->uid,
"msgType"=>'text',
"content"=>'聊天创建成功,您可以开发发送消息了'
]));
}
public function send_message($connection,$data) {
global $text_worker;
if(isset($data['to_uid'])){
if(isset($text_worker->uidConnections["{$data['to_uid']}"])){
$to_connection=$text_worker->uidConnections["{$data['to_uid']}"];
$to_connection->send(json_encode([
"uid"=>$data['uid'],
"msgType"=>$data['msgType'],
"content"=>$data['message']
]));
}
}
}
}
4、修改App\Console下面的Kernel.php文件,如下:
protected $commands = [
Commands\Workerman::class,
];
5、启动Workerman服务,启动命令如下:
php artisan Workerman start
出现上图所示,则表示启动成功
二、前端如何连接Workerman服务(websocket)服务
1、新建两个html页面用于测试,当然,也可以直接在浏览器打开F12在console里面测试,我这里有写好的页面,直接换个ws地址就能用,支持发送文字,图片
37websoket.html
{{http://blog.csdn.net/wwsseeddrrff/article/details/item.content}}
{{http://blog.csdn.net/wwsseeddrrff/article/details/item.content}}
发消息
html,body{margin: 0;padding:0}
#app{height: 100vh;background: #F5F5F5}
.left-box {display: flex;flex-shrink: 0;margin-bottom: 10px;margin-top: 10px;}
.right-box {display: flex;justify-content: flex-end;width: 100%;margin-bottom: 10px;margin-top: 10px;}
.msg-box-l{background: #fff;max-width: 80%;padding: 8px;border-top-right-radius: 10px;border-bottom-right-radius: 10px;border-bottom-left-radius: 10px;margin-left: 10px;margin-top: 10px;box-shadow: 0 2px 11px -7px rgba(2,66,58,.5)}
.msg-box-r{background: #95EC69;max-width: 80%;padding: 8px;border-top-left-radius: 10px;border-bottom-right-radius: 10px;border-bottom-left-radius: 10px;margin-right: 10px;margin-top: 10px;box-shadow: 0 2px 11px -7px rgba(2,66,58,.5)}
.msg-text-l{color: #000;word-break: break-all;}
.msg-text-r{color: #000;word-break: break-all;}
.ta{resize: none;width: 100%;height: 100px;position: fixed;bottom: 20px;}
.ta1{resize: none;width: 70%;margin-left:5%;height: 100px;position: fixed;bottom: 20px;}
.r-b{display: flex;align-items: flex-end;flex-direction: column;}
.send-btn{background: #4287FD;color: #fff;padding: 5px 10px;margin: 10px;}
.input-file {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
38websoket.html
{{http://blog.csdn.net/wwsseeddrrff/article/details/item.content}}
{{http://blog.csdn.net/wwsseeddrrff/article/details/item.content}}
发消息
html,body{margin: 0;padding:0}
#app{height: 100vh;background: #F5F5F5}
.left-box {display: flex;flex-shrink: 0;margin-bottom: 10px;margin-top: 10px;}
.right-box {display: flex;justify-content: flex-end;width: 100%;margin-bottom: 10px;margin-top: 10px;}
.msg-box-l{background: #fff;max-width: 80%;padding: 8px;border-top-right-radius: 10px;border-bottom-right-radius: 10px;border-bottom-left-radius: 10px;margin-left: 10px;margin-top: 10px;box-shadow: 0 2px 11px -7px rgba(2,66,58,.5)}
.msg-box-r{background: #95EC69;max-width: 80%;padding: 8px;border-top-left-radius: 10px;border-bottom-right-radius: 10px;border-bottom-left-radius: 10px;margin-right: 10px;margin-top: 10px;box-shadow: 0 2px 11px -7px rgba(2,66,58,.5)}
.msg-text-l{color: #000;word-break: break-all;}
.msg-text-r{color: #000;word-break: break-all;}
.ta{resize: none;width: 100%;height: 100px;position: fixed;bottom: 20px;}
.ta1{resize: none;width: 70%;margin-left:5%;height: 100px;position: fixed;bottom: 20px;}
.r-b{display: flex;align-items: flex-end;flex-direction: column;}
.send-btn{background: #4287FD;color: #fff;padding: 5px 10px;margin: 10px;}
.input-file {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
分别保存为两个html文件,两个都在浏览器打开,页面上出现 “聊天创建成功,您可以开发发送消息了”,则说明连接成功,可以正常发消息了
至此调试完成!
三、在服务器运行Workerman服务
我们本地调试完成后,最终需要上传到服务器上去运行,例如:Centos,服务上也需要composer安装Workerman,方式和最开头一样,我直接说说如何启动
在服务器上启动Workerman和在本地有一定的区别,因为通常我们希望在服务器上,Websocket能一直处于启动状态,所以,上述所说的启动命令,要稍微改一下,即在命令后加上:–daemonize(守护进程启动)
php artisan Workerman start --daemonize (关掉终端,服务不会停止)
php artisan Workerman stop --daemonize (服务停止)
php artisan Workerman start (关掉终端,服务会停止)
php artisan Workerman stop (服务停止)
至此,laravel集成Workerman搭建websocket服务已完成
本文参考文章:laravel与workerman结合_laravel workerman-CSDN博客