php長接続を実現

13553 ワード

ロングコネクション技術(Long Polling)
サーバ側で1つの接続をholdし、すぐに返さず、データがあるまで返さないのが長接続技術の原理です.
長接続技術の鍵は、新しいデータがあるまでHTTP要求に応答し、クライアントが再び長接続要求を自動的に開始することにある.
では、どうやってお願いをしますか?サーバ側のコードはこのように見えるかもしれません
set_time_limit(0);  //     ,        
while (true) {
    if (hasNewMessage()) {
        echo json_encode(getNewMessage());
        break;
    }
    usleep(100000);      //         
}

そう、すぐに戻ることなく、ループによってholdを実現する要求である.要求に応答するには、新しいデータがクエリされる.その後、クライアントがデータを処理する後、再び長接続要求を開始する.
クライアントのコードはこのようなものです
<script type="text/javascript">
    (function longPolling() {
        $.ajax({
            'url': 'server.php',
            'data': data,
            'dataType': 'json',
            'success': function(data) {
                processData(data);
                longPolling();
            },
            'error': function(data) {
                longPolling();
            }
        });
    })();
script>

簡易なチャットルーム
長い接続で簡単なWebチャットルームを開発できます
次に、redisを通じて簡単なwebチャットルームを開発します.
  • クライアントごとに長い接続が開始すると、サーバ側でメッセージキューが生成され、当該ユーザに対して.その後、新しいデータの有無を傍受する、ある場合はデータをクライアントに返す処理を行い、長い接続要求を再開する.
  • クライアント毎にメッセージが開始すると、メッセージキューのブロードキャストが行われる.

  • 次はコードクリップです.
     
    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['type']) {
                case 'unicast':     //  
                    $this->unicast($data['target'], $data['data'], $data['resource']);
                    break;
                case 'multicast':       //  
                    foreach ($data['target'] as $target) {
                        $this->unicast($target, $data['data'], $data['resource']);
                    }
                    break;
                case 'broadcast':       //  
                    foreach ($this->redisQueue->setQueueName('connections') as $target) {
                        $this->unicast($target, $data['data'], $data['resource']);
                    }
                    break;
            }
            $this->fire('message');
        }
        public function unicast($target, $message, $resource = 'system')
        {
            $redis_queue = new RedisQueue();
            $redis_queue->setQueueName($target)->push($resource . ':' . $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('action') == 'getMessage') {
                    if ($this->hasMessage($data->get('target'))) {
                        $this->response->setData([
                            'state' => 'ok',
                            'message' => '    ',
                            'data' => $this->getMessage($data->get('target'))
                        ]);
                        $this->response->send();
                        break;
                    }
                } elseif ($data->get('action') == 'connect') {
                    $exist = false;
                    foreach ($this->redisQueue->setQueueName('connections') as $connection) {
                        if ($connection == $data->get('data')) {
                            $exist = true;
                        }
                    }
                    if (! $exist) {
                        $this->redisQueue->setQueueName('connections')->push($data->get('data'));   
                    }               
                    $this->fire('connect');
                    break;
                }
    
                usleep(100000);
            }
        }
    }

    ソースコードをgithubにオープンソースしました
    長接続に基づいて開発されたweb版簡易チャットルーム
    長い接続は頻繁なポーリングを避ける.しかし、サーバが長い接続を維持することは、追加のリソース消費もある.大同時時の性能は理想的ではない.スモールアプリケーションでの使用も考えられます
    クライアントはhtml 5のwebsocketプロトコルを使用することを提案し、サーバ側はswooleを使用する.
    swooleについては、ここを突いてもいいです.