美文网首页西岭老湿大型网站架构IT编程
利用Swoole实现PHP+websocket 聊天室

利用Swoole实现PHP+websocket 聊天室

作者: 西岭老湿 | 来源:发表于2016-03-01 22:05 被阅读20365次

    websocket

    Websocket只是一个网络通信协议
    就像 http、ftp等都是网络通信的协议;不要多想;
    相对于HTTP这种非持久的协议来说,Websocket是一个持久化网络通信的协议;

    WebSocket和HTTP的关系

    WebSocket和HTTP的关系


    websocket+http.png

    有交集,但是并不是全部。
    Websocket只是借用了HTTP的一部分协议来完成一次握手。(HTTP的三次握手,此处只完成一次)

    http和websocket 请求头对比:

    http请求头.jpg
    websocket请求头.png

    HTTP:
    原来的时候,客户端通过http(骑马)带着信请求服务器,服务器处理请求(写回信),再次通过http(骑马)返回;链接断开;

    WebSocket:
    客户端通过http(骑马)带着信请求服务器,但同时,携带了Upgrade:websocket和Connection:Upgrade(两根管子),服务器如果支持WebSocket协议(有两根管子的接口),使用Websocket协议返回可用信息(丢弃马匹),此后信息的传递,均使用这两个管子,除非有一方人为的将管子切断;若服务器不支持,客户端请求链接失败,返回错误信息;

    http和websocket 响应头对比:


    http响应头.jpg
    websocket响应头.jpg

    websocket和ajax轮询、long poll的区别

    首先是 ajax轮询 ,ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息
    场景再现:
    客户端:啦啦啦,有没有新信息(Request)
    服务端:没有(Response)
    客户端:啦啦啦,有没有新信息(Request)
    服务端:没有。。(Response)
    客户端:啦啦啦,有没有新信息(Request)
    服务端:你好烦啊,没有啊。。(Response)
    客户端:啦啦啦,有没有新消息(Request)
    服务端:好啦好啦,有啦给你。(Response)
    客户端:啦啦啦,有没有新消息(Request)
    服务端:。。。没。。。。没。。没有

    long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不在论述;

    从上面可以看出,轮询其实就是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性。同时,http的每一次请求与响应结束后,服务器将客户端信息全部丢弃,下次请求,必须携带身份信息(cookie),无状态性

    Websocket的出现,干净利落的解决了这些问题;

    所以上面的情景可以做如下修改。
    客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
    服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
    客户端:麻烦你有信息的时候推送给我噢。。
    服务端:ok,有的时候会告诉你的。
    客户端:balab开始斗图alabala
    服务端:苍井空ala
    客户端:流鼻血了,我擦……
    服务端:哈哈布尔教育牛逼啊哈哈哈哈
    服务端:笑死我了哈哈

    Swoole

    但是,为了用PHP配合HTML5完成一次WebSocket请求和响应,哥走过千山万水,在密林深处,发现了Swoole : http://www.swoole.com/;

    PHP语言的异步、并行、高性能网络通信框架,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。
    支持的服务:
    HttpServer
    WebSocket Server
    TCP Server
    TCP Client
    Async-IO(异步)
    Task(定时任务)

    环境依赖:
    仅支持Linux,FreeBSD,MacOS,3类操作系统
    Linux内核版本2.3.32以上
    PHP5.3.10以上版本
    gcc4.4以上版本或者clang
    cmake2.4+,编译为libswoole.so作为C/C++库时需要使用cmake

    安装:
    必须保证系统中有以下这些软件:
    php-5.3.10 或更高版本
    gcc-4.4 或更高版本
    make
    autoconf

    Swoole是作为PHP扩展来运行的

    安装(root权限):
    cd swoole
    phpize
    ./configure
    make
    sudo make install
    配置php.ini
    extension=swoole.so

    想研究Swoole的同学,自己去看手册(虽然写的不好,但是还是能看懂的)

    做一个聊天室

    服务器端:socket.php

    
    //创建websocket服务器对象,监听0.0.0.0:9502端口
    $ws = new swoole_websocket_server("0.0.0.0", 9502);
    $ws->user_c = [];   //给ws对象添加属性user_c,值为空数组;
    //监听WebSocket连接打开事件
    $ws->on('open', function ($ws, $request) {
        $ws->user_c[] = $request->fd;
        //$ws->push($request->fd, "hello, welcome\n");
    });
    
    //监听WebSocket消息事件
    $ws->on('message', function ($ws, $frame) {
        $msg =  'from'.$frame->fd.":{$frame->data}\n";
       foreach($ws->user_c as $v){
          $ws->push($v,$msg);
      }
       // $ws->push($frame->fd, "server: {$frame->data}");
        // $ws->push($frame->fd, "server: {$frame->data}");
    });
    
    //监听WebSocket连接关闭事件
    $ws->on('close', function ($ws, $fd) {
        //删除已断开的客户端
        unset($ws->user_c[$fd-1]);
    });
    
    $ws->start();
    

    客户端:Socket.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="msg"></div>
    <input type="text" id="text">
    <input type="submit" value="发送数据" onclick="song()">
    </body>
    <script>
        var msg = document.getElementById("msg");
        var wsServer = 'ws://192.168.1.253:9502';
        //调用websocket对象建立连接:
        //参数:ws/wss(加密)://ip:port (字符串)
        var websocket = new WebSocket(wsServer);
        //onopen监听连接打开
        websocket.onopen = function (evt) {
            //websocket.readyState 属性:
            /*
            CONNECTING  0   The connection is not yet open.
            OPEN    1   The connection is open and ready to communicate.
            CLOSING 2   The connection is in the process of closing.
            CLOSED  3   The connection is closed or couldn't be opened.
            */
            msg.innerHTML = websocket.readyState;
        };
    
        function song(){
            var text = document.getElementById('text').value;
            document.getElementById('text').value = '';
            //向服务器发送数据
            websocket.send(text);
        }
          //监听连接关闭
    //    websocket.onclose = function (evt) {
    //        console.log("Disconnected");
    //    };
    
        //onmessage 监听服务器数据推送
        websocket.onmessage = function (evt) {
            msg.innerHTML += evt.data +'<br>';
    //        console.log('Retrieved data from server: ' + evt.data);
        };
    //监听连接错误信息
    //    websocket.onerror = function (evt, e) {
    //        console.log('Error occured: ' + evt.data);
    //    };
    
    </script>
    </html>
    

    websocket API 手册:
    https://developer.mozilla.org/en-US/docs/Web/API/WebSocket

    相关文章

      网友评论

      • db5c12db268e:在学习中碰到什么问题希望能有人交流,加到swoole群486583931一起探讨吧
      • 10445e69d3b1:我是你的学生,你的fans,西岭,一直都系度
      • 暮丶木:最近要写一个聊天室功能 本来都准备用node.js了 看完后发现还有新姿势 嘿嘿
      • bf2770eeec88:不错不错,收藏了。

        推荐下,分库分表中间件 Sharding-JDBC 源码解析 17 篇:http://www.yunai.me/categories/Sharding-JDBC/?jianshu&401


      • d2416ab917d4:代码不能在window下调试的
        西岭老湿:@蓉芳107 不建议在windows下使用swoole
      • b426c3aaad09:Warning: swoole_websocket_server::push(): connection[2] is not a websocket client. in /root/1.php on line 19
        PHP Warning: swoole_websocket_server::push(): connection[3] is not a websocket client. in /root/1.php on line 19
        西岭老湿:代码已经需改,不会出现类似错误了
      • 41fdf75e32d1:怎么配合session使用呢?
      • 8ebd587ddab5:我想问一下,如果同时在线1000人。这1000个人又在同一时间同时发送一条消息,那总共就需要push1000000 次数据,这个数据量是很大的,也很耗时间,怎么解决这个问题。qq 813088607 希望不吝赐教。
        西岭老湿:目前还没有考虑到
      • dd52c682b79c:请教一下,兼容性你是怎么解决的呢
        西岭老湿: 存在兼容性问题吗?
      • b87994a25d43:Uncaught InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.
        丶清风自来:我帮楼主回答下吧。先要运行服务端。否则请求不到
      • 易水人去:如果开启多个进程的话,每个进程里面的globals全局变量不同,会出问题的吧
      • 707dce772d34: $GLOBALS['fd'][] = $fd;这样写不好吧,如果是为了推送信息到指定的fd,swoole提供了swoole_server::$connections这个可以获取到当前所有连接的fd,或者用swoole_server->connnection_list也是可以的
        707dce772d34:@毛瞌羊1031 你问的这个问题和五楼同学问的一样,版主已经给解答了,记录请求者的fd,调用push方法推送回去就可以了
        毛瞌羊1031:楼主你好,怎么指定发送给某个人啊
        :sweat:
        西岭老湿:@ikkyu 感谢提醒
      • WallanceChuang:前段时间研究了一下goeasy,代码简洁易读,服务稳定。后台推送只需要两行代码, js前端推送也只需要3,4行,而且文档齐全,还提供了后台查询信息收发情况,所以我觉得GoEasy推送服务是个不错的选择。官网: http://goeasy.io/
      • 477351545a90:我想问下 怎么用服务器端向客户端推送啊
        西岭老湿:@mayao 服务端有个push方法
      • Nicksxs:作者你好,这篇文章可以转载吗
        西岭老湿:@Nicksxs 可以的,注明作者或者转载出处就可以了
      • ff420d89b1c2:请问,简书的简信用的是这个技术吗?
        西岭老湿:@不吃西红柿123 这个我就不太清楚了,不好意思
      • allin2:感谢分享,写的不错
        西岭老湿:@悟恋 不会啊,没有奇偶之分啊
        悟恋:为什么创建的聊天客户端还有奇数,偶数之分,同一个服务居然数据不是在一起,而是分开的
        西岭老湿:@allin2 谢谢

      本文标题:利用Swoole实现PHP+websocket 聊天室

      本文链接:https://www.haomeiwen.com/subject/svcnkttx.html