2013-12-16 136 views
3

我已經成功實現了下一個方案:一個php套接字服務器腳本,它向所有連接的客戶端腳本廣播消息,並且這些客戶端腳本由前端腳本上的事件源監聽。所以,基本上,方案是:server.php - >client.php - >front.php套接字服務器+事件源魔法斷開連接

但我一直在努力與這部分從client.php

while(TRUE) 
{ 
    $read = array(); 
    $read[] = $client_socket; 

    if(socket_select($read, $write, $except, 10) === FALSE){ 
     $errorcode = socket_last_error(); 
     $errormsg = socket_strerror($errorcode); 
     file_put_contents("select_result.log", "Could not listen on socket : [$errorcode] $errormsg".PHP_EOL); 
    } 

    if(!($client_message = @socket_read($client_socket, 1024))){ 
     $errorcode = socket_last_error(); 
     $errormsg = socket_strerror($errorcode); 
     file_put_contents("read_result.log", "Couldn't read socket: [$errorcode] $errormsg".PHP_EOL, FILE_APPEND); 
    } 

    if($client_message == FALSE) { 
     $client_message = "event: keep_alive" . PHP_EOL 
         . "data: keep_alive" . PHP_EOL . PHP_EOL; 
    } 

    echo $client_message; 

    if(ob_get_level() > 0) ob_flush(); 
    flush(); 
} 

當打開front.php腳本(使用事件源)瀏覽器,它工作正常,但也莫名其妙檢測到斷開連接,當我關閉或重新加載瀏覽器窗口中的front.php。下面是部分從server.php檢測斷開:

if(!($client_input = @socket_read($client, 1024))) { 
    $errorcode = socket_last_error(); 
    $errormsg = socket_strerror($errorcode); 
    echo "$errorcode".PHP_EOL; 
} 

if ($client_input === FALSE) { 
    $socket_key = array_search($client, $client_sockets); 
    socket_getpeername($client, $client_address, $client_port); 
    socket_close($client);//?!!! 
    unset($client_sockets[$socket_key]); 
    echo "[$client_address : $client_port disconnected]".PHP_EOL; 
} 

正如你看到的,出現這種情況時,數據不能從客戶端讀取。從理論上講,當我關閉front.php,client.php應該仍然在後臺無限循環中工作,因爲我沒有任何循環中斷,但是 - 意外! - 它停止。在實驗中,我發現這是由於ob_flush()函數造成的。發表評論時,server.php未檢測到斷開連接(瀏覽器窗口關閉/重新加載)。怎麼會這樣?根據手冊,ob_flush()不返回任何價值。沒有檢測到錯誤...我不知道該怎麼想,也找不到爲什麼會發生這種情況。請幫忙。

+1

「front.php」是包含JavaScript等的HTML頁面。這個問題真的可以稱爲「front.html」嗎? (或者,您的HTML客戶端使用EventSource連接到front.php?)。換句話說,在你的瀏覽器運行的JavaScript中,你是做'新的EventSource(「client.php」)'還是做'new EventSource(「front.php」)? –

+0

你是對的,是的,它可以很容易地稱爲'front.html',並且我做'新的EventSource(「client.php」)'; 'client.php'然後打開到'server.php'的套接字連接。 – Nevertheless

回答

1

我假設你正在使用Apache或在client.php前面的東西。當您的front.php運行var es = new EventSource('client.php')時發生的情況是在瀏覽器和Apache之間創建了專用的TCP/IP套接字。 Apache運行PHP,告訴它加載並運行client.php。 (然後client.php創建一個socket來監聽來自server.php的消息。)

當瀏覽器關閉時(或者你打電話給es.close(),或者由於某種原因瀏覽器和Apache之間的套接字連接丟失),那麼Apache將立即(*)關閉PHP進程(運行client.php的進程)。當那個PHP進程消失時,client.php和服務器之間的套接字。PHP會關閉(立即,或下一次server.php嘗試讀取/寫入它)。

*:通常「立即」。有時候,如果一個套接字沒有完全關閉,它可能會暫停一段時間(幾秒鐘,但不會超過一分鐘)。

我想你的ob_flush()觀察是有點紅鯡魚;我的猜測是,通過不呼叫ob_flush(),有一些東西卡在緩衝區中,這意味着Apache會保持PHP進程的活動狀態,直到超時到期。順便說一下,我使用了這個成語; ob_flush()之前的@基本上在做你的ob_get_level()檢查正在做的事情(但我依稀記得聽說有一種情況,檢查並不總是可靠的)。

+0

哇,從你的回答中,我已經完全明白髮生了什麼......然而,你真的幫助我完成了我的項目。乾杯:) – Nevertheless

0

將Chrome瀏覽器的輸出緩衝區刷新到瀏覽器不會像最近的Chrome/Firefox版本所預期的那樣工作。

你需要某種register_shutdown_func()這不穩定,以及你想要的目的。

由於HTTP協議的設計,將客戶端/服務器放入通過瀏覽器加載的PHP腳本通常非常不穩定。它不是爲了保持連接的活躍而設計的,並且以例如IRC的方式發送消息。由於許多原因,HTTP連接可能會在30-60秒後關閉。

我建議你爲客戶端使用服務器和JavaScript的PHP命令行版本。

另外你加載PHP(mod_php在Apache?)的方式會影響整個事情。你打開多少個PHP/Apache的線程/叉,每個進程持有多少個連接......

一些網絡服務器在頁面完成加載幾秒鐘後完成加載時會終止任何腳本,它很難調試並且很難穩定。

在JavaScript中嘗試使用WebRTC/WebSockets,這樣做更好,並可以更好地集成到任何HTML5站點。

+0

謝謝你的迴應;然而,我已經通過各種瀏覽器(舊/新版本,包括IE 8+)和兩個不同的服務器(不提及localhost)測試了這種方案。你誤解了一下,因爲我正在通過命令行運行'server.php',爲客戶端啓動並偵聽'client.php'輸出的JavaScript event-source'front.php'。通過每隔10秒發送一次'data:'(可能會更多)來保持連接。你所說的是合理的,但時間很短 - 我不能用WebSockets重寫所有的東西。這只是小問題'ob_flush'真的困擾我。 – Nevertheless

相關問題