环境说明:(Linux + centos + php + nginx + mysql + redis)
一、安装swoole扩展及redis
我用宝塔简单
二、服务端代码
因为Swoole只能运行在PHP CLI模式下,所以通过Laravel Command来实现
1.生成Command类
项目根目录下执行:php artisan make:command SwooleServer
2.编写webSocket Server逻辑
SwooleServer.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Request;
class SwooleServer extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:swoole_test';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
//创建server
$server = new \Swoole\WebSocket\Server("0.0.0.0",9502);
//监听连接进入事件
$server->on('Connect', function ($server, $fd) {
$userid = Request::input('userid');
echo "Client: Connect456-$userid.\n";
});
//连接成功回调
$server->on('open', function (\Swoole\WebSocket\Server $server, $request) {
$this->info($request->fd . '链接成功');
$userid = Request::input('userid');
echo "Client: Connect-$userid.\n";
});
//收到消息回调
$server->on('message', function (\Swoole\WebSocket\Server $server, $frame) {
$this->info($frame->data . 'msg');
$msg = json_decode($frame->data,true);
$fd = $frame->fd;
if(isset($msg['type'])){
$type = $msg['type'];
if($type == 1){
//绑定用户信息和fd信息
$from_userid = $msg['from_userid'];
$cacheKey = "socket_user:id_".$from_userid;
$cacheFdKey = "socket_fd:id_".$fd;
$time = 86400;
$data = [
'userid' => $from_userid,
'fd' => $fd
];
Redis::setex($cacheKey,$time,json_encode($data));
Redis::setex($cacheFdKey,$time,json_encode($data));
$to_userinfo = Redis::get($cacheKey);
if($to_userinfo){
$server->push($fd,$from_userid.'连接成功,可以开始聊天啦');
}else{
$server->push($fd,$fd.'连接失败');
}
}else if($type == 2){
//发送消息
$content = $msg['data'];
$from_userid = $msg['from_userid'];
$this->info($from_userid . 'msg');
$to_userids = $msg['to_userids'];//1,2,3
$to_userids_arr = explode(',',$to_userids);
foreach ($to_userids_arr as $k => $v){
$cacheKey = "socket_user:id_".$v;
$to_userinfo = Redis::get($cacheKey);
$to_userinfo = json_decode($to_userinfo,true);
if(!$to_userinfo){
$this->info($content.'对方不在线');
$server->push($frame->fd,$to_userinfo['userid'].'对方不在线');
}else{
$this->info($content.'对方在线');
$to_fd = $to_userinfo['fd'];
//推送信息给指定用户
$server->push($to_fd,$content);
}
}
}else if($type == 3){
//发送心跳包
$this->info('心跳包');
}
}else{
//连接失败
$server->push($frame->fd,'连接失败,退出重进');
}
});
//关闭链接回调
$server->on('close', function ($ser, $fd) {
//断开连接删除用户在线状态
$cache = Redis::get("socket_fd:id_".$fd);
$caches = json_encode($cache,true);
$cacheKey = "socket_user:id_".$caches['userid'];
$cacheFdKey = "socket_fd:id_".$caches['fd'];
Redis::del($cacheKey);
Redis::del($cacheFdKey);
$this->info($fd . '断开链接');
});
$server->start();
}
}
3、服务器放行端口号 宝塔防火墙放行端口号
4、在项目根目录运行以下命令 启动服务 这样就开始监听该端口号了
php artisan command:swoole_test
三、客户端实现
index.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>
<h3>demo 1</h3>
<div style="width:300px;margin:0 auto;border:1px solid #ccc;">
<div id="content" style="overflow:auto;height:500px;"></div>
<hr />
<div style="height:130px;background:white;">
发送人userid:<input type="text" class="form-control" id="from_userid" placeholder="发送人id" value="{{$userid}}"></br></br>
接收人userid:<input type="text" class="form-control" id="to_userids" placeholder="接收人id"></br></br>
发送内容:<input type="text" class="form-control" id="message" placeholder="请输入内容"></br></br>
<button type="button" class="btn btn-primary" onclick="sendMessage()">点击发送</button></br></br>
</div>
</div>
</body>
</html>
<script type="text/javascript">
var arr = {};
if(window.WebSocket){
//创建连接
// 端口和ip地址对应不要写错
// var webSocket = new WebSocket("ws://0.0.0.0:9501");
//https请求
// var wss = "wss://zsy.hzyxhfp.com/wss:9501";
//http请求
var wss = "ws://你的ip:9502";
var webSocket = new WebSocket(wss);
// 握手成功
webSocket.onopen = function (event) {
console.log('webSocket 链接成功');
if (webSocket.readyState === 1) {
var from_userid = document.getElementById('from_userid').value;
var arr = {};
arr['from_userid'] = from_userid;
arr['type'] = 1;//1:第一次连接,绑定用户信息
var arrJson = JSON.stringify(arr);
webSocket.send(arrJson);
// console.log("connected readyState");
}
};
//收到服务端消息回调
webSocket.onmessage = function (event) {
console.log(webSocket,'onmessage');
var content = document.getElementById('content');
content.innerHTML = content.innerHTML.concat('<p style="margin-left:20px;height:20px;line-height:20px;">'+event.data+'</p>');
}
//监听断开连接
webSocket.onclose = function(event) {
console.log(webSocket,'onclose');
var content = document.getElementById('content');
content.innerHTML = content.innerHTML.concat('<p style="margin-left:20px;height:20px;line-height:20px;">您已断开连接</p>');
//重新连接
}
//发送消息
var sendMessage = function(){
var data = document.getElementById('message').value;
arr['data'] = data;
arr['from_userid'] = document.getElementById('from_userid').value;
arr['to_userids'] = document.getElementById('to_userids').value;
arr['type'] = 2;//2:发送信息
var myJSON = JSON.stringify(arr);
console.log(webSocket);
if (webSocket.readyState === 1) {
//当前用户在线
webSocket.send(myJSON);
// webSocket.send(data);
var content = document.getElementById('content');
content.innerHTML = content.innerHTML.concat('<p style="margin-left:20px;height:20px;line-height:20px;color: blue;">'+data+'</p>');
}else{
//当前用户不在线
console.log('连接失败,请刷新重进');
}
}
//每隔5秒发送一个心跳包
setInterval(function(){
console.log('setInterval')
var arr = {};
arr['type'] = 3;
var myJSON = JSON.stringify(arr);
webSocket.send(myJSON)
},5000);
}else{
console.log("您的浏览器不支持WebSocket");
}
</script>
四、随便写个路由访问
Route::get('/swoole_index','TestController@index');
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class TestController extends Controller
{
public function index(Request $request){
$user_id = $request->get('user_id');
return view("index", ['userid'=>$user_id]);
}
}
打开: 你的域名/swoole_index?user_id=1
五、完成