首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 實作 HTTP 長輪詢簡易聊天室(Comet 技術)

     編輯:PHP 綜合知識

核心原理

利用 HTTP 長連線(Long Polling / Comet),瀏覽器發起請求後伺服器保持不回應,直到有新訊息或逾時,實現「伺服器推播」效果。本範例採用 iframe + 無限循環讀取文字檔的極簡架構,適合學習原理,生產環境建議使用 WebSocket 或 Server-Sent Events。

三支檔案架構

1. chat.php — 主頁面

<?php
header('Cache-Control: private');
header('Content-Type: text/html; charset=utf-8');
?>
<html>
<script>
function submitChat(form) {
    form.submit();
    form.content.value = '';
}
</script>
<iframe src="chat_content.php" height="300" width="100%"></iframe>
<iframe name="say" height="0" width="0"></iframe>
<form method="POST" target="say" action="say.php" onsubmit="submitChat(this)">
    <input type="text" size="30" name="content" />
    <input type="button" value="說" onclick="submitChat(this.form)" />
</form>
</html>

2. say.php — 寫入訊息

<?php
$content = trim($_POST['content']);
if ($content) {
    $fp = fopen('./chat.txt', 'a');
    fwrite($fp, $content . "\n");
    fclose($fp);
    clearstatcache();
}
?>

3. chat_content.php — 長連線讀取(核心)

<?php
header('Cache-Control: private');
header('Content-Type: text/html; charset=utf-8');
set_time_limit(30);              // 測試用 30 秒,實際應設更長或 0
echo str_repeat(' ', 256);       // IE 需先輸出 256 字元才會顯示後續內容
ob_flush(); flush();

$fp = new SplFileObject('./chat.txt', 'r+');
$totalLine = 0;
while (!$fp->eof()) { $fp->current(); $totalLine++; $fp->next(); }
$fp->seek($totalLine);           // 移至檔案末尾
$lastLine = $totalLine - 1;

while (true) {
    if (!$fp->eof()) {
        $content = trim($fp->current());
        if ($content !== '') {
            echo '<div>' . htmlspecialchars($content) . '</div>';
            flush();             // 立即推送給瀏覽器
            $fp->next();
            $lastLine++;
        }
    } else {
        $fp->seek($lastLine - 1); // 避免 eof 狀態卡住
        $fp->next();
    }
    // 這裡可加入心跳檢測:若客戶端斷線超過 N 秒則 break
    usleep(1000);                // 降低 CPU 佔用
}
?>

關鍵細節解析

  1. set_time_limit(0):生產環境設為 0(無限制),但受限於 Apache Timeout、Nginx proxy_read_timeout、PHP-FPM request_terminate_timeout 等多層設定,需同步調整。
  2. 輸出緩衝:ob_flush(); flush(); 強制將資料送至瀏覽器,否則會被緩衝區攔截。
  3. IE 256 字元門檻:舊版 IE 需先收到 256 字元才開始渲染,現代瀏覽器已無此問題,保留相容無妨。
  4. SplFileObject:提供 seek()、current()、next()、eof() 等介面,比傳統 fgets() 更適合隨機存取大檔案。
  5. 心跳機制:長連線無法偵測客戶端悄悄關閉,建議每 30 秒寫入心跳時間戳,伺服端檢測超時則結束循環釋放資源。

效能與擴充建議

  • 單機檔案鎖定:flock() 或改用 Redis List / MySQL 防止併發寫入損毀。
  • 多進程阻塞:Apache Prefork 每連線佔一進程,長連線極易耗盡 MaxClients。建議改用 Nginx + PHP-FPM 或 Swoole/Workerman 事件驅動架構。
  • 訊息廣播:引入 Redis Pub/Sub 或 ZeroMQ,實現多伺服器節點同步。
  • 前端升級:替換 iframe 為 fetch + ReadableStream 或 EventSource(SSE),體驗更佳。

完整程式碼下載

本範例已整理為獨立專案,包含心跳檢測、Redis 支援、Docker 部署腳本,歡迎至 GitHub 取用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved