websocket chat
1、常见laravel项目
composer create-project laravel/laravel
2、安装predis和guzzlehttp
composer require guzzlehttp/guzzle
composer require predis/predis
3 、创建WebSocketServerCommand
argument('action');
switch ($arg) {
case 'start':
$this->info('swoole server started');
$this->start();
break;
case 'stop':
$this->info('swoole server stoped');
$this->stop();
break;
case 'restart':
$this->info('swoole server restarted');
$this->restart();
break;
}
}
/**
* 启动Swoole.
*/
private function start()
{
Redis::del($this->userListCacheKey);
$this->ws = new swoole_websocket_server('0.0.0.0', config('websocket.port'));
//监听WebSocket连接打开事件
$this->ws->on('open', function ($ws, $request) {
$userId = $request->fd;
Redis::hset($this->userListCacheKey, $request->fd, $userId);
$this->info("client $request->fd is connected\n");
$this->pushMessage($request->fd, "welcome!client $request->fd connected!\n");
});
//监听WebSocket消息事件
$this->ws->on('message', function ($ws, $frame) {
$this->receiveMessageHandler($frame->fd, $frame->data);
});
//监听WebSocket主动推送消息事件
$this->ws->on('request', function ($request, $response) {
$this->messageHandler($request->post);
Log::info(\GuzzleHttp\json_encode($request->post));
$response->end('ok');
});
//监听WebSocket连接关闭事件
$this->ws->on('close', function ($ws, $fd) {
if (Redis::hexists($this->userListCacheKey, strval($fd))) {
Redis::hdel($this->userListCacheKey, [strval($fd)]);
}
$this->info("client $fd is close\n");
});
$this->ws->start();
}
private function stop()
{
exec('ps -ef | grep \'swoole start\'', $result);
if ($result) {
foreach ($result as $item) {
$process = explode(' ', preg_replace('#\s{2,}#', ' ', $item));
if (isset($process[1])) {
$command = 'exec kill ' . $process[1];
$this->info($command);
exec($command);
}
}
}
$command = 'rm -rf ' . base_path('storage/framework/cache/*');
$this->info($command);
exec($command);
}
private function restart()
{
$this->stop();
$this->start();
}
private function receiveMessageHandler($fd, $message)
{
if ($message === 'ping') {
@$this->ws->push($fd, 'ping');
} else {
if (json_decode($message)) {
$message = json_decode($message);
if ($message->to > 0) {
$this->pushMessage($message->to, $message->msg, $fd);
} else {
$this->info("receive client $fd message:" . $message->to);
$this->pushMessageToAll($message->msg);
}
} else {
$this->pushMessageToAll($message);
}
}
}
private function messageHandler($data)
{
if (isset($data['type']) && in_array($data['type'], ['single', 'broadcast', 'group']) && isset($data['msg']) && $data['msg']) {
if ('single' == $data['type'] && isset($data['fd']) && is_int((int)$data['fd']) && $data['fd'] > 0) {
$this->pushMessage((int)$data['fd'], $data['msg']);
}
if ('broadcast' == $data['type']) {
$this->pushMessageToAll($data['msg']);
}
if ('group' == $data['type'] && isset($data['groupId']) && $data['groupId']) {
$fdList = Redis::HGETALL($this->userListCacheKey . "_" . $data['groupId']);
if ($fdList) {
foreach ($fdList as $fd) {
$this->pushMessage($fd, $data['msg']);
}
}
}
}
$this->info("send to " . $data['type'] . " messages: " . $data['msg']);
}
private function pushMessageToAll($message)
{
$fdList = Redis::HGETALL($this->userListCacheKey);
if ($fdList) {
foreach ($fdList as $fd) {
$this->info("send to " . $fd . " messages: " . $message);
$this->pushMessage($fd, $message);
}
}
}
private function pushMessage($fd, $message, $from = 'sys', $type = 'broadcast')
{
$fd = (int)$fd;
if ($this->ws->isEstablished($fd)) {
@$this->ws->push($fd, json_encode([
'type' => $type,
'from' => $from,
'msg' => $message,
]));
}
}
}
4、设置推送接口路由和方法
Route::middleware('throttle:60,1')->group(function (){
Route::post('/message', 'MessagePushController@message');
});
public function message(Request $request)
{
$this->validate($request, [
'msg' => 'string|required',
'type' => 'string|required|in:single,broadcast,group',
'fd' => 'nullable|required_if:type,single|integer',
'groupId' => 'nullable|required_if:type,group|integer',
]);
$client = new Client(['base_uri' => 'http://127.0.0.1:'.config('websocket.port')]);
$response = $client->request('POST', '/post', [
'form_params' => $request->all()
]);
return new JsonResponse(['status' => $response->getStatusCode() == 200 ? 'success' : 'fail']);
}
5、创建聊天页面和路由
Route::get('/chat', function () {
return view('chatRoom');
});
Laravel
websocket chat
6、websocket操作命令
php artisan swoole start //启动
php artisan swoole stop //停止
php artisan swoole restart //重启
7、测试截图
8、项目地址https://gitee.com/ljt_2010/chat.git