当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP长连接实现与使用方法详解

发布:smiling 来源: PHP粉丝网  添加日期:2021-09-03 11:34:34 浏览: 评论:0 

这篇文章主要介绍了PHP长连接实现与使用方法,结合实例形式较为详细的分析了php长连接的概念、功能、实现与使用方法,需要的朋友可以参考下。

本文实例讲述了PHP长连接实现与使用方法,分享给大家供大家参考,具体如下:

长连接技术(Long Polling)

在服务器端hold住一个连接, 不立即返回, 直到有数据才返回, 这就是长连接技术的原理

长连接技术的关键在于hold住一个HTTP请求, 直到有新数据时才响应请求, 然后客户端再次自动发起长连接请求.

那怎么样hold住一个请求呢?服务器端的代码可能看起来像这样的

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

没错,就是通过循环来实现hold住一个请求, 不至于立即返回. 查询到有新数据之后才响应请求. 然后客户端处理数据后,再次发起长连接请求.

客户端的代码是像这样的:

  1. <script type="text/javascript"
  2.   (function longPolling() { 
  3.     $.ajax({ 
  4.       'url''server.php'
  5.       'data': data, 
  6.       'dataType''json'
  7.       'success'function(data) { 
  8.         processData(data); 
  9.         longPolling(); 
  10.       }, 
  11.       'error'function(data) { 
  12.         longPolling(); 
  13.       } 
  14.     }); 
  15.   })(); 
  16. </script> 

一个简易的聊天室

通过长连接, 我们可以开发一个简易的web聊天室

下面, 我们通过redis开发一个简易的web聊天室

1. 每一个客户端发起长连接时, 在服务器端生成一个消息队列, 对应该用户. 然后监听有无新数据, 有则返回数据到客户端进行处理, 并再起发起长连接请求.

2. 每一个客户端发起消息时, 进行消息队列的广播.

下面是代码片段:

  1. <?php 
  2. namespace church\LongPolling; 
  3. use Closure; 
  4. use church\LongPolling\Queue\RedisQueue; 
  5. use Symfony\Component\HttpFoundation\Request; 
  6. use Symfony\Component\HttpFoundation\JsonResponse; 
  7. class Server 
  8.   public $event = []; 
  9.   public $redisQueue = null; 
  10.   public $request = null; 
  11.   public $response = null; 
  12.   public function __construct() 
  13.   { 
  14.     $this->redisQueue = new RedisQueue(); 
  15.     $this->request = Request::createFromGlobals(); 
  16.     $this->response = new JsonResponse(); 
  17.   } 
  18.   public function on($event, Closure $closure
  19.   { 
  20.     if (is_callable($closure)) { 
  21.       $this->event[$event][] = $closure
  22.     } 
  23.   } 
  24.   public function fire($event
  25.   { 
  26.     if (isset($this->event[$event])) { 
  27.       foreach ($this->event[$eventas $callback) { 
  28.         call_user_func($callback$this); 
  29.       } 
  30.     } 
  31.   } 
  32.   public function sendMessage($data
  33.   { 
  34.     switch ($data['type']) { 
  35.       case 'unicast':   //单播 
  36.         $this->unicast($data['target'], $data['data'], $data['resource']); 
  37.         break
  38.       case 'multicast':    //组播 
  39.         foreach ($data['target'as $target) { 
  40.           $this->unicast($target$data['data'], $data['resource']); 
  41.         } 
  42.         break
  43.       case 'broadcast':    //广播 
  44.         foreach ($this->redisQueue->setQueueName('connections'as $target) { 
  45.           $this->unicast($target$data['data'], $data['resource']); 
  46.         } 
  47.         break
  48.     } 
  49.     $this->fire('message'); 
  50.   } 
  51.   public function unicast($target$message$resource = 'system'
  52.   { 
  53.     $redis_queue = new RedisQueue(); 
  54.     $redis_queue->setQueueName($target)->push($resource . ':' . $message); 
  55.   } 
  56.   public function getMessage($target
  57.   { 
  58.     return $this->redisQueue->setQueueName($target)->pop(); 
  59.   } 
  60.   public function hasMessage($target
  61.   { 
  62.     return count($this->redisQueue->setQueueName($target)); 
  63.   } 
  64.   public function run() 
  65.   { 
  66.     $data = $this->request->request; 
  67.     while (true) { 
  68.       if ($data->get('action') == 'getMessage') { 
  69.         if ($this->hasMessage($data->get('target'))) { 
  70.           $this->response->setData([ 
  71.             'state' => 'ok'
  72.             'message' => '获取成功'
  73.             'data' => $this->getMessage($data->get('target')) 
  74.           ]); 
  75.           $this->response->send(); 
  76.           break
  77.         } 
  78.       } elseif ($data->get('action') == 'connect') { 
  79.         $exist = false; 
  80.         foreach ($this->redisQueue->setQueueName('connections'as $connection) { 
  81.           if ($connection == $data->get('data')) { 
  82.             $exist = true; 
  83.           } 
  84.         } 
  85.         if (! $exist) { 
  86.           $this->redisQueue->setQueueName('connections')->push($data->get('data')); 
  87.         } 
  88.         $this->fire('connect'); 
  89.         break
  90.       } 
  91.       usleep(100000); 
  92.     } 
  93.   } 

长连接避免了过于频繁的轮询. 但服务器维持一个长连接也有额外的资源消耗. 大并发时性能不理想. 在小型应用里面可以考虑使用。

更建议客户端使用html5的websocket协议, 服务器端使用swoole.

有关swoole, 你可以查看官网:https://www.swoole.com/

Tags: PHP长连接

分享到: