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 日,周,月点击排行统计
Jan 11 PHP
php循环语句 for()与foreach()用法区别介绍
Sep 05 PHP
用php实现百度网盘图片直链的代码分享
Nov 01 PHP
PHP函数in_array()使用详解
Aug 20 PHP
PHP实现图片旋转效果实例代码
Oct 01 PHP
php中current、next与reset函数用法实例
Nov 17 PHP
PHP统计目录中文件以及目录中目录大小的方法
Jan 09 PHP
PHP自定义函数获取汉字首字母的方法
Dec 01 PHP
php出租房数据管理及搜索页面
May 23 PHP
解决form中action属性后面?传递参数 获取不到的问题
Jul 21 PHP
php实现基于pdo的事务处理方法示例
Jul 21 PHP
解决php用mysql方式连接数据库出现Deprecated报错问题
Dec 25 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中,文件上传
2006/12/06 PHP
mac下安装nginx和php
2013/11/04 PHP
php使用pdo连接sqlite3的配置示例
2016/05/27 PHP
Ajax实现对静态页面的文章访问统计功能示例
2016/10/10 PHP
再次更新!MSClass (Class Of Marquee Scroll通用不间断滚动JS封装类 Ver 1.6)
2007/02/05 Javascript
JavaScript CSS修改学习第五章 给“上传”添加样式
2010/02/19 Javascript
javascript function调用时的参数检测常用办法
2010/02/26 Javascript
js获取iframe中的window对象的实现方法
2016/05/20 Javascript
js动态添加的DIV中的onclick事件简单实例
2016/07/25 Javascript
jQuery树形插件jquery.simpleTree.js用法分析
2016/09/05 Javascript
JS判断是否手机或pad访问实现方法
2016/12/09 Javascript
jquery插件bootstrapValidator表单验证详解
2016/12/15 Javascript
JavaScript制作简易计算器(不用eval)
2017/02/05 Javascript
Angular.JS实现无限级的联动菜单(使用demo)
2017/02/08 Javascript
JavaScript中object和Object的区别(详解)
2017/02/27 Javascript
vue v-model表单控件绑定详解
2017/05/17 Javascript
微信小程序利用co处理异步流程的方法教程
2017/05/20 Javascript
详解webpack 入门与解析
2018/04/09 Javascript
Vue.js组件实现选项卡以及切换特效
2019/07/24 Javascript
vue 全局环境切换问题
2019/10/27 Javascript
Python实现处理管道的方法
2015/06/04 Python
Python使用re模块正则提取字符串中括号内的内容示例
2018/06/01 Python
python 字典中取值的两种方法小结
2018/08/02 Python
python实现屏保计时器的示例代码
2018/08/08 Python
python3爬虫获取html内容及各属性值的方法
2018/12/17 Python
对python生成业务报表的实例详解
2019/02/03 Python
Python使用正则表达式分割字符串的实现方法
2019/07/16 Python
TensorFlow命名空间和TensorBoard图节点实例
2020/01/23 Python
python接口自动化之ConfigParser配置文件的使用详解
2020/08/03 Python
详解python模块pychartdir安装及导入问题
2020/10/22 Python
Anaconda的安装与虚拟环境建立
2020/11/18 Python
荷兰音乐会和音乐剧门票订购网站:Topticketshop
2019/08/27 全球购物
党组织公开承诺书
2014/03/29 职场文书
食品安全承诺书
2014/05/22 职场文书
小爸爸观后感
2015/06/15 职场文书
pytorch 两个GPU同时训练的解决方案
2021/06/01 Python