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 相关文章推荐
珊瑚虫IP库浅析
Feb 15 PHP
php环境配置 php5 MySQL5 apache2 phpmyadmin安装与配置图文教程
Mar 16 PHP
PHP5 面向对象(学习记录)
Dec 02 PHP
php radio 单选框获取与保持值的实现代码
May 15 PHP
让php处理图片变得简单 基于gb库的图片处理类附实例代码下载
May 17 PHP
php UBB 解析实现代码
Nov 27 PHP
PHP数据集构建JSON格式及新数组的方法
Nov 07 PHP
php 下载保存文件保存到本地的两种实现方法
Aug 12 PHP
php在线解压ZIP文件的方法
Dec 30 PHP
php打包网站并在线压缩为zip
Feb 13 PHP
PHP实现的简单排列组合算法应用示例
Jun 20 PHP
PHP+fiddler抓包采集微信文章阅读数点赞数的思路详解
Dec 20 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
PHP4和PHP5性能测试和对比 测试代码与环境
2007/08/17 PHP
php 提速工具eAccelerator 配置参数详解
2010/05/16 PHP
PHP使用CURL获取302跳转后的地址实例
2014/05/04 PHP
PHP切割汉字的常用方法实例总结
2019/04/27 PHP
PHP实现提高SESSION响应速度的几种方法详解
2019/08/09 PHP
PHP基于array_unique实现二维数组去重
2020/07/14 PHP
IE event.srcElement和FF event.target 功能比较
2010/03/01 Javascript
JavaScript 验证码的实例代码(附效果图)
2013/03/22 Javascript
利用javascript实现web页面中指定区域打印
2013/10/30 Javascript
JS实现留言板功能
2017/06/17 Javascript
js实现点击按钮复制文本功能
2020/07/20 Javascript
VSCode使用之Vue工程配置eslint
2019/04/30 Javascript
微信小程序封装多张图片上传api代码实例
2019/12/30 Javascript
JavaScript 接口原理与用法实例详解
2020/05/12 Javascript
javascript实现前端input密码输入强度验证
2020/06/24 Javascript
浅谈JavaScript中this的指向问题
2020/07/28 Javascript
JavaScript实现alert弹框效果
2020/11/19 Javascript
[01:08:48]LGD vs OG 2018国际邀请赛淘汰赛BO3 第三场 8.25
2018/08/29 DOTA
Python解惑之True和False详解
2017/04/24 Python
纯python实现机器学习之kNN算法示例
2018/03/01 Python
python 限制函数执行时间,自己实现timeout的实例
2019/01/12 Python
Python实现二叉搜索树BST的方法示例
2019/07/30 Python
python实现一个点绕另一个点旋转后的坐标
2019/12/04 Python
详解Python在使用JSON时需要注意的编码问题
2019/12/06 Python
关于初始种子自动选取的区域生长实例(python+opencv)
2020/01/16 Python
英国领先的狗和宠物美容专家:Christies Direct
2017/04/03 全球购物
国际礼品店:GiftsnIdeas
2018/05/03 全球购物
Gap英国官网:Gap UK
2018/07/18 全球购物
草莓网中国:StrawberryNet中国
2020/08/17 全球购物
信用社实习人员自我鉴定
2013/09/20 职场文书
军训的自我鉴定
2013/12/10 职场文书
幼儿园中班评语大全
2014/04/17 职场文书
长江三峡导游词
2015/01/31 职场文书
先进教师个人总结
2015/02/11 职场文书
关于远足的感想
2015/08/10 职场文书
干部作风纪律整顿心得体会
2016/01/23 职场文书