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操作文件方法问答
Mar 16 PHP
PHP生成HTML静态页面实例代码
Aug 31 PHP
检测png图片是否完整的php代码
Sep 06 PHP
php处理json时中文问题的解决方法
Apr 12 PHP
PHP中文件读、写、删的操作(PHP中对文件和目录操作)
Mar 06 PHP
深入PHP curl参数的详解
Jun 17 PHP
腾讯微博提示missing parameter errorcode 102 错误的解决方法
Dec 22 PHP
各种快递查询--Api接口
Apr 26 PHP
php版微信自动登录并获取昵称的方法
Sep 23 PHP
yii 2.0中表单小部件的使用方法示例
May 23 PHP
PHP使用数组实现矩阵数学运算的方法示例
May 29 PHP
PHP htmlspecialchars_decode()函数用法讲解
Mar 01 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
PHP static局部静态变量和全局静态变量总结
2014/03/02 PHP
基于PHP实现假装商品限时抢购繁忙的效果
2015/10/16 PHP
php组合排序简单实现方法
2016/10/15 PHP
[原创]PHP global全局变量经典应用与注意事项分析【附$GLOBALS用法对比】
2019/07/12 PHP
CSS+JS构建的图片查看器
2006/07/22 Javascript
jQuery checkbox全选/取消全选实现代码
2009/11/14 Javascript
Jquery右下角抖动、浮动 实例代码(兼容ie6、FF)
2013/08/15 Javascript
利用Jquery实现可多选的下拉框
2014/02/21 Javascript
表单提交前触发函数返回true表单才会提交
2014/03/11 Javascript
jQuery解析XML与传统JavaScript方法的差别实例分析
2015/03/05 Javascript
js显示文本框提示文字的方法
2015/05/07 Javascript
利用JS判断字符串是否含有数字与特殊字符的方法小结
2016/11/25 Javascript
关于jQuery中fade(),show()起始位置的一点小发现
2017/04/25 jQuery
React路由管理之React Router总结
2018/05/10 Javascript
vue父子组件的通信方法(实例详解)
2019/11/10 Javascript
javascript设计模式 ? 职责链模式原理与用法实例分析
2020/04/16 Javascript
Python使用urllib2模块实现断点续传下载的方法
2015/06/17 Python
python 寻找list中最大元素对应的索引方法
2018/06/28 Python
django框架使用orm实现批量更新数据的方法
2019/06/21 Python
Python3批量生成带logo的二维码方法
2019/06/24 Python
python求最大值最小值方法总结
2019/06/25 Python
python requests更换代理适用于IP频率限制的方法
2019/08/21 Python
详解Python3定时器任务代码
2019/09/23 Python
Python collections中的双向队列deque简单介绍详解
2019/11/04 Python
django 框架实现的用户注册、登录、退出功能示例
2019/11/28 Python
中国专业的综合网上购物商城:京东
2016/08/02 全球购物
西班牙汉普顿小姐:购买帆布鞋和太阳镜
2016/10/23 全球购物
英国在线花园中心:You Garden
2018/06/03 全球购物
人力资源部培训专员岗位职责
2014/01/02 职场文书
如何打造一封优秀的留学推荐信
2014/01/25 职场文书
群众路线党课主持词
2014/04/01 职场文书
过程装备与控制工程专业求职信
2014/07/02 职场文书
七夕活动策划方案
2014/08/16 职场文书
业绩倒数第一的检讨书
2014/09/24 职场文书
如何避免mysql启动时错误及sock文件作用分析
2022/01/22 MySQL
MySQL视图概念以及相关应用
2022/04/19 MySQL