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 相关文章推荐
sqlyog 中文乱码问题的设置方法
Oct 19 PHP
Windows下利用Gvim写PHP产生中文乱码问题解决方法
Apr 20 PHP
kohana框架上传文件验证规则写法示例
Jul 14 PHP
php定义参数数量可变的函数用法实例
Mar 16 PHP
php实现将wav文件转换成图像文件并在页面中显示的方法
Apr 21 PHP
CI映射(加载)数据到view层的方法
Mar 28 PHP
PHP之图片上传类实例代码(加了缩略图)
Jun 30 PHP
php实现多维数组排序的方法示例
Mar 23 PHP
PHP无限极分类函数的实现方法详解
Apr 15 PHP
详解PHP5.6.30与Apache2.4.x配置
Jun 02 PHP
IOS 开发之NSDictionary转换成JSON字符串
Aug 14 PHP
Laravel + Elasticsearch 实现中文搜索的方法
Feb 02 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脚本的10个技巧(2)
2006/10/09 PHP
PHP简单系统查询模块代码打包下载
2008/06/07 PHP
PHPExcel 修改已存在Excel的方法
2018/05/03 PHP
Avengerls vs Newbee BO3 第三场2.18
2021/03/10 DOTA
百度留言本js 大家可以参考下
2009/10/13 Javascript
js监听键盘事件示例代码
2013/07/26 Javascript
js实现网页自动刷新可制作节日倒计时效果
2014/05/27 Javascript
防止登录页面出现在frame中js代码
2014/07/22 Javascript
JQuery球队选择实例
2015/05/18 Javascript
javascript基本语法
2016/05/31 Javascript
动态生成的DOM不会触发onclick事件的原因及解决方法
2016/08/06 Javascript
javascript滚轮控制模拟滚动条
2016/10/19 Javascript
JavaScript结合HTML DOM实现联动菜单
2017/04/05 Javascript
vue2.0开发入门笔记之.vue文件的生成和使用
2017/09/19 Javascript
利用Node.js检测端口是否被占用的方法
2017/12/07 Javascript
[01:10]DOTA2次级职业联赛 - Fly战队宣传片
2014/12/01 DOTA
[01:19:33]DOTA2-DPC中国联赛 正赛 iG vs VG BO3 第一场 2月2日
2021/03/11 DOTA
Python 代码性能优化技巧分享
2012/08/07 Python
Python处理字符串之isspace()方法的使用
2015/05/19 Python
python简单实现计算过期时间的方法
2015/06/09 Python
Python爬虫小技巧之伪造随机的User-Agent
2018/09/13 Python
python 实现语音聊天机器人的示例代码
2018/12/02 Python
python实现定时压缩指定文件夹发送邮件
2020/12/22 Python
django最快程序开发流程详解
2019/07/19 Python
python多线程与多进程及其区别详解
2019/08/08 Python
CSS3为背景图设置遮罩并解决遮罩样式继承问题
2020/06/22 HTML / CSS
Stella McCartney官网:成衣、包袋、香水、内衣、童装及Adidas系列
2018/12/20 全球购物
如何将无状态会话Bean发布为WEB服务,只有无状态会话Bean可以发布为WEB服务?
2015/12/03 面试题
机电专业个人求职信范文
2013/12/30 职场文书
优秀部门获奖感言
2014/02/14 职场文书
会议室标语
2014/06/21 职场文书
甘南现象心得体会
2014/09/11 职场文书
党员反对四风思想汇报范文
2014/10/25 职场文书
党校学习党性分析材料
2014/12/19 职场文书
红色经典电影观后感
2015/06/18 职场文书
交互式可视化js库gojs使用介绍及技巧
2022/02/18 Javascript