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的FTP学习(三)
Oct 09 PHP
用PHP程序实现支持页面后退的两种方法
Jun 30 PHP
PHP三元运算符的结合性介绍
Jan 10 PHP
php二维数组排序与默认自然排序的方法介绍
Apr 27 PHP
解析左右值无限分类的实现算法
Jun 20 PHP
php中的PHP_EOL换行符详细解析
Oct 26 PHP
php去除HTML标签实例
Nov 06 PHP
php网站地图生成类示例
Jan 13 PHP
PHP实现支付宝即时到账功能
Dec 21 PHP
php显示页码分页类的封装
Jun 08 PHP
PHP session垃圾回收机制实例分析
Jun 28 PHP
laravel5 Eloquent 实现事务方式
Oct 21 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生成xml简单实例代码
2009/12/16 PHP
PHP简单检测网址是否能够正常打开的方法
2016/09/04 PHP
php实现简单的守护进程创建、开启与关闭操作
2019/08/13 PHP
php 比较获取两个数组相同和不同元素的例子(交集和差集)
2019/10/18 PHP
javascript 闭包
2011/09/15 Javascript
js获取某元素的class里面的css属性值代码
2014/01/16 Javascript
js正则匹配出所有图片及图片地址src的方法
2015/06/08 Javascript
jquery中的常见问题及快速解决方法小结
2016/06/14 Javascript
可输入文字查找ajax下拉框控件 ComBox的实现方法
2016/10/25 Javascript
利用iscroll4实现轮播图效果实例代码
2017/01/11 Javascript
Jquery中attr与prop的区别详解
2017/05/27 jQuery
vue.js分页中单击页码更换页面内容的方法(配合spring springmvc)
2018/02/10 Javascript
使用js实现将后台传入的json数据放在前台显示
2018/08/06 Javascript
vue.js父子组件通信动态绑定的实例
2018/09/28 Javascript
小程序指纹验证的实现代码
2018/12/04 Javascript
JS无限级导航菜单实现方法
2019/01/05 Javascript
JavaScript HTML DOM元素 节点操作汇总
2019/07/29 Javascript
JavaScript实现更换背景图片
2019/10/18 Javascript
JS校验与最终登陆界面功能完整示例
2020/01/13 Javascript
[36:14]DOTA2上海特级锦标赛D组小组赛#1 EG VS COL第二局
2016/02/28 DOTA
Python ORM框架SQLAlchemy学习笔记之关系映射实例
2014/06/10 Python
python调用新浪微博API项目实践
2014/07/28 Python
python实现多线程的两种方式
2016/05/22 Python
python微信跳一跳系列之自动计算跳一跳距离
2018/02/26 Python
python输入多行字符串的方法总结
2019/07/02 Python
使用Dajngo 通过代码添加xadmin用户和权限(组)
2020/07/03 Python
德国最大的网上鞋店之一:Schuhe24.de
2017/06/10 全球购物
美国医生配方营养补充剂供应商:Healthy Directions
2019/07/10 全球购物
毕业生医学检验求职信
2013/10/16 职场文书
财经学院自荐信范文
2014/02/02 职场文书
军人违纪检讨书
2014/02/04 职场文书
金融与证券专业求职信
2014/06/22 职场文书
教育项目合作协议书格式
2014/10/17 职场文书
2015年毕业实习工作总结
2014/12/12 职场文书
优秀教师事迹材料
2014/12/15 职场文书
个人创业事迹材料
2014/12/30 职场文书