swoole+websocket实现聊天室功能

2023-10-09 522 阅读 0评论

基础介绍

(1)Swoole是一个面向生产环境的 PHP 异步网络通信引擎,使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。 使用 PHP + Swoole 作为网络通信框架,可以使企业 IT 研发团队的效率大大提升。—百度百科

(2)WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 —百度百科

(3)那这篇的主题就是如何使用Swoole+WebSocket实现一个简易的聊天室。

熟悉网络通信协议的同学肯定不会陌生。

功能需求及问题处理

web端:(1)每次刷新都会生成一个唯一的ID(id值从1开始).(2)第一次进入网站时会要求用户设置昵称并会与ID进行绑定。问题点:(1)刷新页面后用户标志(ID)会重新生成,之前生成ID被弃用。(2)WebSocket生成了新的用户ID,但是跟现在的无法形成关联关系。

server端:(1)当用户进入聊天室后,发送广播给所有人并加入聊天群组(使用redis存储)。(2)当用户退出直播间后,发送广播给所有人并清除该用户的记录。(3)用户每发送一次消息都要形成新的记录广播给所有人。(4)用户生成新的昵称后把昵称推送给他。

web端问题处理方法:(1)浏览器刷新时提醒用户刷新即将重新获得新的身份。(2)用户连接成功后记录用户name,每次连接把这个name带上,清除之前该name的绑定关系,形成新的关系。

php实现代码:

<?php
include
"RedisManager.php";
$server = new Swoole\WebSocket\Server("0.0.0.0",8877);
//客户端连接
$server->on('open', function (SwoolelWebSocketiServer $server, $request) {
   //连接成功把当前在线的用户返回
   $user_list = RedisConnectgetRedis()->hGetAll('message:user');
   alone($server, $request->fd,[
       'type'=>'first',
       'data'=>$user_list
   ]);
});

//接收客户端发送的消息
$server->on('message' , function (Swoole\WebSocket\Server $server, $frame){
   $message_data = json_decode($frame->data, true);
   $type = $message_data['type'];
   $data = $message_data['user_name'];
   if (isset($message_data['send_message'])){
       $user_message = $message_data['send_message'];
   }
   switch ($type) {
       case 'save_user':
           $new_name = '大夏单子'.$frame->fd.'';
           RedisConnect::getRedis()->hset('message:user',$frame->fd,$new_name);
           //把生成的用户昵称返回给他
           alone($server, $frame->fd,[
               'type' => 'new_name',
               'name' => $new_name,
               'id' => $frame->fd
           ]);
           //广播消息给其他用户
           groupSending($server, [
               'type' => 'open',
               'name' => $new_name,
               'id' => $frame->fd,
           ]);
           break;
       case 'send_message':
           $msg = [
               'id' => $frame-> fd,
               'user_name' => $data,
               'message' => $user_message,
               'type' => 'message'
           ];
           //接受用户发送的消息
           RedisConnect::getRedis()->lpush('message:user:say', $msg);
           groupSending($server, $msg);
           break;
   }
});
//退出聊天室
$server->on('close' , function ($ser, $fd){
   $user = RedisConnect::getRedis()->hget('message:user',$fd);
   RedisConnect::getRedis()->hdel('message:user', $fd);
   $msg = [
       'id' => $fd,
       'user_name' => $user,
       'message' => '退出聊天室',
       'type' => 'close'
   ];
   groupSending($ser, $msg, $fd);
});
//开启服务
$server->start();
//群发消息
function groupSending($server, $msg, $self = null){
   foreach ($server->connections as $conn){
       if ($conn == $self) break;
       //不再推送给当前退出的用户
       $server->push($conn, json_encode($msg));
   }
}
//单独发消息
function alone($server,$fd, $msg){
   $server->push($fd, json_encode($msg));
}

html代码:

<IDOCTYPE html>
    <html>
