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下使用iconv需要注意的问题
Nov 20 PHP
基于php无限分类的深入理解
Jun 02 PHP
利用中国天气预报接口实现简单天气预报
Jan 20 PHP
去掉destoon资讯内容页keywords关键字自带的文章标题的方法
Aug 21 PHP
微信公众平台开发关注及取消关注事件的方法
Dec 23 PHP
PHP实现返回JSON和XML的类分享
Jan 28 PHP
Laravel 5框架学习之模型、控制器、视图基础流程
Apr 08 PHP
php实现图片上传并利用ImageMagick生成缩略图
Mar 14 PHP
PHP获取当前URL路径的处理方法(适用于多条件筛选列表)
Feb 10 PHP
cakephp2.X多表联合查询join及使用分页查询的方法
Feb 23 PHP
PHP count_chars()函数讲解
Feb 14 PHP
PHP操作路由器实现方法示例
Apr 27 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
为了这两部电子管收音机,买了6套全新电子管和10粒刻度盘灯泡
2021/03/02 无线电
PHP往XML中添加节点的方法
2015/03/12 PHP
LBS blog sql注射漏洞[All version]-官方已有补丁
2007/08/26 Javascript
jQuery chili图片远处放大插件
2009/11/30 Javascript
JQuery跳出each循环的方法
2015/04/16 Javascript
JavaScript中利用jQuery绑定事件的几种方式小结
2016/03/06 Javascript
基于WebUploader的文件上传js插件
2016/08/19 Javascript
浅谈js继承的实现及公有、私有、静态方法的书写
2016/10/28 Javascript
Vue.js第三天学习笔记(计算属性computed)
2016/12/01 Javascript
原生js实现旋转木马轮播图效果
2017/02/27 Javascript
Vue.js实现一个漂亮、灵活、可复用的提示组件示例
2017/03/17 Javascript
微信JS SDK接入的几点注意事项(必看篇)
2017/06/23 Javascript
ES6学习教程之块级作用域详解
2017/10/09 Javascript
jQuery+ajax读取json数据并按照价格排序示例
2018/03/28 jQuery
VUE 实现滚动监听 导航栏置顶的方法
2018/09/11 Javascript
React 组件中的 bind(this)示例代码
2018/09/16 Javascript
vue简单练习 桌面时钟的实现代码实例
2019/09/19 Javascript
layer.confirm()右边按钮实现href的例子
2019/09/27 Javascript
JavaScript this在函数中的指向及实例详解
2019/10/14 Javascript
jquery+ajax实现异步上传文件显示进度条
2020/08/17 jQuery
对python中raw_input()和input()的用法详解
2018/04/22 Python
详解多线程Django程序耗尽数据库连接的问题
2018/10/08 Python
Python实现定期检查源目录与备份目录的差异并进行备份功能示例
2019/02/27 Python
python每5分钟从kafka中提取数据的例子
2019/12/23 Python
Python异步编程之协程任务的调度操作实例分析
2020/02/01 Python
解决TensorFlow调用Keras库函数存在的问题
2020/07/06 Python
用python写PDF转换器的实现
2020/10/29 Python
CSS3 3D旋转rotate效果实例介绍
2016/05/03 HTML / CSS
周仰杰(JIMMY CHOO)法国官方网站:闻名世界的鞋子品牌
2019/09/27 全球购物
情人节寄语大全
2014/04/11 职场文书
党员教师群众路线对照检查材料思想汇报
2014/09/29 职场文书
交通事故死亡赔偿协议书
2014/12/03 职场文书
打架检讨书
2015/01/27 职场文书
离婚答辩状怎么写
2015/05/22 职场文书
工作报告范文
2019/06/20 职场文书
httpclient调用远程接口的方法
2022/08/14 Java/Android