基础介绍
(1)Swoole是一个面向生产环境的 PHP 异步网络通信引擎,使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。 使用 PHP + Swoole 作为网络通信框架,可以使企业 IT 研发团队的效率大大提升。—百度百科
(2)WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 —百度百科
(3)那这篇的主题就是如何使用Swoole+WebSocket实现一个简易的聊天室。
熟悉网络通信协议的同学肯定不会陌生。
功能需求及问题处理
web端:
(1)每次刷新都会生成一个唯一的ID(id值从1开始).
(2)第一次进入网站时会要求用户设置昵称并会与ID进行绑定。
问题点:
(1)刷新页面后用户标识(ID)会重新生成,之前生成ID被弃用。
(2)websocket生成了新的用户ID,但是跟现在的无法形成关联关系。
server端:
(1)当用户进入聊天室后,发送广播给所有人并加入聊天群组(使用redis存储)。
(2)当用户退出直播间后,发送广播给所有人并清除该用户的记录。
(3)用户每发送一次消息都要形成新的记录广播给所有人。
(4)用户生成新的昵称后把昵称推送给他。
web端问题处理方法:
(1)浏览器刷新时提醒用户刷新即将重新获得新的身份。
(2)用户连接成功后记录用户name,每次连接把这个name带上,清除之前该name的绑定关系,形成新的关系。
演示地址
代码实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87<?php
include "RedisManager.php";
$server = new Swoole\WebSocket\Server("0.0.0.0", 8877);
// 客户端连接
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
//连接成功把当前在线的用户返回
$user_list = RedisConnect::getRedis()->hGetAll('message:user');
alone($server, $request->fd, [
'type' => 'first',
'data' => $user_list
]);
});
// 接收客户端发送的消息
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
$message_data = json_decode($frame->data, true);
$type = $message_data['type'];
$data = $message_data['user_name'];
if (isset($message_data['send_message'])) {
$user_message = $message_data['send_message'];
}
switch ($type) {
case 'save_user':
$new_name = '大厦单子' . $frame->fd . '号';
// 修改用户昵称
RedisConnect::getRedis()->hset('message:user', $frame->fd, $new_name);
// 把生成的用户昵称返回给他
alone($server, $frame->fd, [
'type' => 'new_name',
'name' => $new_name,
'id' => $frame->fd
]);
// 广播消息给其他用户
groupSending($server, [
'type' => 'open',
'name' => $new_name,
'id' => $frame->fd
]);
break;
case 'send_message':
$msg = [
'id' => $frame->fd,
'user_name' => $data,
'message' => $user_message,
'type' => 'message'
];
// 接受用户发送的消息
RedisConnect::getRedis()->lpush('message:user:say', $msg);
groupSending($server, $msg);
break;
}
});
// 退出聊天室
$server->on('close', function ($ser, $fd) {
$user = RedisConnect::getRedis()->hget('message:user', $fd);
RedisConnect::getRedis()->hdel('message:user', $fd);
$msg = [
'id' => $fd,
'user_name' => $user,
'message' => '退出聊天室',
'type' => 'close'
];
groupSending($ser, $msg, $fd);
});
// 开启服务
$server->start();
// 群发消息
function groupSending($server, $msg, $self = null)
{
foreach ($server->connections as $conn) {
if ($conn == $self) break; //不再推送给当前退出的用户
$server->push($conn, json_encode($msg));
}
}
// 单独发消息
function alone($server, $fd, $msg)
{
$server->push($fd, json_encode($msg));
}
1 | <!DOCTYPE html> |
总结
这篇文章只是简单的介绍前后端如何实现通信,很多的细节问题没有进行处理。
UI比较low,这里大家只看代码就好了…
更多关于Swoole的功能还待探索…敬请期待…