
PHP调用Workerman 5.0实现一对一聊天实战指南
在即时通讯场景中,一对一聊天是最基础且核心的功能。Workerman是一款高性能的PHP Socket框架,其5.0版本对PHP8有了更好的支持。本文将详细讲解如何使用PHP结合Workerman 5.0实现一个稳定、高效的WebSocket一对一聊天系统。
一、环境要求与安装
Workerman 5.0要求PHP版本大于等于8.0。首先,通过Composer安装Workerman:
composer require workerman/workerman ^5.0
安装完成后,我们就可以开始编写服务端代码。
二、服务端核心逻辑实现
一对一聊天的核心在于将用户的唯一标识(UID)与对应的WebSocket连接进行绑定。当用户A向用户B发送消息时,服务端通过B的UID找到对应的连接,并将消息推送过去。
创建服务端文件chat.php,具体代码如下:
<?php
use WorkermanWorker;
require_once __DIR__ . '/vendor/autoload.php';
// 全局数组,保存uid到connection的映射
$uidConnectionMap = [];
$ws_worker = new Worker("websocket://0.0.0.0:8282");
// 设置进程数,单进程方便演示映射关系,生产环境建议多进程配合Redis共享
$ws_worker->count = 1;
$ws_worker->onMessage = function ($connection, $data) use (&$uidConnectionMap) {
$msg = json_decode($data, true);
if (!$msg) {
return;
}
switch ($msg['type']) {
case 'login':
// 绑定uid与connection
$uid = $msg['uid'];
$connection->uid = $uid;
$uidConnectionMap[$uid] = $connection;
$connection->send(json_encode(['type' => 'login', 'status' => 'success']));
break;
case 'chat':
$toUid = $msg['to_uid'];
$content = $msg['content'];
if (isset($uidConnectionMap[$toUid])) {
// 目标用户在线,直接推送
$uidConnectionMap[$toUid]->send(json_encode([
'type' => 'chat',
'from_uid' => $connection->uid,
'content' => $content
]));
} else {
// 目标用户不在线
$connection->send(json_encode(['type' => 'error', 'msg' => '对方不在线']));
}
break;
}
};
$ws_worker->onClose = function ($connection) use (&$uidConnectionMap) {
// 连接断开时清除映射
if (isset($connection->uid)) {
unset($uidConnectionMap[$connection->uid]);
}
};
Worker::runAll();在上述代码中,我们使用全局变量$uidConnectionMap来维护用户ID与连接的映射。客户端在连接后需要先发送login类型的消息进行身份绑定,随后即可通过指定to_uid发送chat类型的消息。
三、客户端前端实现
客户端需要实现WebSocket的连接、登录绑定以及消息的发送与接收。前端代码实现如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Workerman一对一聊天</title>
</head>
<body>
<input type="text" id="uid" placeholder="我的ID">
<button onclick="login()">登录</button><br><br>
<input type="text" id="to_uid" placeholder="对方ID">
<input type="text" id="msg" placeholder="消息内容">
<button onclick="send()">发送</button>
<div id="chat-box" style="margin-top:20px; border:1px solid #ccc; height:300px; overflow-y:auto;"></div>
<script>
// 替换为实际的WebSocket服务地址
var ws = new WebSocket("ws://www.ipipp.com:8282");
var chatBox = document.getElementById('chat-box');
ws.onopen = function() {
chatBox.innerHTML += '<p>连接服务器成功</p>';
};
ws.onmessage = function(e) {
var data = JSON.parse(e.data);
if (data.type === 'chat') {
chatBox.innerHTML += '<p>来自 ' + data.from_uid + ': ' + data.content + '</p>';
} else if (data.type === 'login') {
chatBox.innerHTML += '<p>登录成功</p>';
} else if (data.type === 'error') {
chatBox.innerHTML += '<p style="color:red;">系统: ' + data.msg + '</p>';
}
};
function login() {
var uid = document.getElementById('uid').value;
if (!uid) return alert('请输入ID');
ws.send(JSON.stringify({type: 'login', uid: uid}));
}
function send() {
var toUid = document.getElementById('to_uid').value;
var msg = document.getElementById('msg').value;
if (!toUid || !msg) return alert('请填写对方ID和消息');
ws.send(JSON.stringify({type: 'chat', to_uid: toUid, content: msg}));
chatBox.innerHTML += '<p style="text-align:right;">我: ' + msg + '</p>';
}
</script>
</body>
</html>客户端通过原生的WebSocket API与服务端通信。用户输入自己的ID进行登录绑定,然后输入对方的ID和消息内容进行发送。服务端会将消息路由到指定的UID连接上。
四、运行与测试
在命令行中启动Workerman服务:
php chat.php start
打开两个浏览器窗口,分别访问前端页面。一个窗口登录UID为1,另一个登录UID为2。在UID为1的窗口中,对方ID输入2,填写消息发送,即可在UID为2的窗口中看到收到的消息。
五、生产环境注意事项
本文示例为了直观展示一对一通信原理,使用了PHP变量$uidConnectionMap来存储映射关系,且设置了单进程运行。在生产环境中,Workerman通常会开启多进程以充分利用多核CPU。此时,内存中的变量无法跨进程共享,必须使用Redis等存储组件来维护UID到进程的映射关系,结合Workerman的Channel组件实现跨进程消息推送。此外,还需补充心跳机制定时清理僵尸连接,保证服务长期稳定运行。