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实现框架(二)
Oct 09 PHP
目录,文件操作详谈―PHP
Nov 25 PHP
smarty 原来也不过如此~~呵呵
Nov 25 PHP
php站内搜索并高亮显示关键字的实现代码
Dec 29 PHP
深入解析PHP内存管理之谁动了我的内存
Jun 20 PHP
解析Win7 XAMPP apache无法启动的问题
Jun 26 PHP
php对二维数组进行排序的简单实例
Dec 19 PHP
thinkPHP5.0框架开发规范简介
Mar 25 PHP
php json相关函数用法示例
Mar 28 PHP
PHP基于简单递归函数求一个数阶乘的方法示例
Apr 26 PHP
Laravel框架分页实现方法分析
Jun 12 PHP
PHP xpath()函数讲解
Feb 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
纯真IP数据库的应用 IP地址转化成十进制
2009/06/14 PHP
PHP-redis中文文档介绍
2013/02/07 PHP
更改localhost为其他名字的方法
2014/02/10 PHP
ThinkPHP 表单自动验证运用示例
2014/10/13 PHP
tp5框架前台无限极导航菜单类实现方法分析
2020/03/29 PHP
javascript查找字符串中出现最多的字符和次数的小例子
2013/10/29 Javascript
JavaScript中的单引号和双引号报错的解决方法
2014/09/01 Javascript
理解和运用JavaScript的闭包机制
2015/08/13 Javascript
详解JavaScript表单验证(E-mail 验证)
2016/03/31 Javascript
基于MVC+EasyUI的web开发框架之使用云打印控件C-Lodop打印页面或套打报关运单信息
2016/08/29 Javascript
AngularJS操作键值对象类似java的hashmap(填坑小结)
2016/11/12 Javascript
Javascript 两种刷新方法以及区别和适用范围
2017/01/17 Javascript
JavaScript函数节流的两种写法
2017/04/07 Javascript
angular中不同的组件间传值与通信的方法
2017/11/04 Javascript
Angular4学习教程之DOM属性绑定详解
2018/01/04 Javascript
vue之父子组件间通信实例讲解(props、$ref、$emit)
2018/05/22 Javascript
详解vue文件中使用echarts.js的两种方式
2018/10/18 Javascript
详解jQuery中的prop()使用方法
2020/01/05 jQuery
[01:28:44]DOTA2-DPC中国联赛定级赛 RNG vs iG BO3第一场 1月10日
2021/03/11 DOTA
python 随机数生成的代码的详细分析
2011/05/15 Python
python解决字符串倒序输出的问题
2018/06/25 Python
python下载库的步骤方法
2019/10/12 Python
python全局变量引用与修改过程解析
2020/01/07 Python
Tensorflow 多线程与多进程数据加载实例
2020/02/05 Python
Django的CVB实例详解
2020/02/10 Python
matlab中二维插值函数interp2的使用详解
2020/04/22 Python
理解Django 中Call Stack机制的小Demo
2020/09/01 Python
宝拉珍选澳大利亚官方购物网站:Paula’s Choice澳大利亚
2016/09/13 全球购物
卡西欧B级产品官方网站:Casio Outlet
2018/05/22 全球购物
优质飞蝇钓和渔具:RiverBum
2020/05/10 全球购物
2014升学宴答谢词
2014/01/26 职场文书
2014年党的群众路线活动个人整改措施
2014/10/28 职场文书
教师见习报告范文
2014/11/03 职场文书
自愿离婚协议书范本
2015/01/26 职场文书
Python基础之常用库常用方法整理
2021/04/30 Python
阿里云服务器部署RabbitMQ集群的详细教程
2022/06/01 Servers