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 Http_Template_IT类库进行模板替换
Mar 19 PHP
php设计模式 Proxy (代理模式)
Jun 26 PHP
codeigniter框架批量插入数据
Jan 09 PHP
PHP APC缓存配置、使用详解
Mar 06 PHP
适用于抽奖程序、随机广告的PHP概率算法实例
Apr 09 PHP
php 使用file_get_contents读取大文件的方法
Nov 13 PHP
ThinkPHP3.2.2的插件控制器功能
Mar 05 PHP
php实现将数组转换为XML的方法
Mar 09 PHP
基于php实现随机合并数组并排序(原排序)
Nov 26 PHP
一个简单的php MVC留言本实例代码(必看篇)
Sep 22 PHP
PHP输出XML格式数据的方法总结
Feb 08 PHP
laravel框架 api自定义全局异常处理方法
Oct 11 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
使用CodeIgniter的类库做图片上传
2014/06/12 PHP
用PHP解决的一个栈的面试题
2014/07/02 PHP
点击表单提交时出现jQuery没有权限的解决方法
2014/07/23 Javascript
avalonjs制作响应式瀑布流特效
2015/05/06 Javascript
jquery自定义右键菜单、全选、不连续选择
2016/03/01 Javascript
AngularJS 遇到的小坑与技巧小结
2016/06/07 Javascript
两行代码轻松搞定JavaScript日期验证
2016/08/03 Javascript
JS触发服务器控件的单击事件(详解)
2016/08/06 Javascript
jQuery将表单序列化成一个Object对象的实例
2016/11/29 Javascript
vue实现点击图片放大效果
2017/08/15 Javascript
js字符限制(字符截取) 一个中文汉字算两个字符
2017/09/12 Javascript
vue中使用vue-router切换页面时滚动条自动滚动到顶部的方法
2017/11/28 Javascript
JS函数节流和函数防抖问题分析
2017/12/18 Javascript
React 组件间的通信示例
2018/06/14 Javascript
Vue基础配置讲解
2019/11/29 Javascript
用Python实现QQ游戏大家来找茬辅助工具
2014/09/14 Python
python图像处理之反色实现方法
2015/05/30 Python
Python解惑之True和False详解
2017/04/24 Python
PyCharm代码格式调整方法
2018/05/23 Python
Python3中内置类型bytes和str用法及byte和string之间各种编码转换 问题
2018/09/27 Python
Python实现TCP探测目标服务路由轨迹的原理与方法详解
2019/09/04 Python
Python 限定函数参数的类型及默认值方式
2019/12/24 Python
python中pop()函数的语法与实例
2020/12/01 Python
英国最大的在线奢侈手表零售商:Jura Watches
2018/01/29 全球购物
Luxplus丹麦:香水和个人护理折扣
2018/04/23 全球购物
中兴通讯全球官方网站:ZTE
2020/12/26 全球购物
大学生文员专业个人求职信范文
2014/01/05 职场文书
校园广播稿500字
2014/02/04 职场文书
企业办公室主任岗位职责
2014/02/19 职场文书
旅游与环境专业求职信
2014/06/05 职场文书
上下班时间调整通知
2015/04/23 职场文书
关于军训的感想
2015/08/07 职场文书
php中配置文件保存修改操作 如config.php文件的读取修改等操作
2021/05/12 PHP
CSS3实现列表无限滚动/轮播效果
2021/06/23 HTML / CSS
分位数回归模型quantile regeression应用详解及示例教程
2021/11/02 Python
Python Pygame实战在打砖块游戏的实现
2022/03/17 Python