其实这个聊天室的DEMO我早都发到Github上了,之前学习Swoole的时候就已经练过手了
之前的同事在群里说他找了一家用Swoole开发的公司,要他做一个简单的聊天室
我最近一直是在找工作的,这就抽空来说一下吧
简单分析一下做聊天室都需要干些什么?
1、首先要有一台WebSocket
服务器
2、使用WebSocket
协议与服务器进行通信
那什么是WebSocket协议呢?
WebSocket
是HTML5
开始提供的一种在单个TCP
连接上进行全双工通讯的协议
在WebSocket API
中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道,两者之间就直接可以数据互相传送
浏览器通过JavaScript
向服务器发出建立WebSocket
连接的请求,连接建立以后,客户端和服务器端就可以通过TCP
连接直接交换数据
简单的来说,WebSocket
只是一个网络通信协议
就像HTTP
、FTP
等都是网络通信的协议,相对于HTTP
这种非持久的协议来说,WebSocket
是一个持久化网络通信的协议
环境依赖:
这就不用多说了,Linux
的服务器,装好PHP
和Swoole
,因为只是一个简单的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
您好,测试了一下,似乎您这个$clientFds数组每次用户登陆就会被新用户的fd覆盖而非把新用户fd push进$clientFds,我也尝试了很多种办法,但一直没法实现$clientFds保存当前所用用户信息的功能,似乎是全局数组在此处没法使用。我在另外一个地方实现了这个功能,https://github.com/kong36088/ChatRoom ,希望能为您提供一个参考,感谢您的博文
@Moechu 是的 我也只是提供一个伪代码
没有广播功能
研究得好深入啊
聊天室:oops: 😳 😳