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 相关文章推荐
Windows下PHP的任意文件执行漏洞
Oct 09 PHP
php 魔术函数使用说明
Feb 21 PHP
php抓取页面的几种方法详解
Jun 17 PHP
计算php页面运行时间的函数介绍
Jul 01 PHP
MacOS 安装 PHP的图片裁剪扩展Tclip
Mar 25 PHP
Ubuntu中启用php的mail()函数并解决发送邮件速度慢问题
Mar 27 PHP
CodeIgniter针对lighttpd服务器URL重写的方法
Jun 10 PHP
php中让人头疼的浮点数运算分析
Oct 10 PHP
yii2利用自带UploadedFile实现上传图片的示例
Feb 16 PHP
php7函数,声明,返回值等新特性介绍
May 25 PHP
基于ThinkPHP5框架使用QueryList爬取并存入mysql数据库操作示例
May 25 PHP
PHP结合Redis+MySQL实现冷热数据交换应用案例详解
Jul 09 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
珊瑚虫IP库浅析
2007/02/15 PHP
关于PHP自动判断字符集并转码的详解
2013/06/26 PHP
九个你必须知道而且又很好用的php函数和特点
2013/08/08 PHP
PHP获取时间排除周六、周日的两个方法
2014/06/30 PHP
PHP 生成N个不重复的随机数
2015/01/21 PHP
php简单实现查询数据库返回json数据
2015/04/16 PHP
PHP实现linux命令tail -f
2016/02/22 PHP
PHP在线打包下载功能示例
2016/10/15 PHP
PHP基于PDO调用sqlserver存储过程通用方法【基于Yii框架】
2017/10/07 PHP
javascript indexOf函数使用说明
2008/07/03 Javascript
了解一点js的Eval函数
2012/07/26 Javascript
jquery()函数的三种语法介绍
2013/10/09 Javascript
简单谈谈JavaScript的同步与异步
2015/12/31 Javascript
Bootstrap入门书籍之(四)菜单、按钮及导航
2016/02/17 Javascript
Bootstrap自动适应PC、平板、手机的Bootstrap栅格系统
2016/05/27 Javascript
浅析JavaScript中的平稳退化(graceful degradation)
2017/07/24 Javascript
layui表格checkbox选择全选样式及功能的实例
2018/03/07 Javascript
JS非行间样式获取函数的实例代码
2018/06/05 Javascript
解决webpack dev-server不能匹配post请求的问题
2018/08/24 Javascript
关于vue编译版本引入的问题的解决
2018/09/17 Javascript
javascript面向对象三大特征之多态实例详解
2019/07/24 Javascript
vue插槽slot的简单理解与用法实例分析
2020/03/14 Javascript
JS实现前端动态分页码代码实例
2020/06/02 Javascript
[03:56]DOTA2完美大师赛趣味视频之小鸽子和Mineski打台球
2017/11/24 DOTA
Python面向对象之类和对象实例详解
2018/12/10 Python
Python3中lambda表达式与函数式编程讲解
2019/01/14 Python
关于tf.reverse_sequence()简述
2020/01/20 Python
python函数超时自动退出的实操方法
2020/12/28 Python
计算机本科生自荐信
2013/10/15 职场文书
大学生饮食配送创业计划书
2014/01/04 职场文书
给学校的建议书范文
2014/05/15 职场文书
服务承诺书怎么写
2014/05/24 职场文书
年终考核实施方案
2014/05/26 职场文书
新娘婚礼致辞
2015/07/27 职场文书
2015年文秘个人工作总结
2015/10/14 职场文书
乡镇干部学习心得体会
2016/01/23 职场文书