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


Posted in PHP onFebruary 11, 2018

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

长连接技术(Long Polling)

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

长连接技术的关键在于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({
      'url': 'server.php',
      'data': data,
      'dataType': 'json',
      'success': function(data) {
        processData(data);
        longPolling();
      },
      'error': 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['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);
    }
  }
}

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

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

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

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
PHP has encountered an Access Violation
Jan 15 PHP
php adodb操作mysql数据库
Mar 19 PHP
php购物车实现代码
Oct 10 PHP
php自动注册登录验证机制实现代码
Dec 20 PHP
解析在apache里面给php写虚拟目录的详细方法
Jun 24 PHP
PHP生成随机密码类分享
Jun 25 PHP
php读取目录及子目录下所有文件名的方法
Oct 20 PHP
教你在PHPStorm中配置Xdebug
Jul 27 PHP
TP(thinkPHP)框架多层控制器和多级控制器的使用示例
Jun 13 PHP
PHP中十六进制颜色与RGB颜色值互转的方法
Mar 18 PHP
php查看一个变量的占用内存的实例代码
Mar 29 PHP
PHP序列化和反序列化深度剖析实例讲解
Dec 29 PHP
搜索附近的人PHP实现代码
Feb 11 #PHP
PHP的RSA加密解密方法以及开发接口使用
Feb 11 #PHP
php+ajax实现无刷新文件上传功能(ajaxuploadfile)
Feb 11 #PHP
PHP实现的多维数组排序算法分析
Feb 10 #PHP
ThinkPHP整合datatables实现服务端分页的示例代码
Feb 10 #PHP
PHP实现APP微信支付的实例讲解
Feb 10 #PHP
PHP有序表查找之插值查找算法示例
Feb 10 #PHP
You might like
PHP5新特性: 更加面向对象化的PHP
2006/11/18 PHP
php的array_multisort()使用方法介绍
2012/05/16 PHP
php字符串截取的简单方法
2013/07/04 PHP
PHP exif扩展方法开启详解
2014/07/28 PHP
深入理解PHP类的自动载入机制
2016/09/16 PHP
Yii2框架BootStrap样式的深入理解
2016/11/07 PHP
php简单中奖算法(实例)
2017/08/15 PHP
PHP中常用的三种设计模式详解【单例模式、工厂模式、观察者模式】
2019/06/14 PHP
jquery插件珍藏(图片局部放大/信息提示框)
2013/01/08 Javascript
jquery仿QQ商城带左右按钮控制焦点图片切换滚动效果
2013/06/27 Javascript
以jQuery中$.Deferred对象为例讲解promise对象是如何处理异步问题
2015/11/13 Javascript
动态加载JavaScript文件的两种方法
2016/04/22 Javascript
vue.js从安装到搭建过程详解
2017/03/17 Javascript
protractor的安装与基本使用教程
2017/07/07 Javascript
js实现多张图片延迟加载效果
2017/07/17 Javascript
利用百度地图API获取当前位置信息的实例
2017/11/06 Javascript
webpack配置导致字体图标无法显示的解决方法
2018/03/06 Javascript
React 实现车牌键盘的示例代码
2019/12/20 Javascript
Vue elementui字体图标显示问题解决方案
2020/08/18 Javascript
Javascript异步流程控制之串行执行详解
2020/09/27 Javascript
Python创建xml的方法
2015/03/10 Python
Python Json模块中dumps、loads、dump、load函数介绍
2018/05/15 Python
Python使用dict.fromkeys()快速生成一个字典示例
2019/04/24 Python
python实现计算器功能
2019/10/31 Python
python/golang 删除链表中的元素
2020/09/14 Python
使用phonegap创建联系人的实现方法
2017/03/30 HTML / CSS
VICHY薇姿英国官网:全球专业敏感肌护肤领先品牌
2017/07/04 全球购物
法国发饰品牌:Alexandre De Paris
2018/12/04 全球购物
中国领先的汽车保养服务平台:途虎养车
2019/10/18 全球购物
教师简历自我评价
2014/02/03 职场文书
党支部书记岗位责任制
2014/02/11 职场文书
模具专业毕业推荐信
2014/03/08 职场文书
社区节水倡议书
2015/04/29 职场文书
2016廉洁从政心得体会
2016/01/19 职场文书
MySQL删除和插入数据很慢的问题解决
2021/06/03 MySQL
mysql left join快速转inner join的过程
2021/06/30 MySQL