当前位置:首页 > 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);   //避免太过频繁的查询 
  8. } 

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

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

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

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

Tags: PHP长连接

分享到: