首頁  >  文章  >  後端開發  >  php實作長連接方法

php實作長連接方法

小云云
小云云原創
2018-03-21 11:50:004556瀏覽

在伺服器端hold住一個連接, 不立即返回, 直到有數據才返回, 這就是長連接技術的原理,本文主要和大家分享php實現長連接方法,希望能幫助到大家。

長連線技術的關鍵在於hold住一個HTTP請求, 直到有新資料時才回應請求, 然後客戶端再次自動發起長連線請求.

那怎麼樣hold住一個請求呢?伺服器端的程式碼可能看起來像這樣的

set_time_limit(0);  //这句很重要, 不至于运行超时while (true) 
、{    if (hasNewMessage()) 
{        echo json_encode(getNewMessage());        break;
    }
    usleep(100000);      //避免太过频繁的查询}


沒錯,就是透過循環來實現hold住一個請求, 不至於立即返回. 查詢到有新資料之後才回應請求. 然後客戶端處理資料後,再次發起長連線請求.

客戶端的程式碼是像這樣的

<script type="text/javascript">
    (function longPolling() {
        $.ajax({            &#39;url&#39;: &#39;server.php&#39;,            &#39;data&#39;: data,            &#39;dataType&#39;: &#39;json&#39;,            &#39;success&#39;: function(data) {
                processData(data);
                longPolling();
            },            &#39;error&#39;: function(data) {
                longPolling();
            }
        });
    })();</script>

一個簡易的聊天室

##透過長連接, 我們可以開發一個簡易的web聊天室

下面, 我們透過redis開發一個簡易的web聊天室

  1. 每一個客戶端發起長連線時, 在伺服器端產生一個訊息佇列, 對應該用戶. 然後監聽有無新資料, 有則傳回資料到客戶端處理, 並再起發起長連線請求.

  2. 每一個客戶端發起訊息時, 進行訊息佇列的廣播.

下面是程式碼片段:

<?php namespace church\LongPolling;use Closure;use church\LongPolling\Queue\RedisQueue;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\JsonResponse;class Server{
    public $event = [];    public $redisQueue = null;    public $request = null;    public $response = null;    public function __construct()
    {
        $this->redisQueue = new RedisQueue();        $this->request = Request::createFromGlobals();        $this->response = new JsonResponse();
    }    public function on($event, Closure $closure)
    {
        if (is_callable($closure)) {            $this->event[$event][] = $closure;
        }
    }    public function fire($event)
    {
        if (isset($this->event[$event])) {            foreach ($this->event[$event] as $callback) {
                call_user_func($callback, $this);
            }
        }
    }    public function sendMessage($data)
    {
        switch ($data[&#39;type&#39;]) {            case &#39;unicast&#39;:     //单播
                $this->unicast($data[&#39;target&#39;], $data[&#39;data&#39;], $data[&#39;resource&#39;]);                break;            case &#39;multicast&#39;:       //组播
                foreach ($data[&#39;target&#39;] as $target) {                    $this->unicast($target, $data[&#39;data&#39;], $data[&#39;resource&#39;]);
                }                break;            case &#39;broadcast&#39;:       //广播
                foreach ($this->redisQueue->setQueueName(&#39;connections&#39;) as $target) {                    $this->unicast($target, $data[&#39;data&#39;], $data[&#39;resource&#39;]);
                }                break;
        }        $this->fire(&#39;message&#39;);
    }    public function unicast($target, $message, $resource = &#39;system&#39;)
    {
        $redis_queue = new RedisQueue();        $redis_queue->setQueueName($target)->push($resource . &#39;:&#39; . $message);
    }    public function getMessage($target)
    {
        return $this->redisQueue->setQueueName($target)->pop();
    }    public function hasMessage($target)
    {
        return count($this->redisQueue->setQueueName($target));
    }    public function run()
    {
        $data = $this->request->request;        while (true) {            if ($data->get(&#39;action&#39;) == &#39;getMessage&#39;) {                if ($this->hasMessage($data->get(&#39;target&#39;))) {                    $this->response->setData([                        &#39;state&#39; => &#39;ok&#39;,                        &#39;message&#39; => &#39;获取成功&#39;,                        &#39;data&#39; => $this->getMessage($data->get(&#39;target&#39;))
                    ]);                    $this->response->send();                    break;
                }
            } elseif ($data->get(&#39;action&#39;) == &#39;connect&#39;) {                $exist = false;                foreach ($this->redisQueue->setQueueName(&#39;connections&#39;) as $connection) {                    if ($connection == $data->get(&#39;data&#39;)) {                        $exist = true;
                    }
                }                if (! $exist) {                    $this->redisQueue->setQueueName(&#39;connections&#39;)->push($data->get(&#39;data&#39;));   
                }               
                $this->fire(&#39;connect&#39;);                break;
            }
            usleep(100000);
        }
    }
}

已將原始程式碼開源到github

基於長連接開發的web版簡易聊天室,長連接避免了過於頻繁的輪詢. 但伺服器維持一個長連接也有額外的資源消耗. 大並發時性能不理想. 在小型應用裡面可以考慮使用。更建議客戶端使用html5的websocket協定, 伺服器端使用swoole.

相關推薦:

php socket如何實作長連線

#http長連接與短連接原理淺析

PHP中長連接的實作

以上是php實作長連接方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn