原创

easyswoole实现在线聊天室功能

温馨提示:
本文最后更新于 2017年08月15日,已超过 2,468 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

一 : 安装easyswoole,可参考http://www.php20.cn/article/82

把example/example\multiUsage_01\的实例覆盖到src,访问ip:端口(9501)/test/websocket.php可查看最简单实例

灵活组合加上前端可以做的非常好

仙士可博客

以下为我自己项目修改后的代码,看不懂可以去看简单实例

前台js

var user_info;
var is_connect = 0;
var websocket;
var msg = 0;
is_login();
autoWidth();
var initWebSocket = function () {
    var wsServer = 'ws://139.199.164.211:9501';
    websocket = new WebSocket(wsServer);
    websocket.onopen = function (evt) {
        var html = '<p>正在连接服务器...</p>';
        addLine(html);
    };
    websocket.onclose = function (evt) {
        var html = '<p>服务器异常关闭,正在重连</p>';
        addLine(html);
        socket_connect();
    };
    websocket.onmessage = function (evt) {
        var data = JSON.parse(evt.data);
        if (data['status'] == false) {
            msgTip(data['info']);
            return;
        }
        if (parseInt(data['fd']) > 0) {
            $.ajax({
                url: SiteUrl + 'index.php?g=Res&m=Chat&a=add_client',
                dataType: 'JSON',
                data: {fd: data['fd'], key: key},
                type: 'POST',
                success: function (res) {
                    check_login(res);
                    /*  if (res['status'] == false) {
                     msgTip(res['info']);
                     return;
                     }*/
                    is_connect = 1;
                    user_info = res['data'];
                    var html = '<p>连接服务器成功</p>';
                    addLine(html);
                }
            });
        } else {
            if (data['user_info']['id'] == user_info['id']) {
                addMyLine(data['msg']);
            } else {
                addOthLine(data['msg'],data['user_info']);
            }
        }

        if (msg == 0) {//消息为0则返回最近聊天记录的10条
            get_msg();
        }
        msg++;
        if (msg >= 50) {
            $('#speak_box').find('div').eq(-30).prevAll().remove();
            msg = 30;
        }
    };
    websocket.onerror = function (evt, e) {
        loadingShow(evt.data);
    };
};

initWebSocket();

var socket_connect = function () {
    window.connect = setInterval(function () {
        initWebSocket();
        if (is_connect == 1) {
            clearInterval(window.connect);
        }
    }, 5000);
};

function addLine(data) {
    var html = '<div class="answer">';
    html += data;
    html += '</div>';
    $('#speak_box').append(html);
}
function addMyLine(data) {
    var html = '<div class="question">';
    html += '<div class="heard_img right"><img src="' + Host + '/data/upload/avatar/' + user_info.avatar + '"></div>';
    html += '<div class="question_text clear" style="max-width: 285px;"><p>' + html2Escape(data) + '</p><i></i></div>';
    html += '</div>';
    $('#speak_box').append(html);
}
function addOthLine(msg,user) {
    if (user.avatar == null) {
        user.avatar = 'admin.gif';
    }
    var html = '<div class="answer">';
    html += '<div class="heard_img left"><p>' + user.user_nicename + '</p><img src="' + Host + '/data/upload/avatar/' + user.avatar + '"></div>';
    html += '<div class="answer_text" style="max-width: 285px;"><p>' + html2Escape(msg) + '</p><i></i></div>';
    html += '</div>';
    $('#speak_box').append(html);
}

function send(msg, to_user_id) {
    to_user_id = to_user_id ? to_user_id : 0;

    $.ajax({
        url: SiteUrl + 'index.php?g=Res&m=Chat&a=add_msg',
        dataType: 'JSON',
        data: {key: key, to_user_id: to_user_id, msg: msg},
        type: 'POST',
        success: function (res) {
            check_login(res);
            if (res['status'] == false) {
                msgTip(res['info']);
                return;
            }
            data = '{"msg":"' + msg + '","key":"' + key + '"}',
                websocket.send(data);
        }
    });
}

