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下读取文本文件的代码
Jul 02 PHP
用php将任何格式视频转为flv的代码
Sep 03 PHP
PHP 变量类型的强制转换
Oct 23 PHP
通过具体程序来理解PHP里面的抽象类
Jan 28 PHP
php更改目录及子目录下所有的文件后缀的代码
Sep 24 PHP
php中的注释、变量、数组、常量、函数应用介绍
Nov 16 PHP
php利用curl抓取新浪微博内容示例
Apr 27 PHP
destoon安全设置中需要设置可写权限的目录及文件
Jun 21 PHP
VB中的RasEnumConnections函数返回632错误解决方法
Jul 29 PHP
php基于curl实现的股票信息查询类实例
Nov 11 PHP
php获取POST数据的三种方法实例详解
Dec 20 PHP
yii2 在控制器中验证请求参数的使用方法
Jun 19 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使之能同时支持GIF和JPEG
2006/10/09 PHP
PHP 表单提交给自己
2008/07/24 PHP
PHP连接SQLServer2005的方法
2015/01/27 PHP
php自动加载方式集合
2016/04/04 PHP
php实现支持中文的文件下载功能示例
2017/08/30 PHP
Yii Framework框架使用PHPExcel组件的方法示例
2019/07/24 PHP
关于javascript DOM事件模型的两件事
2010/07/22 Javascript
js自动闭合html标签(自动补全html标记)
2012/10/04 Javascript
js获取url中指定参数值的示例代码
2013/12/14 Javascript
JS实现选中当前菜单后高亮显示的导航条效果
2015/10/15 Javascript
jQuery 如何给Carousel插件添加新的功能
2016/04/18 Javascript
微信小程序 购物车简单实例
2016/10/24 Javascript
JS实现数组去重复值的方法示例
2017/02/18 Javascript
node.js多个异步过程中判断执行是否完成的解决方案
2017/12/10 Javascript
vue-cli 目录结构详细讲解总结
2019/01/15 Javascript
JS栈stack类的实现与使用方法示例
2019/01/31 Javascript
Jquery $.map使用方法实例详解
2020/09/01 jQuery
windows下安装python paramiko模块的代码
2013/02/10 Python
Python的Bottle框架中实现最基本的get和post的方法的教程
2015/04/30 Python
Django admin实现图书管理系统菜鸟级教程完整实例
2017/12/12 Python
python使用flask与js进行前后台交互的例子
2019/07/19 Python
python利用itertools生成密码字典并多线程撞库破解rar密码
2019/08/12 Python
python脚本执行CMD命令并返回结果的例子
2019/08/14 Python
Python 日期时间datetime 加一天,减一天,加减一小时一分钟,加减一年
2020/04/16 Python
Pycharm无法打开双击没反应的问题及解决方案
2020/08/17 Python
pyqt5实现井字棋的示例代码
2020/12/07 Python
Madewell澳大利亚官方网站:美国休闲服饰品牌
2019/07/18 全球购物
Java里面如何创建一个内部类的实例
2015/01/19 面试题
管道维修工岗位职责
2013/12/27 职场文书
公司庆典邀请函范文
2014/01/13 职场文书
政府门卫岗位职责
2014/04/29 职场文书
宣传活动总结范文
2014/07/01 职场文书
大学生党员批评与自我批评
2014/09/28 职场文书
2014年政风行风自查自纠报告
2014/10/21 职场文书
从QQtabBar看css命名规范BEM的详细介绍
2021/08/07 HTML / CSS
关于Nginx中虚拟主机的一些冷门知识小结
2022/03/03 Servers