PHP进阶之利用Swoole实现一个简单的WebSocket多人聊天室

其实这个聊天室的DEMO我早都发到Github上了,之前学习Swoole的时候就已经练过手了

之前的同事在群里说他找了一家用Swoole开发的公司,要他做一个简单的聊天室

我最近一直是在找工作的,这就抽空来说一下吧

简单分析一下做聊天室都需要干些什么?

1、首先要有一台WebSocket服务器

2、使用WebSocket协议与服务器进行通信

那什么是WebSocket协议呢?

WebSocketHTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议

WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道,两者之间就直接可以数据互相传送

浏览器通过JavaScript向服务器发出建立WebSocket连接的请求,连接建立以后,客户端和服务器端就可以通过TCP连接直接交换数据

简单的来说,WebSocket只是一个网络通信协议

就像HTTPFTP等都是网络通信的协议,相对于HTTP这种非持久的协议来说,WebSocket是一个持久化网络通信的协议

环境依赖:

这就不用多说了,Linux的服务器,装好PHPSwoole,因为只是一个简单的DEMO,就不存数据了

搭建流程

1、首先有握手信号标识是否成功,成功之后调用回调函数onOpen,这个是可以不设置的,一般用作于欢迎信息之类的

Swoole的文档解释如下:

WebSocket客户端与服务器建立连接并完成握手后会回调此函数

function onOpen(swoole_websocket_server $svr, swoole_http_request $req);
  • $req 是一个Http请求对象,包含了客户端发来的握手请求信息
  • onOpen事件函数中可以调用push向客户端发送数据或者调用close关闭连接
  • onOpen事件回调是可选的

2、当服务器收到来自客户端的数据帧时会回调onMessage函数,客户端发来数据,我们再此函数来将数据广播出去就形成了聊天,经过各种处理形成一个成型的聊天室

Swoole的文档解释如下:

当服务器收到来自客户端的数据帧时会回调此函数

function onMessage(swoole_websocket_server  $server, swoole_websocket_frame $frame);
  • $frame 是swoole_websocket_frame对象,包含了客户端发来的数据帧信息
  • onMessage回调必须被设置,未设置服务器将无法启动
  • 客户端发送的ping帧不会触发onMessage,底层会自动回复pong

3、在onMessage如何发送数据?

WebSocket客户端连接推送数据,长度最大不得超过2M

function swoole_websocket_server->push(int $fd, string $data, int $opcode = 1, bool $finish = true);
  • $fd 客户端连接的ID,如果指定的$fd对应的TCP连接并非websocket客户端,将会发送失败
  • $data 要发送的数据内容
  • $opcode,指定发送数据内容的格式,默认为文本。发送二进制内容$opcode参数需要设置为WEBSOCKET_OPCODE_BINARY
  • 发送成功返回true,发送失败返回false

swoole_websocket_server->push在swoole-1.7.11以上版本可用

代码实现

通过上面的介绍,下面就直接上代码了,在环境目录中间一个名为WebSocket的PHP文件,你也可以换

<?php
// +----------------------------------------------------------------------
// | Swoole聊天室demo
// +----------------------------------------------------------------------
// | IhadPHP [ 学无止境,编码不止,开源为盼 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017 - 2018 https://qq52o.me, All rights reserved.
// +----------------------------------------------------------------------
// | Author: 沈唁 <[email protected]>
// +----------------------------------------------------------------------

# 定义clientFds数组 保存所有websocket连接
$clientFds = [];

# 创建websocket服务
$server = new swoole_websocket_server("0.0.0.0", 9501);
# 握手成功 触发回调函数
$server->on('open', function (swoole_websocket_server $server, $request) use (&$clientFds) {
   # echo "server: handshake success with fd{$request->fd}\n";
   # 将所有客户端连接标识,握手成功后保存到数组中
   $clientFds[] = $request->fd;
});
# 收到消息 触发回调函数
$server->on('message', function (swoole_websocket_server $server, $frame) use (&$clientFds) {
   # echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
   # $server->push($frame->fd, "this is server");
   # 当有用户发送信息,发送广播通知所有用户
   foreach ($clientFds as $fd) {
      $server->push($fd, $frame->data);
   }
});
# 关闭连接 触发回调函数
$server->on('close', function ($ser, $fd) use (&$clientFds) {
   # echo "client {$fd} closed\n";
   # 关闭会话 销毁标识fd
   # 根据value 去数组中找对应的key
   $res = array_search($fd, $clientFds, true);
   unset($clientFds[$res]);
});
# 启动websocket服务
$server->start();

然后我们在cli下启动服务

php /home/wwwroot/default/WebSocket.php

前端搞一个客户端client链接服务器进行通讯,目前大部分浏览器都支持 WebSocket() 接口,所以就直接写了

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>WebSocket聊天室</title>
</head>
<body>
<div id="main" style="width:600px;height: 200px; overflow: auto;border: solid 2px black;">
</div>
<textarea id="textarea"></textarea>
<br/>
<input type="button" value="发送数据" onclick="send()">
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
    var name =prompt("请输入您的昵称","匿名者"); //弹出input框
    // 打开一个 web socket
    var ws = new WebSocket("ws://118.25.224.221:9501");

    ws.onopen = function() {
        console.log("连接成功");
    };

    //收到消息 触发回调
    ws.onmessage = function (evt) {
        var data = evt.data;
        console.log("收到socket服务消息,内容:" + data);
        $('#main').append("<p>" + data + "</p>");
    };

    function send() {
        var data = document.getElementById('textarea').value;
        ws.send(name+ ":"+ data);
    }

    ws.onclose = function() {
        // 关闭 websocket
        console.log("连接已关闭...");
    };
</script>
</body>
</html>

注意替换代码中的ip和端口号,我这边的测试服务器到期了,就不放图片了,代码是没有问题的,我之前都是测试过的,页面比较简陋,就这样吧

测试注意打开控制台~

最后放一个我的项目地址:learn-swoole

5 条评论

发表评论

*