function get_msg() {
    $.ajax({
        url: SiteUrl + 'index.php?g=Res&m=Chat&a=get_msg',
        dataType: 'JSON',
        data: {key: key},
        type: 'POST',
        success: function (res) {
            check_login(res);
            if (res['status'] == false) {
                msgTip(res['info']);
                return;
            }
            for (var x in res['data']['list']){
                var vo = res['data']['list'][x];
                if (vo['user_id'] == user_info['id']) {
                    addMyLine(vo['content']);
                } else {
                    addOthLine(vo['content'],vo);
                }
            };
        }
    });
}


var wen = document.getElementById('wenwen');
function _touch_start(event) {
    event.preventDefault();
    $('.wenwen_text').css('background', '#c1c1c1');
    $('.wenwen_text span').css('color', '#fff');
    $('.saying').show();
}

function _touch_end(event) {
    event.preventDefault();
    $('.wenwen_text').css('background', '#fff');
    $('.wenwen_text .circle-button').css('color', '#666');
    $('.saying').hide();
    var str = '<div class="question">';
    str += '<div class="heard_img right"><img src="../assets/images/chat/dglvyou.jpg"/></div>';
    str += '<div class="question_text clear"><p>不好意思,我听不清!</p><i></i>';
    str += '</div></div>';
    $('.speak_box').append(str);
    for_bottom();
    setTimeout(function () {
        var ans = '<div class="answer"><div class="heard_img left"><img src="../assets/images/chat/dglvyou.jpg"/></div>';
        ans += '<div class="answer_text"><p>我不知道你在说什么?</p><i></i>';
        ans += '</div></div>';
        $('.speak_box').append(ans);
        for_bottom();
    }, 1000);
}

wen.addEventListener("touchstart", _touch_start, false);
wen.addEventListener("touchend", _touch_end, false);

function for_bottom() {
    var speak_height = $('.speak_box').height();
    $('.speak_box,.speak_window').animate({scrollTop: speak_height}, 500);
}

function autoWidth() {
    $('.question_text').css('max-width', $('.question').width() - 60);
}

function to_write() {
    $('.wenwen_btn img').attr('src', '../assets/images/chat/yy_btn.png');
    $('.wenwen_btn').attr('onclick', 'to_say()');
    $('.write_box,.wenwen_help button').show();
    $('.circle-button,.wenwen_help a').hide();
    $('.write_box input').focus();
    for_bottom();
}

function to_say() {
    msgTip('暂不支持语音聊天');
    return;
    $('.write_list').remove();
    $('.wenwen_btn img').attr('src', '../assets/images/chat/jp_btn.png');
    $('.wenwen_btn').attr('onclick', 'to_write()');
    $('.write_box,.wenwen_help button').hide();
    $('.circle-button,.wenwen_help a').show();
}


function keyup() {
    var footer_height = $('.wenwen-footer').outerHeight(),
        text = html2Escape($('.write_box input').val()),
        str = '<div class="write_list">' + html2Escape(text) + '</div>';
    if (text == '' || text == undefined) {
        $('.write_list').remove();
    } else {
        $('.write_list').remove();
        $('.wenwen-footer').append(str);
        $('.write_list').css('bottom', footer_height);
    }
}

function up_say() {
    $('.write_list').remove();
    var text = $('.write_box input').val();
    if (text.length < 1) {
        msgTip('内容不能为空');
        return;
    }
    $('.write_box input').val('');
    $('.write_box input').focus();
    autoWidth();
    for_bottom();
    send(text);
    /*
     setTimeout(function () {
     var ans = '<div class="answer"><div class="heard_img left"><img src="../assets/images/dglvyou.jpg"/></div>';
     ans += '<div class="answer_text"><p>您发送的文字是:' + text + '</p><i></i>';
     ans += '</div></div>';
     $('.speak_box').append(ans);
     for_bottom();
     }, 1000);*/
}

