
一、安装workerman
命令运行:
composer require topthink/think-worker (或 composer require workerman/workerman)
二、配置config/worker_server.php文件里面的worker_class参数(注意:反斜杠不能写错),如下图所示:
三、在上图worker_class参数路径创建文件worker.php,如下图所示:
文件内容如下:
<?php
namespace app\index\controller;
use think\facade\Db;
use think\worker\Server;
use Workerman\Lib\Timer;
//define('HEARTBEAT_TIME', 20); // 心跳间隔
class Worker extends Server
{
protected $socket = 'websocket://0.0.0.0:2345/wss'; //设置地址
protected $online_count = 0; //统计在线人数
/* 此处为实现SSL证书wss连接
protected $context = [
'ssl' => [
'local_cert' => 'C:/Apache24/ssl/api_hilo8_com.crt', //证书文件的存放路径(请改成自己的路径)
'local_pk' => 'C:/Apache24/ssl/api_hilo8_com.key', //证书私钥的存放路径(请改成自己的路径)
'verify_peer' => false,
//'allow_self_signed' => true, //如果是自签名证书需要开启此选项
],
];
protected $option = [
'transport' => 'ssl', //设置transport开启ssl,启用wss://
];
*/
/**
* 收到信息
* @param $connection
* @param $data
*/
public function onMessage($connection, $data)
{
$origin = json_decode($data, true);
switch ($origin['type']) {
//绑定用户
case 'login':
$connection->uid = $origin['uid'];
$connection->uname = $origin['uname'];
$connection->login_time = time();
$this->online_count++;
$this->onlineUid();
$send_content = [
'type' => 'text',
'content' => '用户(' . $connection->uid . '=' . $connection->uname . ')已上线!' . $connection->login_time
];
$this->sendMessage($send_content);
break;
//收到消息,并回复
case 'text':
$user = [];
$user['from_uid'] = $origin['from_uid']; // 发送方用户uid
$user['from_uname'] = $origin['from_uname']; //发送方用户名称
$user['to_uid'] = $origin['to_uid']; // 接收方用户uid
$user['to_uname'] = $origin['to_uname']; //接收方用户名称
$content = $origin['content']; // 需要发送到对方的内容
$send_content = [
'type' => 'text',
'user' => $user,
'content' => $content
];
$this->sendMessage($send_content, $origin['to_uid']);
break;
}
echo ("收到消息:{$data}\n");
}
/**
* 当连接建立时触发的回调函数
* @param $connection
*/
public function onConnect($connection)
{
$link_time = time();
echo "连接时间:{$link_time}\n";
$send_content = [
'type' => 'connect',
'content' => '连接成功:' . $link_time
];
$connection->send(json_encode($send_content, JSON_UNESCAPED_UNICODE));
}
/**
* 当连接断开时触发的回调函数
* @param $connection
*/
public function onClose($connection)
{
if (isset($connection->uid)) {
echo "连接关闭 " . $connection->uid . "=" . $connection->uname . " (time:" . time() . ")\n";
$this->online_count--;
$send_content = [
'type' => 'text',
'content' => '用户(' . $connection->uid . '=' . $connection->uname . ')已下线!' . time()
];
$this->sendMessage($send_content);
}
}
/**
* 当客户端的连接上发生错误时触发
* @param $connection
* @param $code
* @param $msg
*/
public function onError($connection, $code, $msg)
{
echo "error $code $msg\n";
}
/**
* 每个进程启动
* @param $worker
*/
public function onWorkerStart($worker)
{
Timer::add(10, function () use ($worker) {
if ($this->online_count > 0) {
$this->onlineUid();
}
});
}
//统计在线用户
private function onlineUid()
{
$online_uid = $online_data = [];
foreach ($this->worker->connections as $connection) {
if (isset($connection->uid)) {
//array_push($online_uid, $connection->uid . '=' . $connection->uname);
$online_data[] = ['uid' => $connection->uid, 'uname' => $connection->uname];
}
}
$send_content = [
'type' => 'online',
'count' => $this->online_count,
'content' => $online_data
];
$this->sendMessage($send_content);
}
//发送消息
private function sendMessage($content = [], $uid = 0)
{
$message = json_encode($content, JSON_UNESCAPED_UNICODE);
if (is_numeric($uid) && $uid > 0) {
//单发消息
foreach ($this->worker->connections as $connection) {
if (isset($connection->uid) && $connection->uid == $uid) {
$connection->send($message);
}
}
} else {
//群发消息
foreach ($this->worker->connections as $connection) {
$connection->send($message);
}
}
}
}
说明:虽然上面可以实现SSL证书wss连接,但不建议这样做,建议以服务器代理方式实现。
四、启动WebSocket,命令:php think worker:server
五、客户端连接,代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket聊天</title>
<script type="text/javascript" src="https://www.hilo8.com/js/jquery.js"></script>
<style>
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
.container {
position: relative;
height: 100%;
overflow: auto;
}
.online-box {
position: absolute;
width: 150px;
height: 100%;
background-color: antiquewhite;
}
.online-box h4 {
text-align: center;
}
.online-box ul li {
cursor: pointer;
padding: 5px 0px;
font-size: 14px;
}
.online-box ul li:hover {
color: #c00;
}
.main-box {
margin-left: 150px;
}
#msg {
color: #999;
font-size: 12px;
background-color: #f6f6f6;
padding: 10px;
}
#msg p {
margin: 10px 0px 0px 0px;
}
#msg p span {
border: 1px solid #ddd;
border-radius: 4px;
padding: 3px 5px;
display: block;
font-size: 14px;
color: #333;
background-color: #fff;
float: left;
}
#msg p span.self {
background-color: #090;
color: #fff;
float: right;
}
#msg font{font-size: 12px; color: #999; display: block;}
.clear {
clear: both;
*zoom: 1
}
.clear:after {
content: '\20';
clear: both;
*zoom: 1;
display: block;
height: 0
}
</style>
</head>
<body>
<div class="container">
<div class="online-box">
<h4>在线用户列表</h4>
<ul>
<li data-user="0,所有人">所有人</li>
</ul>
</div>
<div class="main-box">
<h1>WebSocket示例</h1>
<p>
<input type="text" id="uname" placeholder="请输入您的名称" />
<button type="button">连接</button>
<button type="button">关闭</button>
</p>
<p>
接收人: <span id="to-uname-span">所有人</span><br>
<input type="hidden" id="to-uid" value="0"><input type="hidden" id="to-uname" value="所有人">
<input type="text" id="content" placeholder="请输入要发送的内容" />
<button>发送</button>
</p>
<hr>
<div id="msg" class="clear"></div>
</div>
</div>
<script>
$(function(){
var socket;
var uid = Math.floor(Math.random() * 10000) + 1; //随机生成用户id号
var uname = ''; //用户名称
/* 连接状态 */
var state = 0;
var sockState = function () {
let status = ['未连接', '连接成功,可通信', '正在关闭', '连接已关闭或无法打开'];
return status[state];
}
//显示服务器返回消息
var showMsg = function (message) {
$("#msg").append('<p class="clear">' + message + '</p>');
}
/* 打开连接事件 */
$("button:eq(0)").click(function () {
uname = $('#uname').val();
if (uname == '') {
alert('请输入您的名称再连接');
return;
}
try {
/* 连接 */
socket = new WebSocket("ws://api.shiyunkj.com:2345/wss");
/* 绑定事件 */
/* 监听连接状态 */
socket.onopen = function () {
state = socket.readyState;
socket.send(JSON.stringify({ type: 'login', uid: uid, uname: uname }));
};
/* 接收消息 */
socket.onmessage = function (e) {
let res = JSON.parse(e.data);
switch (res.type) {
case 'online':
let online_count = $('.online-box ul li').length - 1;
let lis = '<li data-user="0,所有人">所有人</li>';
if (online_count != res.count) {
$.each(res.content, function (key, val) {
lis += '<li data-user="' + val.uid + ',' + val.uname + '">' + val.uname + '</li>';
})
$('.online-box ul').html(lis);
}
break;
case 'connect':
case 'text':
let str = '';
if (res.user != undefined) {
let user = res.user.from_uname || '';
if (user != '') { str = '<font>'+user+'</font>'; }
}
showMsg(str+"<span>" + res.content + '</span>');
break;
default:
showMsg("接收信息出错...");
}
//showMsg("<br>" + res.content);
};
/* 关闭连接 */
socket.onclose = function () {
state = socket.readyState;
showMsg("连接已关闭或无法连接服务器..." + state);
};
} catch (exception) {
showMsg("有错误发生...");
}
});
/* 发送数据事件 */
$("button:eq(2)").click(function () {
if (state != 1) {
alert(sockState());
return;
} else if ($("#content").val() == "") {
alert("请输入发送内容!");
return;
}
try {
showMsg('<span class="self">' + $("#content").val() + '</span>');
let data = { type: "text", from_uid: uid, from_uname: uname, to_uid: $("#to-uid").val(), to_uname: $("#to-uname").val(), content: $("#content").val() };
socket.send(JSON.stringify(data));
} catch (exception) {
showMsg("发送数据出错...");
}
/* 清空文本框 */
$("#content").val("");
});
/* 断开连接 */
$("button:eq(1)").click(function () {
try {
showMsg("关闭连接...");
socket.close();
} catch (exception) {
alert(sockState());
}
});
//选择用户
$('.online-box ul').on('click', 'li', function () {
let arr = $(this).data('user').split(',');
$('#to-uid').val(arr[0]);
$('#to-uname').val(arr[1]);
$('#to-uname-span').html(arr[1]);
});
});
</script>
</body>
</html>
可以多人在线聊天,如下图演示:
上一篇:TP6 生成Qrcode二维码和Barcode条形码的方法
讨论数量:0