一、实时通信的常见场景

在开发Web应用时,我们经常需要实现实时通信功能,比如在线聊天、股票行情推送、游戏对战、协同编辑等。这些场景要求服务器能主动向客户端推送数据,而不是让客户端不断刷新页面。PHP作为一门广泛使用的后端语言,如何选择合适的实时通信方案呢?目前主流的技术方案主要有两种:WebSocket长轮询(Long Polling)

二、WebSocket:全双工实时通信

WebSocket是HTML5引入的一种协议,它允许服务器和客户端建立持久连接,实现真正的全双工通信。相比传统的HTTP请求,WebSocket在建立连接后,双方可以随时发送数据,而不需要频繁建立和断开连接。

技术栈:PHP + Ratchet(WebSocket库)

<?php
// 使用Ratchet库实现WebSocket服务端
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;

// 自定义WebSocket处理类
class MyWebSocketHandler implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    // 当新客户端连接时触发
    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
        echo "New connection: {$conn->resourceId}\n";
    }

    // 收到客户端消息时触发
    public function onMessage(ConnectionInterface $from, $msg) {
        foreach ($this->clients as $client) {
            if ($client !== $from) {
                $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 "An error occurred: {$e->getMessage()}\n";
        $conn->close();
    }
}

// 启动WebSocket服务器
$server = IoServer::factory(
    new HttpServer(new WsServer(new MyWebSocketHandler())),
    8080
);
$server->run();
?>

优点:

  1. 低延迟:数据可以实时推送,无需等待客户端请求。
  2. 高效:连接建立后,通信开销小,适合高频数据交互。
  3. 全双工:服务器和客户端可以同时发送数据。

缺点:

  1. 兼容性:老旧浏览器可能不支持WebSocket。
  2. 服务器资源占用:每个连接都需要维持,高并发时压力较大。

三、长轮询:兼容性更好的替代方案

长轮询是一种模拟实时通信的技术,客户端发送请求后,服务器会保持连接,直到有数据可返回或超时。客户端收到响应后立即发起新的请求,从而实现“伪实时”效果。

技术栈:PHP + jQuery(前端轮询)

<?php
// 长轮询服务端示例
header('Content-Type: application/json');

// 模拟数据变化检测
function checkForUpdates($lastId) {
    $data = ["msg" => "New message", "id" => rand(1, 1000)];
    // 假设数据有更新
    if ($data['id'] > $lastId) {
        return $data;
    }
    return null;
}

$lastId = isset($_GET['lastId']) ? (int)$_GET['lastId'] : 0;
$timeout = 30; // 超时时间(秒)
$startTime = time();

// 长轮询核心逻辑:等待数据更新或超时
while (true) {
    $updates = checkForUpdates($lastId);
    if ($updates !== null) {
        echo json_encode($updates);
        break;
    }
    if (time() - $startTime >= $timeout) {
        echo json_encode(["status" => "timeout"]);
        break;
    }
    sleep(1); // 降低CPU占用
}
?>

前端JavaScript代码:

// 使用jQuery实现长轮询
function longPoll() {
    $.ajax({
        url: 'long_polling.php',
        data: { lastId: lastReceivedId },
        dataType: 'json',
        timeout: 40000, // 略大于服务器超时时间
        success: function(data) {
            if (data.id) {
                console.log("收到新数据:", data.msg);
                lastReceivedId = data.id;
            }
            longPoll(); // 立即发起下一次请求
        },
        error: function() {
            setTimeout(longPoll, 5000); // 出错后延迟重试
        }
    });
}
longPoll(); // 启动长轮询

优点:

  1. 兼容性好:所有浏览器都支持HTTP请求。
  2. 实现简单:不需要额外协议或服务器支持。

缺点:

  1. 高延迟:数据更新后仍需等待客户端发起请求。
  2. 资源浪费:频繁的HTTP请求会增加服务器负担。

四、技术选型建议

应用场景对比

  • WebSocket:适合高频、低延迟场景,如在线游戏、实时聊天。
  • 长轮询:适合兼容性要求高、数据更新不频繁的场景,如通知提醒。

注意事项

  1. WebSocket
    • 需要服务器支持(如Ratchet、Swoole)。
    • 注意处理连接中断和重连逻辑。
  2. 长轮询
    • 设置合理的超时时间,避免服务器资源耗尽。
    • 前端需要处理请求堆积问题。

总结

WebSocket是实时通信的未来,但在兼容性和实现复杂度上存在挑战;长轮询则是一种简单可靠的替代方案。根据项目需求选择合适的技术,甚至可以结合两者使用(如降级方案)。