<head>
    <meta charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>聊天室</title>
    <style>
        *{margin:0;padding:0;).box{width:602px;height:500px;margin:20px
        auto}.body{width:500px;height:500px;border:1px solid #333;padding:20px;box-sizing:border-box;float:right}.title{text-align:center;margin-top:10px}.msg{margin-top:20px;width:100%;text-align:left;}.message_box{width:100%;height:90%;margin-top:10px;font-size:12px;overflow:hidden;overflow-y:auto;}.name{font-weight:bold;}.right{text-align:right;}.prompt{width:600px;margin:0
        auto}.live{width:100px;height:500px;float:left;border:1px solid #333;font-size:12px;text-align:center}.Iive-box{height:100%;overflow:hidden;overflow-y:auto}.Ilive-box p{margin-top:10px;}
    </style>
</head>
<body>
<div>
    <div>
    <div>+十十++十+十+++聊天室++十十+十++++++</div>
        <div id="test"></div>
    </div>
    <div>
    <div>在线人数</div>
        <div></div>
    </div>
</div>
<div>
    <input type="text" name="say">
    <input type="button" value="发送" id="send">
    <!--<input type="button" value="增加消息" id="add" > -->
</div>
</body>
<script src="jquery.js"></script>
<script src='//cdn.bootcss.com/socket.io/1.3.7/socket.io.js'></script>
<script src='https://cdn.bootcss.com/socket.io/2.0.3/socket.io.js'></script>
<script type="text/javascript">
    window.onbeforeunload = function (event){
        window.localStorage.clear();
    };
    $(function(){
        window.user_name = '';
        //获取在线用户
        if(!("WebSocket" in window)){
            alert("您的浏览器不支持WebSocketl");
            return false;
        }
        //连接
        let ws = new WebSocket("ws://49.235.172.110:8877");//连接成功
        ws.onopen = function (event){
            //修改昵称
            let user_info = {
                'type' : 'save_user',
                'user_name': '',
            };
            ws.send(JSON.stringify(user_info));
            console.log('连接成功','修改昵称成功');
        };
        ws.onmessage = function (evt) {
            let received_msg = JSON.parse(evt.data);
            let type = received_msg['type'];
            switch (type){
                case 'message':
                    add_msg(received_msg);
                    break;
                case 'close':
                    $(".Jive-box").find('p[user='+ received_msg['id'] + ']').remove();
                    break;
                case 'connect':
                    console.log(received _msg);
                    break;
                case 'new_name':
                    user_name = received_msg[ 'name'];
                    break;
                case 'open':
                    $('.live-box').append('<p user="' + received_msg['id']+'">'+
                    received_msg['name'] + '</p>');
                    break;
                case 'first':
                    let data = received_msg['data'];let str = ";
                    $.each(data, function (k, v){
                        if (isNaN(v)) {
                            str += '<p user="' + k + '">' + v + '</p>';
                        }
                    })
                    $('.live-box').append(str);
                    break;
            }
        }
        //发送消息
        $("#send").click(function () {
            send_msg();
        })
        $("input[name=say]").bind("keydown", function (e){
            var theEvent = e || window.event;
            var code = theEvent.keyCode || theEvent.which || theEvent.charCode;
            if (code == 13) {
                send_msg();
            }
        });
        //发送消息
        function send_msg() {
            var message = $(input[name=say]).val();
            if (message !== '' ){
                var arr = {
                    'type': 'send_message',
                    'user_ name': user_name,
                    'send_message': message
                }
            }
            ws.send(JSON.stringify(arr));
            $('input[name=say]').val('');
            var ele = document.getElementById("test");
            if (ele.scrollHeight > ele.clientHeight) {
                ele.scrollTop = ele.scrollHeight + 100;
            }
        }
        //增加消息
        function add_msg(data){
            let str ='<div>';
            str += '<span>'+ data['user_name'] + '</span>:';
            str += '<span>'+ data['message'] + '</span> </div>';
            $('.message_box').append(str);
        }
    })
</script>
</html>

这篇文章只是简单的介绍前后端如何实现通信,很多的细节问题没有进行处理。UI比较low,可以按照自己需求进行修改…

喜欢就支持以下吧
点赞 0

发表评论

快捷回复: 表情:
aoman baiyan bishi bizui cahan ciya dabing daku deyi doge fadai fanu fendou ganga guzhang haixiu hanxiao zuohengheng zhuakuang zhouma zhemo zhayanjian zaijian yun youhengheng yiwen yinxian xu xieyanxiao xiaoku xiaojiujie xia wunai wozuimei weixiao weiqu tuosai tu touxiao tiaopi shui se saorao qiudale qinqin qiaoda piezui penxue nanguo liulei liuhan lenghan leiben kun kuaikule ku koubi kelian keai jingya jingxi jingkong jie huaixiao haqian aini OK qiang quantou shengli woshou gouyin baoquan aixin bangbangtang xiaoyanger xigua hexie pijiu lanqiu juhua hecai haobang caidao baojin chi dan kulou shuai shouqiang yangtuo youling
提交
评论列表 (有 0 条评论, 522人围观)

最近发表

热门文章

最新留言

热门推荐

标签列表