本文目录导读:

我来为你详细介绍使用PHP搭建实时聊天系统的几种方案。
WebSocket + WebSocket服务器(推荐)
安装Ratchet WebSocket服务器
composer require cboden/ratchet
WebSocket服务器端代码(server.php)
<?php
require 'vendor/autoload.php';
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
class Chat implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
echo "Chat server started\n";
}
public function onOpen(ConnectionInterface $conn) {
// 新客户端连接
$this->clients->attach($conn);
echo "New connection: ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
$numRecv = count($this->clients) - 1;
echo sprintf(
'Connection %d sending message "%s" to %d other connection%s' . "\n",
$from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's'
);
// 广播消息给所有客户端
foreach ($this->clients as $client) {
if ($from !== $client) {
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "Error: {$e->getMessage()}\n";
$conn->close();
}
}
// 启动WebSocket服务器
$server = IoServer::factory(
new HttpServer(
new WsServer(
new Chat()
)
),
8080
);
echo "Server running on port 8080...\n";
$server->run();
客户端前端代码(index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">PHP实时聊</title>
<style>
#messages {
border: 1px solid #ccc;
height: 300px;
overflow-y: scroll;
padding: 10px;
margin-bottom: 10px;
}
#input-area {
margin-top: 10px;
}
</style>
</head>
<body>
<h2>实时聊天室</h2>
<div id="messages"></div>
<div id="input-area">
<input type="text" id="message" placeholder="输入消息...">
<button onclick="sendMessage()" id="sendBtn">发送</button>
</div>
<script>
// 连接WebSocket服务器
const ws = new WebSocket('ws://localhost:8080');
const messages = document.getElementById('messages');
const messageInput = document.getElementById('message');
const sendBtn = document.getElementById('sendBtn');
ws.onopen = function() {
console.log('连接成功');
addMessage('系统', '已连接到聊天服务器');
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
addMessage(data.username, data.message);
};
ws.onclose = function() {
console.log('连接关闭');
addMessage('系统', '已断开连接');
sendBtn.disabled = true;
};
function sendMessage() {
const message = messageInput.value.trim();
if (message && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
username: '用户' + Math.floor(Math.random() * 1000),
message: message
}));
messageInput.value = '';
}
}
function addMessage(username, message) {
const div = document.createElement('div');
div.innerHTML = `<strong>${username}:</strong> ${message}`;
messages.appendChild(div);
messages.scrollTop = messages.scrollHeight;
}
// 按Enter发送消息
messageInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
</script>
</body>
</html>
启动服务器
php server.php
AJAX轮询(简单实现)
服务器端(chat_server.php)
<?php
session_start();
// 消息存储示例(实际应用使用数据库)
$messagesFile = 'messages.json';
if (!file_exists($messagesFile)) {
file_put_contents($messagesFile, json_encode([]));
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 发送消息
$data = json_decode(file_get_contents('php://input'), true);
$messages = json_decode(file_get_contents($messagesFile), true);
$newMessage = [
'username' => $data['username'] ?? '匿名',
'message' => $data['message'],
'time' => time()
];
array_push($messages, $newMessage);
// 只保留最近100条消息
if (count($messages) > 100) {
$messages = array_slice($messages, -100);
}
file_put_contents($messagesFile, json_encode($messages));
echo json_encode(['status' => 'success']);
} elseif ($_SERVER['REQUEST_METHOD'] === 'GET') {
// 获取消息
$lastTime = $_GET['lastTime'] ?? 0;
$messages = json_decode(file_get_contents($messagesFile), true);
$newMessages = array_filter($messages, function($msg) use ($lastTime) {
return $msg['time'] > $lastTime;
});
echo json_encode(array_values($newMessages));
}
客户端(ajax_chat.html)
<!DOCTYPE html>
<html>
<head>AJAX轮询聊天</title>
<style>
#messages {
border: 1px solid #ccc;
height: 300px;
overflow-y: scroll;
padding: 10px;
}
</style>
</head>
<body>
<h2>AJAX轮询聊天</h2>
<div id="messages"></div>
<div>
<input type="text" id="username" placeholder="用户名">
<input type="text" id="message" placeholder="消息">
<button onclick="sendMessage()">发送</button>
</div>
<script>
let lastTime = 0;
// 发送消息
function sendMessage() {
const username = document.getElementById('username').value || '匿名';
const message = document.getElementById('message').value;
if (!message) return;
fetch('chat_server.php', {
method: 'POST',
body: JSON.stringify({
username: username,
message: message
})
}).then(response => response.json())
.then(data => {
console.log('Message sent:', data);
document.getElementById('message').value = '';
});
}
// 轮询获取新消息
function pollMessages() {
fetch(`chat_server.php?lastTime=${lastTime}`)
.then(response => response.json())
.then(messages => {
messages.forEach(msg => {
addMessage(msg.username, msg.message);
lastTime = msg.time;
});
});
setTimeout(pollMessages, 1000); // 1秒轮询一次
}
function addMessage(username, message) {
const div = document.getElementById('messages');
const msgDiv = document.createElement('div');
msgDiv.innerHTML = `<strong>${username}:</strong> ${message}`;
div.appendChild(msgDiv);
div.scrollTop = div.scrollHeight;
}
// 启动轮询
pollMessages();
</script>
</body>
</html>
Server-Sent Events (SSE)
服务器端(sse_server.php)
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
// 模拟实时消息
$counter = 0;
while (true) {
$counter++;
// 这里可以从数据库或队列获取真实消息
$newData = [
'username' => '用户' . rand(1, 1000),
'message' => "这是第 {$counter} 条实时消息",
'time' => time()
];
echo "data: " . json_encode($newData) . "\n\n";
flush();
// 每2秒发送一次
sleep(2);
}
客户端(sse_chat.html)
<!DOCTYPE html>
<html>
<head>SSE实时聊天</title>
</head>
<body>
<h2>SSE实时聊天</h2>
<div id="messages"></div>
<script>
if (typeof EventSource !== 'undefined') {
const source = new EventSource('sse_server.php');
source.onmessage = function(event) {
const data = JSON.parse(event.data);
addMessage(data.username, data.message);
};
source.onerror = function() {
console.log('连接错误,尝试重新连接...');
};
} else {
alert('您的浏览器不支持SSE');
}
function addMessage(username, message) {
const div = document.getElementById('messages');
const msgDiv = document.createElement('div');
msgDiv.innerHTML = `<strong>${username}:</strong> ${message}`;
div.appendChild(msgDiv);
}
</script>
</body>
</html>
数据库集成示例
MySQL数据库表结构
CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
使用PDO存储消息
<?php
class MessageDatabase {
private $pdo;
public function __construct() {
$this->pdo = new PDO(
'mysql:host=localhost;dbname=chat_db;charset=utf8',
'username',
'password'
);
}
public function saveMessage($username, $message) {
$stmt = $this->pdo->prepare(
'INSERT INTO messages (username, message) VALUES (?, ?)'
);
return $stmt->execute([$username, $message]);
}
public function getRecentMessages($limit = 50) {
$stmt = $this->pdo->prepare(
'SELECT * FROM messages ORDER BY created_at DESC LIMIT ?'
);
$stmt->execute([$limit]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
推荐方案
对于生产环境,建议使用:
- WebSocket方案:适用于需要真正实时的聊天应用
- 加上Redis:用于消息队列和缓存
- 使用专业聊天云服务:如Pusher或Firebase
安全提示
- 输入验证:对所有用户输入进行验证和过滤
- XSS防护:输出时进行HTML转义
- 速率限制:防止滥用和洪水攻击
- 认证授权:实现用户登录系统
选择哪种方案取决于你的具体需求:实时性、并发量和预算等因素。