Hyperf2.1框架基于websocket协议实现群聊,聊天功能
功能是聊天,群聊基于用户的聊天,所以我直接在网站的用户中心页面添加这个功能
环境还是那套,需要环境安装教程的可以看我之前的文章。话不多websocket是什么大家也都在百度看到过,我就不过多介绍,我直接上干货,开始做聊天功能步骤流程。
功能涉及知识点
websocket协议
redis集合
功能需要的依赖组件
安装websocket-server组件
composer require hyperf/websocket-server
redis组件包
这个默认框架已经安装,自己安装本地安装redis服务,或者远程别的服务器
添加配置文件 /config/autoload/server.php
[
'name' => 'ws',
'type' => Server::SERVER_WEBSOCKET,
'host' => '0.0.0.0',
'port' => 9502,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_HAND_SHAKE => [Hyperf\WebSocketServer\Server::class, 'onHandShake'],
Event::ON_MESSAGE => [Hyperf\WebSocketServer\Server::class, 'onMessage'],
Event::ON_CLOSE => [Hyperf\WebSocketServer\Server::class, 'onClose'],
],
]
启动hyperf框架
[[email protected] ~]# cd /home/www/hyperf-skeleton/
[[email protected] hyperf-skeleton]# php bin/hyperf.php start
...
[INFO] WebSocket Server listening at 0.0.0.0:9502
[INFO] HTTP Server listening at 0.0.0.0:9501
...
这就说明websocket启动了, 其他省略
添加websocket路由
//ws
Router::addServer('ws', function () {
Router::get('/ws', 'App\Controller\WebSocketController');
});
websocket服务端控制器代码
/app/Controller/WebSocketController.php
<?php
declare(strict_types=1);
namespace App\Controller;
use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server as WebSocketServer;
use Hyperf\DbConnection\Db;
use Hyperf\Utils\ApplicationContext;
class WebSocketController implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{
public function onMessage($server, Frame $frame): void
{
$data = json_decode($frame->data,true);
if(isset($data['type']) && $data['type'] == 'ping') {
$server->push($frame->fd, 'Ping');//心跳
}else{
//心跳刷新缓存
$redis = ApplicationContext::getContainer()->get(\Hyperf\Redis\Redis::class);
//获取所有的客户端id
$fdList = $redis->sMembers('websocket_zongscan');
//如果当前客户端在客户端集合中,就刷新
if (in_array($frame->fd, $fdList)) {
$redis->sAdd('websocket_zongscan', $frame->fd);
$redis->expire('websocket_zongscan', 3600);
}
//绑定用户信息
$user = Db::table('user')->where('user_id',$data['uid'])->first();
$data['username'] = $user->username;
$data['face'] = $user->face;
if(count($fdList)) {
foreach ($fdList as $k => $v) {
$server->push(intval($v),json_encode($data));
echo "线程:$frame->fd 向线程 $v 发送信息\\n";
}
}
}
}
public function onClose($server, int $fd, int $reactorId): void
{
//删掉客户端id
$redis = ApplicationContext::getContainer()->get(\Hyperf\Redis\Redis::class);
//移除集合中指定的value
$redis->sRem('websocket_zongscan', $fd);
var_dump('closed');
}
public function onOpen($server, Request $request): void
{
//保存客户端id
$redis = ApplicationContext::getContainer()->get(\Hyperf\Redis\Redis::class);
$redis->sAdd('websocket_zongscan', $request->fd);
$redis->expire('websocket_zongscan', 3600);
$server->push($request->fd, 'Opened');
}
}
前端代码,我这里是写在用户中心需要登录
/storage/view/home/user/center.blade.php
<div class="col-md-4 column">
<div id="talk-window" class="talk-window">
<div class="tw-title">本站技术讨论 - 聊天窗口<span class="close-tw-window">×</span></div>
<div class="tw-content">
<div class="talk-list-div"><a href="javascript:;" id="bottom-link"></a></div>
<div class="talk-operate-div">
<div class="talk-operate-btn-list">可添加emoji表情等附加功能按钮</div>
<div class="talk-operate-textarea"><textarea id="msg-text" placeholder="老铁,请打字..."></textarea></div>
<div class="send-btn">
<button id="sendBtn" class="btn btn-primary button" style="margin-right: 10px;">发送</button>
</div>
</div>
</div>
</div>
<div class="h-clear"></div>
</div>
其他的就省略了
js
@endsection
@section('js')
<script>
var replyData = [
'[自动回复]您好,我现在有事不在,一会再和您联系',
'[自动回复]因为工作的关系,曾面对无数好友呼叫不能回应,最痛苦的事莫过于此。如果给我一次机会,我要说三个字:我离开。如果一定要给这三个字加个期限,我希望是:一会儿!',
'[自动回复]这次是我不经意的离开,却造成我们失之交臂的遗憾。于是我忘记了吃饭,无法再安睡,于是我不甘寂寞急忙归来。',
'[自动回复]您所呼叫的用户尚在厕所中,稍后请拿厕纸给他!',
'[自动回复]你所呼叫的用户正在系统整理,请稍后再呼。',
'[自动回复]你好,我去杀几个人,很快回来。',
'[自动回复]对不起,你所联系的用户因为太过帅气,已被TC公司删除。详情请咨询110,谢谢,再见。',
'[自动回复]走开一下,如果3分钟之内还没回请不要发飙,因为我正在对着设相头摆POSE!',
'[自动回复]有事留下你的真实姓名,家庭住址,电话号码,你的银行账号和密码、我会和你联系!?',
'[自动回复]计算机正在处理你的信息,请稍候,如果长时间没有响应,请重新启动计算机!',
];
function reply() {
var num = parseInt(Math.random() * 10);
var str = '<div class="msg-div"><div style="text-align: center;">'+ getCurrentDate(new Date()) +'</div>';
str += '<div class="other-msg-div">';
str += '<div class="header-img-div">';
str += '<img src="/upload/photo/4.jpg" />';
str += '</div>';
str += '<div class="msg-content">';
str += replyData[num];
str += '</div>';
str += '</div>';
str += '</div>';
//$("#bottom-link").before(str);
$("#bottom-link").append(str);
scrollHeight = $('.talk-list-div').prop("scrollHeight");
$('.talk-list-div').scrollTop(scrollHeight, 200);
}
function getCurrentDate(date){
var y = date.getFullYear();
var m = date.getMonth()+1;
var d = date.getDate();
var h = date.getHours();
var min = date.getMinutes();
var s = date.getSeconds();
var str=y+'年'+(m<10?('0'+m):m)+'月'+(d<10?('0'+d):d)+'日 '+(h<10?('0'+h):h)+':'+(min<10?('0'+min):min)+':'+(s<10?('0'+s):s);
return str;
}
/*###################*/
$(function() {
//=========================初始化====================================
var $window = $(window);
var $messageArea = $('#bottom-link'); // 消息显示的区域
var $inputArea = $('#msg-text'); // 输入消息的区域
$inputArea.focus(); // 首先聚焦到输入框
var connected = false; // 用来判断是否连接的标志
//====================webSocket连接======================
// 创建一个webSocket连接
var socket = new WebSocket('ws://'+window.location.host+'/ws');
// 当webSocket连接成功的回调函数
socket.onopen = function () {
console.log("webSocket open");
connected = true;
};
// 断开webSocket连接的回调函数
socket.onclose = function () {
console.log("webSocket close");
connected = false;
};
window.setInterval(function () { //每隔5秒钟发送一次心跳,避免websocket连接因超时而自动断开
socket.send('{"type":"ping"}');
}, 5000);
//=======================接收消息并显示===========================
// 接受webSocket连接中,来自服务端的消息
socket.onmessage = function(event) {
if (event.data == 'Ping'){
console.log("ping");
} else {
if (event.data == 'Opened') {
console.log("revice:", event.data);
var type = 1;
var msg = '我加入';
setTimeout(function () { reply(); }, 300);
} else {
// 将服务端发送来的消息进行json解析
var data = JSON.parse(event.data);
//console.log("revice:" , data);
var name = data.username;
var face = data.face;
var type = 0;
var msg = data.message;
}
// type为0表示有人发消息
var $messageDiv;
if (type == 0) {
var str = '<div class="msg-div">';
if (name != '{{$session['username']}}') {
str += '<div class="other-msg-div">';
}else{
str += '<div class="my-msg-div">';
}
str += '<div class="header-img-div">';
str += '<img src="' + face + '" />';
str += '</div>';
str += '<div class="msg-content">';
str += msg;
str += '</div>';
str += '</div>';
str += '</div>';
var $messageDiv = $("#bottom-link").data('username', name).append(str);
scrollHeight = $('.talk-list-div').prop("scrollHeight");
$('.talk-list-div').scrollTop(scrollHeight, 200);
} else {// type为1或2表示有人加入或退出
var $messageBodyDiv = $('<span style="color:#999999;">').text(msg);
$messageDiv = $('<ol>').append($messageBodyDiv);
}
$messageArea.append($messageDiv);
}
}
//========================发送消息==========================
// 当回车键敲下
$window.keydown(function (event) {
// 13是回车的键位
if (event.which === 13) {
sendMessage();
typing = false;
}
});
// 发送按钮点击事件
$("#sendBtn").click(function () {
sendMessage();
});
// 通过webSocket发送消息到服务端
function sendMessage () {
var inputMessage = $inputArea.val(); // 获取输入框的值
if (inputMessage && connected) {
$inputArea.val(''); // 清空输入框的值
var messages = '{"uid":"{{$session['user_id']}}","message":"'+ inputMessage + '"}';
socket.send(messages); // 基于WebSocket连接发送消息
console.log("send message:" + messages);
}
}
});
</script>
@parent
@endsection
完事了 就是这么简单
看看效果
url:http://192.168.1.98:9501/user/center
用两台电脑,两个账号测试
1号电脑
2号电脑
服务端
完
相关文章