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 相关文章推荐
用文本文件制作留言板提示(上)
Oct 09 PHP
php Smarty 字符比较代码
Feb 27 PHP
浅析php中三个等号(===)和两个等号(==)的区别
Aug 06 PHP
理解PHP中的stdClass类
Apr 18 PHP
php实现监控varnish缓存服务器的状态
Dec 30 PHP
php文件上传简单实现方法
Jan 24 PHP
PHP错误处理函数
Apr 03 PHP
Zend Framework常用校验器详解
Dec 09 PHP
PHP基于Redis消息队列实现发布微博的方法
May 03 PHP
php爬取天猫和淘宝商品数据
Feb 23 PHP
php处理多图上传压缩代码功能
Jun 13 PHP
laravel实现一个上传图片的接口,并建立软链接,访问图片的方法
Oct 12 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 多维数组的排序问题 根据二维数组中某个项排序
2011/11/09 PHP
php导入导出excel实例
2013/10/25 PHP
php+mysqli批量查询多张表数据的方法
2015/01/29 PHP
php使用SAE原生Mail类实现各种类型邮件发送的方法
2016/10/10 PHP
PHP实现的解汉诺塔问题算法示例
2018/08/06 PHP
PHP sdk文档处理常用代码示例解析
2020/12/09 PHP
IE浏览器打印的页眉页脚设置解决方法
2009/12/08 Javascript
JavaScript实现统计文本框Textarea字数增强用户体验
2012/12/21 Javascript
javascript操纵OGNL标签示例代码
2014/06/16 Javascript
ECMAScript6块级作用域及新变量声明(let)
2015/06/12 Javascript
实例讲解JS中setTimeout()的用法
2016/01/28 Javascript
JavaScript Math 对象常用方法总结
2016/04/28 Javascript
JavaScript作用域示例详解
2016/07/07 Javascript
详解Vue自定义过滤器的实现
2017/01/10 Javascript
使用Vue写一个datepicker的示例
2018/01/27 Javascript
微信小程序实现tab页面切换功能
2018/07/13 Javascript
vue实现密码显示与隐藏按钮的自定义组件功能
2019/04/23 Javascript
javascript如何实现create方法
2019/11/04 Javascript
vue把输入框的内容添加到页面的实例讲解
2019/11/11 Javascript
JS常见内存泄漏及解决方案解析
2020/05/30 Javascript
[01:03:36]Ti4 循环赛第三日DK vs Titan
2014/07/12 DOTA
[54:54]Newbee vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
python开启多个子进程并行运行的方法
2015/04/18 Python
Python实现Linux中的du命令
2017/06/12 Python
Django实现简单分页功能的方法详解
2017/12/05 Python
Django数据库表反向生成实例解析
2018/02/06 Python
python模块导入的方法
2019/10/24 Python
python GUI库图形界面开发之PyQt5布局控件QGridLayout详细使用方法与实例
2020/03/06 Python
Under Armour安德玛法国官网:美国高端运动科技品牌
2018/06/29 全球购物
计算机科学与技术应届生求职信
2013/11/07 职场文书
银行职业规划书范文
2013/12/28 职场文书
部队领导证婚词
2014/01/12 职场文书
第28个世界无烟日活动总结
2015/02/10 职场文书
评奖评优个人先进事迹材料
2015/11/04 职场文书
七个非常实用的Python工具包总结
2021/06/15 Python
gtx1650怎么样 gtx1650显卡相当于什么级别
2022/04/08 数码科技