使用workerman进行websocket通信/模拟商桥群发消息
学习心得
- 提供
websocket
通信服务的服务器, 像一个跟所有人保持通信的传声筒, 它可以接收来自客户端的消息, 也能主动给客户端发送消息. 而http
通信, 服务器只能跟发送请求的客户端交互, 不能主动发送消息给客户端.
1. 使用workerman进行websocket通信
1.1. workerman的下载和简单开发实例说明
- 访问workerman官网,点击“下载”栏目,在“workerman内核(Linux Windows通用)”框中点击“下载ZIP压缩文件”开始下载workerman文件包,并解压缩(假设解压缩到Workerman目录中):
- 参考workerman说明文档中“简单开发实例”的实例二,创建wk_test.php文件,并拷贝实例二ws_test.php文件中的代码到创建的文件中:
- 在wk_test.php文件所在目录,用git batch启动workerman服务端:
- 打开chrome浏览器,按F12打开调试控制台,在Console一栏输入客户端给服务端发送消息的代码:
1.2. 实例中的细节说明
websocket
链接跟http
链接的区别:http
链接在客户端向服务器发送请求, 服务器返回响应后, 链接就会断开;websocket
链接在链接成功后, 可以持续通信, 链接会一直保持.workerman
应用在服务器中的位置跟Apache
,Nginx
相同. 即,web
服务器.使用
workerman
创建使用websocket
通信的应用服务器的一般步骤:- 创建一个
Worder
应用对象, 传入参数指定应用负责处理websocket
通信.$ws_worker = new Worker("websocket://0.0.0.0:2000");
; 其中的0.0.0.0:2000
表示接受任意IP客户端发送到服务器2000
端口的链接请求. - 启动提供
websocket
通信的系统进程数.$ws_worker->count = 4;
; - 指定收到客户端发送来数据后的处理数据的方法脚本:
$ws_worker->onMessage = function($connection, $data) {...}
; 其中参数$connection
是发送数据的客户端与服务器的websocket
链接;$data
是客户端发送过来的数据. - 指定当客户端断开链接时的处理脚本.
$ws_worker->onClose = function($connection) {...}
; 其中参数$connection
是即将关闭的websocket
链接. - 启动
Worker
应用. - 服务端使用
websocket
链接的send(消息字符串)
向该链接的客户端发送消息数据.$connection->send('hello!');
Worder
对象的connections
属性是当前与服务器保持通讯的websocket
链接数组.
- 创建一个
客户端与提供
websocket
通信的服务器交互的一般步骤:- 创建一个跟服务器进行
websocket
通信的链接:ws = new WebSocket("ws://127.0.0.1:2000");
. 其中127.0.0.1
是客户端要与之创建websocket
链接的服务器地址,2000
是服务器监听链接和数据收发的端口号. - 指定当连接创建成功时的回调处理方法, 一般链接创建成功后, 就发送唯一标识当前客户端信息的数据, 如保存在
cookie
中的用户id
等.ws.onopen = function() {// 一般发送客户端的唯一标识给服务器存储 }
- 指定收到服务器返回的消息时的处理方法脚本.
ws.onmessage = function(e) {// 收到服务器的消息了, 处理消息数据吧}
- 客户端同样使用
websocket
连接的send(消息字符串)
方法向服务器发送数据.
- 创建一个跟服务器进行
2. 模拟商桥群发消息
- 实现思路: 服务器类内置一个
connections
属性数组,connections
属性保存有所有成功跟客户端创建链接的连接对象. 当某个客户端给服务器发送消息时, 服务器遍历connections
中链接对象, 给连接对象的客户端发送消息, 即可实现群发.
代码
- 1- 服务端
<?php
use Workerman\Worker;
require_once __DIR__ . '/workerman/Autoloader.php';
// 注意:这里与上个例子不同,使用的是websocket协议
$ws_worker = new Worker("websocket://0.0.0.0:2000");
// 启动4个进程对外提供服务
$ws_worker->count = 4;
// 当接收到客户端发来的消息时的处理方法
$ws_worker->onMessage = function($connection, $data)
{
// 发送消息的连接id
$message['from_id'] = $connection->id;
// 消息内容
$message['msg'] = $data
// 转成json字符串
$jsonStr = json_encode($message);
// 遍历当前所有的链接对象, 给连接对象对应的客户端发送数据
foreach($ws_worker->connection as $conn) {
$connection->send($jsonStr);
}
}
// 运行worker
Worker::runAll();
- 2- 客户端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>"百度商桥"客户端</title>
<link rel="stylesheet" href="/static/plugin/layui/css/layui.css" media="all">
<script src="/static/plugin/layui/layui.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #fafafa;
}
.im {
width: 400px;
height: 520px;
background-color: wheat;
margin: 40px auto;
padding: 10px;
}
.im .history {
background-color: white;
border: 1px solid #aaa;
width: 100%;
height: 290px;
padding: 5px;
overflow-y: auto;
}
.im .inputing {
background-color: white;
border: 1px solid #aaa;
width: 100%;
height: 150px;
margin-top: 10px;
padding: 5px;
outline: none;
}
.im .btn-area {
width: 100%;
text-align: right;
margin-top: 10px;
}
.msg-box-friend {
padding: 10px;
width: 75%;
margin: 5px auto 0 5px;
background-color: lightblue;
border: 1px solid #ccc;
border-radius: 5px;
/* overflow-x: wordwrap; */
white-space:normal;
}
.msg-box-me {
padding: 10px;
width: 75%;
margin: 5px 5px 0 auto;
background-color: wheat;
border: 1px solid #ccc;
border-radius: 5px;
/* overflow-x: wordwrap; */
white-space:normal;
}
</style>
</head>
<body>
<div class="im">
<div class="history">
</div>
<!-- contenteditable="true", 这个div就可编辑了 -->
<div class="inputing" contenteditable="true">
</div>
<div class="btn-area">
<span class="layui-btn layui-btn-success" onclick="send()">发送</span>
</div>
</div>
</body>
<script>
layui.use(['layer'], function() {
layer = layui.layer;
});
// 假设服务端ip为127.0.0.1
ws = new WebSocket("ws://127.0.0.1:2000");
/* 当客户端连通服务器端的时候 */
ws.onopen = function() {
// ...
};
ws.onmessage = function(e) {
// alert("收到服务端的消息:" + e.data);
var receive = JSON.parse(e.data);
var str = "<div class='msg-box-friend'>"+receive.from_id+"说: "+receive.msg+"</div>";
$(str).appendTo('.history');
};
function send() {
// 消息框
var str = "<div class='msg-box-me'>我说: "+$('.inputing').html()+"</div>";
var data = {};
// 标识此次发送的数据是发送消息
data.type='msg';
// 标识是从客服端发的
data.group = 'admin'
// 标识私聊的对象id,0标识群发. DEL_客户连接时由系统随机分配客服小姐姐, 不需要手动指定了
// data.sendId = 0;
// 要发送的消息
data.msg = $('.inputing').html();
// 在消息历史中显示发送的消息
$(str).appendTo('.history');
// 发送JSON格式的数据
ws.send(JSON.stringify(data));
$('.inputing').html('');
}
</script>
</html>