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
Discuz!5的PHP代码高亮显示插件(黑暗中的舞者更新)
Jan 29 PHP
Wordpress php 分页代码
Oct 21 PHP
php读取javascript设置的cookies的代码
Apr 12 PHP
PHP学习 变量使用总结
Mar 24 PHP
php中实现简单的ACL 完结篇
Sep 07 PHP
php cli 小技巧
Jun 03 PHP
php获取文件内容最后一行示例
Jan 09 PHP
php发送post请求函数分享
Mar 06 PHP
PHP使用imagick读取PDF生成png缩略图的两种方法
Mar 20 PHP
PHP单例模式与工厂模式详解
Aug 29 PHP
tp5(thinkPHP5框架)captcha验证码配置及验证操作示例
May 28 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
建立动态的WML站点(二)
2006/10/09 PHP
使用php重新实现PHP脚本引擎内置函数
2007/03/06 PHP
php 注释规范
2012/03/29 PHP
简单的php购物车代码
2020/06/05 PHP
Extjs学习笔记之五 一个小细节renderTo和applyTo的区别
2010/01/07 Javascript
javascript AOP 实现ajax回调函数使用比较方便
2010/11/20 Javascript
jQuery EasyUI API 中文文档 - ComboBox组合框
2011/10/07 Javascript
Extjs 继承Ext.data.Store不起作用原因分析及解决
2013/04/15 Javascript
JavaScript bold方法入门实例(把指定文字显示为粗体)
2014/10/17 Javascript
详细介绍jQuery.outerWidth() 函数具体用法
2015/07/20 Javascript
4种JavaScript实现简单tab选项卡切换的方法
2016/01/06 Javascript
BootStrap selectpicker
2016/06/20 Javascript
js动态获取子复选项并设计全选及提交的实现方法
2016/06/24 Javascript
JS实现的透明度渐变动画效果示例
2018/04/28 Javascript
node.js使用免费的阿里云ip查询获取ip所在地【推荐】
2018/09/03 Javascript
jQuery超简单遮罩层实现方法示例
2018/09/06 jQuery
jQuery动态操作表单示例【基于table表格】
2018/12/06 jQuery
详解滑动穿透(锁body)终极探索
2019/04/16 Javascript
vant实现购物车功能
2020/06/29 Javascript
vue 实现一个简单的全局调用弹窗案例
2020/09/10 Javascript
[03:04]2018年国际邀请赛典藏宝瓶&莱恩声望物品展示 片尾有彩蛋
2018/06/04 DOTA
Python决策树和随机森林算法实例详解
2018/01/30 Python
Python使用装饰器进行django开发实例代码
2018/02/06 Python
TensorFlow实现Softmax回归模型
2018/03/09 Python
Python基于Floyd算法求解最短路径距离问题实例详解
2018/05/16 Python
Python实现字典排序、按照list中字典的某个key排序的方法示例
2018/12/18 Python
python实现自动解数独小程序
2019/01/21 Python
解决Python中定时任务线程无法自动退出的问题
2019/02/18 Python
python使用多线程查询数据库的实现示例
2020/08/17 Python
Superdry瑞典官网:英国日本街头风品牌
2017/05/17 全球购物
手工制作的意大利礼服鞋:Ace Marks
2018/12/15 全球购物
用C#语言写出在本地创建一个UDP接收端口的具体过程
2016/02/22 面试题
北大青鸟学生求职信
2013/09/24 职场文书
新郎父亲婚宴答谢词
2014/01/11 职场文书
2015年端午节活动方案
2015/05/05 职场文书
2016年全国爱眼日宣传教育活动总结
2016/04/05 职场文书