function removeHtmlTab(tab) {
    return tab.replace(/<[^<>]+?>/g, '');//删除所有HTML标签
}

function html2Escape(sHtml) {
    return sHtml.replace(/[<>&"]/g, function (c) {
        return {'<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;'}[c];
    });
}

后台event.php监听文件

<?php
/**
 * Created by PhpStorm.
 * User: yf
 * Date: 2017/1/23
 * Time: 上午12:06
 */

namespace Conf;


use App\Model\Chat\Chat;
use Core\AbstractInterface\AbstractEvent;
use Core\AutoLoader;
use Core\Component\Di;
use Core\Component\Logger;
use Core\Component\Version\Control;
use Core\Http\Request;
use Core\Http\Response;
use Core\Swoole\SwooleHttpServer;
use Core\Swoole\Timer;
use App\Utility\Mysql;

class Event extends AbstractEvent
{
    function frameInitialize()
    {
        // TODO: Implement frameInitialize() method.
        date_default_timezone_set('Asia/Shanghai');
        $loader = AutoLoader::getInstance();
        $loader->requireFile("App/Vendor/MysqliDb/MysqliDb.php");
        $loader->requireFile("App/Vendor/Smarty/Smarty.class.php");
    }

    function beforeWorkerStart(\swoole_http_server $server)
    {
        // TODO: Implement beforeWorkerStart() method.
        //udp 请勿用receive事件
        //添加自带多协议监听
        $udp = $server->addlistener("0.0.0.0", 9502, SWOOLE_SOCK_UDP);
        $udp->on('packet', function (\swoole_server $server, $data, $addr) {
            Logger::console("receive data {$data}");
            $server->sendto($addr['address'], $addr['port'], "Swoole: $data");
        });

//        $listener = $server->addlistener("0.0.0.0",9502,SWOOLE_TCP);
//        //混合监听tcp时    要重新设置包解析规则  才不会被HTTP覆盖,且端口不能与HTTP SERVER一致 HTTP本身就是TCP
//        $listener->set(array(
//            "open_eof_check"=>false,
//            "package_max_length"=>2048,
//        ));
//        $listener->on("connect",function(\swoole_server $server,$fd){
//            Logger::console("client connect");
//        });
//        $listener->on("receive",function(\swoole_server $server,$fd,$from_id,$data){
//            Logger::console("received connect");
//            $server->send($fd,"swoole ".$data);
//            $server->close($fd);
//        });
//        $listener->on("close",function (\swoole_server $server,$fd){
//            Logger::console("client close");
//        });
        $server->on("handshake", function (\swoole_http_request $request, \swoole_http_response $response) {
            Logger::console("handshake");
            //自定定握手规则,没有设置则用系统内置的(只支持version:13的)
            if (!isset($request->header['sec-websocket-key'])) {
                //'Bad protocol implementation: it is not RFC6455.'
                $response->end();
                return false;
            }
            if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key'])
                || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))
            ) {
                //Header Sec-WebSocket-Key is illegal;
                $response->end();
                return false;
            }

