资讯专栏INFORMATION COLUMN

swoole 服务端120行代码构建一个websocket 聊天室.

韩冰 / 551人阅读

摘要:的异步并行高性能网络通信引擎,使用纯语言编写,提供了语言的异步多线程服务器,异步网络客户端,异步,异步,数据库连接池,,消息队列,毫秒定时器,异步文件读写,异步查询。内置了服务器端客户端服务器端。

swoole :http://www.swoole.com/
PHP的异步、并行、高性能网络通信引擎,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。 Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端。

7-22更新------ 昨天已经用面向对象和redis重做了一次,现在支持切换分组,私信了.并且对其他地方也做了一些优化. 视频演示: http://www.bilibili.com/video... 视频中的列表没有正常刷新漏洞已修复 github : https://github.com/buffge/buf...

demo 地址 http://www.buffge.xin/buffcha... end------ 7-27更新

今天服务器被人攻击了 他攻击也许就是为了测试或者玩我,而且他也没有得逞
他利用我的redis的漏洞来攻击我,因为我本地用一个可视化软件去查看我服务器的数据库,所以我
开放的权限并且没有加密,之后有人想透过redis 给我服务器上传脚本. 不过这个脚本就是重复运行而已,应该是个
练手的.他这个脚本没有运行起来,因为我redis 的权限很低. 他首先给我redis里面加入了这样一句话

然后想让这个脚本在我服务器中循环运行.真的可恶.我好心开源给大家看了玩玩,我也没空去搞那么安全那么好 但是就是有这种人渣来黑我.
等过几天我忙完了手上的事重新开启这个聊天室并添加新功能 和新样式 不说了,我先举报去了,我已经找到他的ip和他的群了.就看tx管不管了.bye~
-------end


昨晚一直在学习swoole. 终于搞出了一个多人聊天室.新手可以看看,高玩请直接右上角.

思路:

 1:用户登录主页面,此时后台将用户放入数据库(我不会redis memcached 等),
 数据库格式为

第一次登录只需要将fd插入就行了.

2:此时如果不进行注册,那么就无法发送信息给全局.
如果注册了 那么填写用户名,ajax请求 将用户名进行加盐信息摘要获得token.
注册成功后发送token:117fdba5e4d4050ed18ffd85ac86ed5b5d72dec6a271f4e4f6a6e03f4b957cd4:user_name:张三123
这样的一个字符串给服务器,服务器会进行token 和名称验证,如果是正确的token.那么将用户信息插入到数据库.
并通知其他websocket 有新用户上线.

3:快乐的聊天吧.

路上遇到的坑:

        1:我原本准备在server.php(就是swoole 后台服务)创建一个对象,
        存储所有访客的信息 姓名等等,但是他不共享这个对象,我觉得可以用redis 缓存起来,
        我这里就写了玩的,就用mysql弄了,也没有面向对象.
        2:不能用session  反正用了 之后就是各种问题,我原本是准备用session 存储用户名称的.
        后台没办法想出一个加密token 然后存入数据库,
        具体的token是这样的.
        


注册时候用盐和sha256生成token,然后去swoole 服务器那边验证的时候也用同样的用户名和盐进行计算,看是否一样.如果一样 说明是正确的登录了. 我不知道这个方法好不好.以前只会session,这个是昨晚突然想到的.

服务器那边的日志如下:

4个用户的聊天界面如下:

服务器端 代码 看注释.写的渣别说,我是菜鸟.

   
  <#时间 = "00:46:16">
  <#人物 = "buff" >
  <#备注 = " ">
 */
