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 21 PHP
php iconv() : Detected an illegal character in input string
Dec 05 PHP
php 带逗号千位符数字的处理方法
Jan 10 PHP
Php无限级栏目分类读取的实现代码
Feb 19 PHP
php自动加载autoload机制示例分享
Feb 20 PHP
PHP读取配置文件类实例(可读取ini,yaml,xml等)
Jul 28 PHP
PHP获取IP地址所在地信息的实例(使用纯真IP数据库qqwry.dat)
Nov 15 PHP
yii2实现 &quot;上一篇,下一篇&quot; 功能的代码实例
Feb 04 PHP
php防止sql注入的方法详解
Feb 20 PHP
使用phpQuery获取数组的实例
Mar 13 PHP
PHP的静态方法与普通方法用法实例分析
Sep 26 PHP
PHP基于ip2long实现IP转换整形
Dec 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上传图片成功的全部过程分享
2013/08/12 PHP
PHP实现求两个字符串最长公共子串的方法示例
2017/11/17 PHP
jquery的map与get方法详解
2013/11/04 Javascript
jquery教程ajax请求json数据示例
2014/01/13 Javascript
纯JavaScript实现获取onclick、onchange等事件的值
2014/12/29 Javascript
JavaScript中数据结构与算法(四):串(BF)
2015/06/19 Javascript
浅析四种常见的Javascript声明循环变量的书写方式
2015/10/14 Javascript
JavaScript接口的实现三种方式(推荐)
2016/06/14 Javascript
AngularJS双向绑定和依赖反转实例详解
2017/04/15 Javascript
Vue2.0 给Tab标签页和页面切换过渡添加样式的方法
2018/03/13 Javascript
jQuery length 和 size()区别总结
2018/04/26 jQuery
JS实现的全选、全不选及反选功能【案例】
2019/02/19 Javascript
小程序异步问题之多个网络请求依次执行并依次收集请求结果
2019/05/05 Javascript
Python键盘输入转换为列表的实例
2018/06/23 Python
python之django母板页面的使用
2018/07/03 Python
pandas 将索引值相加的方法
2018/11/15 Python
python3 cvs将数据读取为字典的方法
2018/12/22 Python
Python3.5文件读与写操作经典实例详解
2019/05/01 Python
Python分支语句与循环语句应用实例分析
2019/05/07 Python
python sorted函数的小练习及解答
2019/09/18 Python
适合Python初学者的一些编程技巧
2020/02/12 Python
pycharm工具连接mysql数据库失败问题
2020/04/01 Python
PyCharm设置Ipython交互环境和宏快捷键进行数据分析图文详解
2020/04/23 Python
Python中正则表达式对单个字符,多个字符和匹配边界等使用
2021/01/27 Python
基于HTML5的齿轮动画特效
2016/02/29 HTML / CSS
anello泰国官方网站:日本流行包包品牌
2019/08/08 全球购物
你懂得怎么写自荐信吗?
2013/12/27 职场文书
书香校园活动方案
2014/02/28 职场文书
考核评语大全
2014/04/29 职场文书
党的群众路线教育实践活动实施方案
2014/10/31 职场文书
因家庭原因离职的辞职信范文
2015/05/12 职场文书
三国演义读书笔记
2015/06/25 职场文书
电视新闻稿
2015/07/17 职场文书
人事部:年度述职报告范文
2019/07/12 职场文书
python神经网络学习 使用Keras进行回归运算
2022/05/04 Python
Linux服务器离线安装 nginx的详细步骤
2022/06/16 Servers