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自动跳转中英文页面
Jul 29 PHP
php 学习资料零碎东西
Dec 04 PHP
用C/C++扩展你的PHP 为你的php增加功能
Sep 06 PHP
浅析php插件 HTMLPurifier HTML解析器
Jul 01 PHP
PHP 面向对象程序设计(oop)学习笔记(三) - 单例模式和工厂模式
Jun 12 PHP
php简单实现无限分类树形列表的方法
Mar 27 PHP
php中关于socket的系列函数总结
May 18 PHP
php使用GD实现颜色渐变实例
Jun 02 PHP
如何批量清理系统临时文件(语言:C#、 C/C++、 php 、python 、java )
Feb 01 PHP
CentOS系统中PHP安装扩展的方式汇总
Apr 09 PHP
php usort 使用用户自定义的比较函数对二维数组中的值进行排序
May 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
ftp类(myftp.php)
2006/10/09 PHP
PHP中的正规表达式(一)
2006/10/09 PHP
PHP面向对象学习笔记之一 基础概念
2012/10/06 PHP
php递归函数怎么用才有效
2018/02/24 PHP
struts2 jquery 打造无限层次的树
2009/10/23 Javascript
jQuery中filter(),not(),split()使用方法
2010/07/06 Javascript
JQuery UI DatePicker中z-index默认为1的解决办法
2010/09/28 Javascript
JavaScript控制Session操作方法
2013/01/17 Javascript
ie9 提示'console' 未定义问题的解决方法
2014/03/20 Javascript
jquery实现从数组移除指定的值
2015/06/24 Javascript
js实现的动画导航菜单效果代码
2015/09/10 Javascript
zTree实现节点修改的实时刷新功能
2017/03/20 Javascript
JavaScript实现移动端页面按手机屏幕分辨率自动缩放的最强代码
2017/08/18 Javascript
详解利用 Vue.js 实现前后端分离的RBAC角色权限管理
2017/09/15 Javascript
值得收藏的vuejs安装教程
2017/11/21 Javascript
基于express中路由规则及获取请求参数的方法
2018/03/12 Javascript
利用Decorator如何控制Koa路由详解
2018/06/26 Javascript
react 国际化的实现代码示例
2018/09/14 Javascript
详解vue-router数据加载与缓存使用总结
2018/10/29 Javascript
js实现课堂随机点名系统
2019/11/21 Javascript
vue 中this.$set 动态绑定数据的案例讲解
2021/01/29 Vue.js
Python 多进程并发操作中进程池Pool的实例
2017/11/01 Python
浅谈Python Opencv中gamma变换的使用详解
2018/04/02 Python
pycharm打开命令行或Terminal的方法
2019/01/16 Python
在Pycharm中自动添加时间日期作者等信息的方法
2019/01/16 Python
Python数据结构与算法(几种排序)小结
2019/06/22 Python
pandas和spark dataframe互相转换实例详解
2020/02/18 Python
函授自我鉴定
2013/11/06 职场文书
设计部经理的岗位职责
2013/11/16 职场文书
校园报刊亭的创业计划书
2014/01/02 职场文书
片区教研活动总结
2014/07/02 职场文书
2014年艾滋病防治工作总结
2014/12/10 职场文书
2015学校六五普法工作总结
2015/04/22 职场文书
宣传部部长竞选稿
2015/11/21 职场文书
安全学习心得体会范文
2016/01/18 职场文书
大学校园餐饮创业计划书
2019/08/07 职场文书