if (php_sapi_name() !== "cli") {
    exit("使用cli模式");
}
$serv = new SwooleWebsocketServer("192.168.1.109", 9501);
//回调函数 新建一个websocket连接时 触发的事件
$serv->on("Open", function($server, $req) {
    $mysqli = new mysqli("127.0.0.1", "buffge", "daimin", "buffchat");
    $charsetsql = "set names utf8";
    $mysqli->query($charsetsql);
    //将新登陆的用户 保存到mysql
    $sql = "INSERT INTO `users` (`fd`) VALUES ("{$req->fd}")";
    $mysqli->query($sql);
    if ($mysqli->affected_rows !== 1) {
        echo "插入数据失败!sql== {$sql}";
    }
    echo "新客户端连接: " . $req->fd . "时间:" . date("Y-n-j H:i:s") . "
";
    $userlist = "";
    //检查当前共有多少用户在线
    $sql = "select `user_name` from users where `user_name`!=""";
    $res = $mysqli->query($sql);
    for ($i = 0; $i < $res->num_rows; $i++) {
        $result = $res->fetch_assoc();
        $userlist .= (""" . $result["user_name"] . "",");
    }
    $userlist = substr($userlist, 0, strlen($userlist) - 1);
    //通知用户 当前在线用户列表
    $server->push($req->fd, "{"code":"4","users":[{$userlist}]}");
    $res->free();
    $mysqli->close();
});
//当收到用户的消息时 触发事件
$serv->on("Message", function($server, $frame) {
    $mysqli = new mysqli("127.0.0.1", "buffge", "daimin", "buffchat");
    $charsetsql = "set names utf8";
    $mysqli->query($charsetsql);
    $sql = "select * from users where fd={$frame->fd}";
    $res = $mysqli->query($sql);
    $result = $res->fetch_assoc();
    //获取当前发消息的人的名称
    $user_name = $result["user_name"];
    echo "收到来自客户端{$frame->fd}的消息: " . $frame->data . "
";
    //当用户是第一个注册时候(发送的语句前面5个字是token)
    if (strpos($frame->data, "token") === 0) {
        //如果数据库中没有令牌
        if ($result["token"] === null) {
            $userData = explode(":", $frame->data);
            $hash = hash("sha256", "daimin" . $userData[3]);
            if ($userData[1] == $hash) {
                $sql = "UPDATE `users` SET `token` = "{$hash}",`user_name` = "{$userData[3]}" WHERE `fd` = {$frame->fd}";
                $mysqli->query($sql);
                if ($mysqli->affected_rows !== 1) {
                    echo "更新用户信息失败, sql === {$sql}";
                    $server->push($frame->fd, "{"code":"5","mes":"更新用户信息失败"}");
                }
                $server->push($frame->fd, "{"code":"3","user_name":"{$userData[3]}"}");
                $res->free();
                $userlist = "";
                //检查当前共有多少用户在线
                $sql = "select `user_name` from users where `user_name`!=""";
                $res = $mysqli->query($sql);
                for ($i = 0; $i < $res->num_rows; $i++) {
                    $result = $res->fetch_assoc();
                    $userlist .= (""" . $result["user_name"] . "",");
                }
                $userlist = substr($userlist, 0, strlen($userlist) - 1);
                //通知所有用户 当前在线用户列表
                foreach ($server->connections as $fd) {
                    $server->push($fd, "{"code":"4","users":[{$userlist}]}");
                }
                echo "新注册用户 {$userData[3]}
";
            }
            else {
                $server->push($frame->fd, "{"code":"5","mes":"token错误"}");
            }
        }
        //如果只是发送包含token这个字符串的语句 群发
        else {
            foreach ($server->connections as $fd) {
                $server->push($fd, "{"code":"2","mes":"{$frame->data}","user_name":"{$user_name}"}");
            }
        }
    }
    //如果不是注册用户
    else {
        //如果没有令牌
        if ($result["token"] === null) {
            $server->push($frame->fd, "{"mes":"请先登录!"}");
            return;
        }
        //将换行转换为br
        $mes = nl2br($frame->data);
        //格式化json
        $mes = str_replace("
", "", $mes);
        //群发消息
        foreach ($server->connections as $fd) {
            $server->push($fd, "{"code":"2","mes":"{$mes}","user_name":"{$user_name}"}");
        }
    }
    $res->free();
    $mysqli->close();
});
//当websocket 断开连接时 触发事件
$serv->on("Close", function($server, $fd) {
    $mysqli = new mysqli("127.0.0.1", "buffge", "daimin", "buffchat");
    $charsetsql = "set names utf8";
    $mysqli->query($charsetsql);
    $sql = "DELETE FROM `users` WHERE `users`.`fd` = {$fd}";
    //当用户退出时 删除信息
    $mysqli->query($sql);
    if ($mysqli->affected_rows !== 1) {
        echo "删除用户信息失败, sql === {$sql}";
    }
    echo "客户端{$fd}已断开连接
";
    $mysqli->close();
});

$serv->start();

前端js 核心

websocket.onmessage = function (evt) {
    console.log(evt.data);
    var data = JSON.parse(evt.data);
    //根据code 判断业务
    switch (data.code) {
        //个人消息
        case "1":
            break;
            //全局消息
        case "2":
            var $time = bf_get_time();//这是我自己写的一个获取时间函数
            var $mes = data.mes;//消息内容
            var $user_name = data.user_name;//发消息的人
            var $who = $user_name === selfName ? "self" : "other";//根据名称设置格式
            var $append = "

" + $user_name + "

" + $mes + "

" $(".gui_content").append($append); var $cont_scrTop = $(".gui_content").scrollTop(); var $list_height = $(".mes_item:last-of-type").height(); $(".gui_content").animate({"scrollTop": $cont_scrTop + $list_height}, 100); break; //通知注册用户成功消息 case "3": var $append = "
  • "; $append += data.user_name; $append += "
  • "; $(".user_list ul").append($append); $(".gui_user").html("

    欢迎 " + data.user_name + "

    "); selfName = data.user_name; break; //更新当前在线列表 删除的我没写~ case "4": var $append = ""; var $nowUser = $(".user_list ul li"); outer: for (var user in data.users) { for (var i = 0; i < $nowUser.length; i++) { if ($nowUser[i].innerText === data.users[user]) { continue outer; } } console.log(data.users[user]); $append += "
  • "; $append += data.users[user]; $append += "
  • "; } $(".user_list ul").append($append); break; case "5": alert(data.mes); break; } console.log("收到来自服务器的消息: " + data.code); };

    等有空了用面向对象写一次把mysql 换成redis,现在的代码有点乱,一些功能也没有写,因为太困了,连续写了8小时.

    如果有大神看的话 能不能告诉我 哪里该改进. 比如怎么验证用户,用哪种方法存储之类的.谢谢了

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/23284.html

    相关文章

    • Websocket解析及实现

      摘要:早期的轮询是通过不断自动刷新页面而实现的。长轮询的另一个问题是缺乏标准实现。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户端和服务器端的连接不过期。协议解析协议包含两部分一部分是握手,一部分是数据传输。 Websocket是什么? Websocket是一个因为应用场景越来越复杂而提出的,针对浏览器和web服务器之间双向持续通信而设计,而且优雅地兼容HTTP的协议(我猜想:同...

      XboxYan 评论0 收藏0
    • 实战 swoole天室

      摘要:是一个请求对象,包含了客户端发来的握手请求信息事件函数中可以调用向客户端发送数据或者调用关闭连接事件回调是可选的当服务器收到来自客户端的数据帧时会回调此函数。 前言:了解概念之后就应该练练手啦,不然就是巨婴 有收获的话请加颗小星星,没有收获的话可以 反对 没有帮助 举报三连 代码仓库 实战swoole【聊天室】 在线体验 准备工作 需要先看初识swoole【上】,了解基本的服务端...

      andycall 评论0 收藏0
    • php只能做网站?基于swoole+websocket开发双向通信应用

      摘要:那么,是否就无法用来开发双向通信的应用呢答案是否定的。内置通信支持,可以与程序基于进行双向通信。通信协议于年被定为标准,并由补充规范。前言 众所周知,PHP用于开发基于HTTP协议的网站应用非常便捷。而HTTP协议是一种单向的通信协议,只能接收客户端的请求,然后响应请求,不能主动向客户端推送信息。因此,一些实时性要求比较高的应用,如实时聊天、直播应用、在线网页游戏等,就不适合采用HTTP协议...

      番茄西红柿 评论0 收藏0
    • php只能做网站?基于swoole+websocket开发双向通信应用

      摘要:那么,是否就无法用来开发双向通信的应用呢答案是否定的。内置通信支持,可以与程序基于进行双向通信。通信协议于年被定为标准,并由补充规范。前言 众所周知,PHP用于开发基于HTTP协议的网站应用非常便捷。而HTTP协议是一种单向的通信协议,只能接收客户端的请求,然后响应请求,不能主动向客户端推送信息。因此,一些实时性要求比较高的应用,如实时聊天、直播应用、在线网页游戏等,就不适合采用HTTP协议...

      琛h。 评论0 收藏0
    • swoole 超简单 构建天室.资辞 群聊,组聊,单聊.

      摘要:今天来做一个简单的聊天室支持换房间支持私信的写的代码有点渣里面有很多不是很好的地方毕竟我只是一个野生程序猿环境地址样子差不多是这个样子的我不想把代码发到我的服务器上因为这个项目太小了很垃圾而且怕被攻击这里有录的一个演示视频没有广告的你们可以 今天来做一个简单的聊天室,支持换房间,支持私信的. 写的代码有点渣,里面有很多不是很好的地方.毕竟我只是一个野生程序猿. 环境: php7.0...

      Towers 评论0 收藏0

    发表评论

    0条评论

    最新活动
    阅读需要支付1元查看
    <