app/Console/Commands/Swoole.php
<?php
namespace App\Console\Commands;
use App\Handlers\SwooleHandler;
use Illuminate\Console\Command;
class Swoole extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'swoole {action}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'swoole socket';
protected $ws;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$action = $this->argument('action');
$this->ws = new \swoole_websocket_server( "http://" .env('SOCKET_DOMAIN'), env('SOCKET_PORT') );
$this->ws->set(['worker_num' => env('SOCKET_WORKER_NUM')]);
switch ($action) {
case 'start':
$handler = new SwooleHandler();
$this->ws->on('Open', [$handler, 'onOpen']);
$this->ws->on('Message', [$handler, 'onMessage']);
$this->ws->on('Close', [$handler, 'onClose']);
$this->ws->start();
break;
case 'reload':
$this->ws->reload();
break;
case 'stop':
$this->ws->stop();
break;
}
}
}
app/Handlers/SwooleHandler.php
<?php
namespace App\Handlers;
use LRedis;
use App\Models\Message;
class SwooleHandler
{
public function onOpen($ws, $request)
{
$user_id = $request->get['user_id'];
echo "client - {$user_id} is opened\n";
LRedis::hSet('FRONT_USERS', $user_id, $request->fd);
}
public function onMessage($ws, $frame)
{
$user_id = $frame->data;
$fd = LRedis::hGet('FRONT_USERS', $user_id);
echo "client - {$fd} is send\n";
$num = Message::query()->where('user_id',$user_id)->count();
$ws->push($fd, $num);
}
public function onClose($ws, $fd)
{
echo "client - {$fd} is closed\n";
$all = LRedis::hGetAll('FRONT_USERS');
foreach ($all as $key => $val) {
if ($fd == LRedis::hGet('FRONT_USERS', $key)) {
LRedis::hDel('FRONT_USERS', $key);
echo "del {$key}\n";
}
}
}
}
app/Events/MessageEvent.php
<?php
namespace App\Events;
use App\Models\Message;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class MessageEvent extends Event implements ShouldBroadcast
{
use SerializesModels;
public $user_id;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($from_user_id, $user_id, $content)
{
$this->model = new Message();
$this->from_user_id = $from_user_id;
$this->user_id = $user_id;
$this->content = $content;
}
/**
* Get the channels the event should be broadcast on.
*
* @return array
*/
public function broadcastOn()
{
return ['message-channel'];
}
}
app/Listeners/MessageListener.php
<?php
namespace App\Listeners;
use App\Events\MessageEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class MessageListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param MessageEvent $event
* @return void
*/
public function handle(MessageEvent $event)
{
$model = $event->model;
$model->from_user_id = $event->from_user_id;
$model->user_id = $event->user_id;
$model->content = $event->content;
$model->save();
}
}
app/Providers/EventServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* 事件侦听器映射到应用程序
*
* @var array
*/
protected $listen = [
// 站内信事件监听
'App\Events\MessageEvent' => [
'App\Listeners\MessageListener',
],
];
/**
* Register any other events for your application.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
}
}
前台模板
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>OA 首页</title>
</head>
<body>
<div style="width: 300px;margin: 100px auto;">
<h1>Hi, {{auth('front')->user()->name}}</h1>
<a href="{{url('auth/logout')}}">Logout</a>
</div>
<script type="text/javascript">
var exampleSocket = new WebSocket("ws://{{env('SOCKET_DOMAIN')}}:{{env('SOCKET_PORT')}}?user_id={{auth('front')->user()->id}}");
exampleSocket.onopen = function (event) {
exampleSocket.send({{auth('front')->user()->id}});
};
exampleSocket.onmessage = function (event) {
console.log(event.data);
}
</script>
</body>
</html>
运行 php artisan swoole start,前台页面第一次加载的时候,websocket 链接的 onopen, onmessage, onclose 都是可以正常运行的。
Mac-Pro:xxx.com xxx$ php artisan swoole start
client - 22 is opened
client - 1 is send
client - 1 is closed
但是执行 \Illuminate\Support\Facades\Event::fire(new \App\Events\MessageEvent($from_user_id, $user_id, $content)) 触发事件的时候,前台页面 onmessage 里面不能输出最新的数据。。求大神指教。谢谢!
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.