            $key = base64_encode(sha1($request->header['sec-websocket-key']
                . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
                true));
            $headers = array(
                'Upgrade' => 'websocket',
                'Connection' => 'Upgrade',
                'Sec-WebSocket-Accept' => $key,
                'Sec-WebSocket-Version' => '13',
                'KeepAlive' => 'off',
            );
            foreach ($headers as $key => $val) {
                $response->header($key, $val);
            }
            $response->status(101);
            $response->end();
            $result['status'] = true;
            $result['fd'] = $request->fd;
            SwooleHttpServer::getInstance()->getServer()->push($request->fd, json_encode($result));
        });

        //添加websocket回调事件
        $server->on("message", function (\swoole_websocket_server $server, \swoole_websocket_frame $frame) {
            Logger::console("接收数据:" . $frame->data);
            $data = json_decode($frame->data, 1);
            $chat_model = new Chat();
            //验证用户
            $user_info = $chat_model->get_info('select id,
    user_login,
    user_nicename,
    user_email,
    avatar,
    coin,
    mobile,
    money,
    freeze_money,
    sex
     from dm_users WHERE login_key="' . $data['key'] . '" limit 1');
     //这段的sql代码参考\example\multiUsage_01\App\Model\Goods\goods.php 的Mysql基类 方法
            if (!$user_info) {
                $result = array();
                $result['status'] = false;
                $result['info'] = '你没有登陆账号!';
                $server->push($frame->fd, json_encode($result));
            } else {
                if(empty($user_info['avatar'])){
                    $user_info['avatar']='admin.gif';
                }
                $data['user_info'] = $user_info;
                unset($data['key']);
                $data['status']=true;
                $data['msg'] = htmlentities($data['msg']);
                foreach ($server->connections as $key => $value) {
                    $server->push($value, json_encode($data));
                }
            }
        });

        $server->on("close", function (\swoole_http_server $server, $fd) {
            $info = SwooleHttpServer::getInstance()->getServer()->connection_info($fd);
            if ($info['websocket_status']) {
                Logger::console("websocket 客户端 {$fd} 关闭");
            }
        });
        //添加websocket回调事件结束


    }

    function onStart(\swoole_http_server $server)
    {
        // TODO: Implement onStart() method.
        //使用event loop实现自定义 socket监听
        $listener = stream_socket_server(
            "udp://0.0.0.0:9503",
            $error,
            $errMsg,
            STREAM_SERVER_BIND
        );
        if ($errMsg) {
            throw new \Exception("listen fail");
        } else {
            //加入event loop
            swoole_event_add($listener, function ($listener) {
                $data = stream_socket_recvfrom($listener, 9503, 0, $client);
                Logger::console("rec data {$data} in event loop");
                stream_socket_sendto($listener, "hello this is event loop", 0, $client);
            });
        }
    }

    function onShutdown(\swoole_http_server $server)
    {
        // TODO: Implement onShutdown() method.
    }

    function onWorkerStart(\swoole_server $server, $workerId)
    {
        // TODO: Implement onWorkerStart() method.
        //为第一个worker添加一个定时器
        /*   if ($workerId == 0) {
               //10秒
               Timer::loop(10 * 1000, function () {
                   Logger::console("this is timer");
               });
           }*/

    }

    function onWorkerStop(\swoole_server $server, $workerId)
    {
        // TODO: Implement onWorkerStop() method.
    }

    function onRequest(Request $request, Response $response)
    {
        // TODO: Implement onRequest() method.
    }

    function onDispatcher(Request $request, Response $response, $targetControllerClass, $targetAction)
    {
        // TODO: Implement onDispatcher() method.
    }

    function onResponse(Request $request, Response $response)
    {
        // TODO: Implement afterResponse() method.
    }

    function onTask(\swoole_http_server $server, $taskId, $fromId, $taskObj)
    {
        // TODO: Implement onTask() method.
    }

    function onFinish(\swoole_http_server $server, $taskId, $fromId, $taskObj)
    {
        // TODO: Implement onFinish() method.
    }

    function onWorkerError(\swoole_http_server $server, $worker_id, $worker_pid, $exit_code)
    {
        // TODO: Implement onWorkerError() method.
    }
}

注意:easyswoole只能在php-cli状态下运行,你修改完代码需要重启服务才能使代码生效,详细操作方法请看server.php

注意:该文章写的example文件夹已经转移到官网的实例文档中,源码已经删除

关于数据库操作的文件也已经移除,需要自己去实现model层的操作,可以查找相关的操作类

正文到此结束
本文目录