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 相关文章推荐
用Socket发送电子邮件(利用需要验证的SMTP服务器)
Oct 09 PHP
多文件上传的例子
Oct 09 PHP
PHP通用检测函数集合
Feb 08 PHP
PHP导入Excel到MySQL的方法
Apr 23 PHP
PHP循环函数使用介绍之PHP基础入门教程
Sep 21 PHP
php禁止浏览器使用缓存页面的方法
Nov 07 PHP
ecshop 2.72如何修改后台访问地址
Mar 03 PHP
PHP下使用mysqli的函数连接mysql出现warning: mysqli::real_connect(): (hy000/1040): ...
Feb 14 PHP
PHP flush 函数使用注意事项
Aug 26 PHP
PHP巧妙利用位运算实现网站权限管理的方法
Mar 12 PHP
PHP使用curl_multi_select解决curl_multi网页假死问题的方法
Aug 15 PHP
PHP常用的类封装小结【4个工具类】
Jun 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
Apache环境下PHP利用HTTP缓存协议原理解析及应用分析
2010/02/16 PHP
php判断文件夹是否存在不存在则创建
2015/04/09 PHP
表单提交错误后返回内容消失问题的解决方法(PHP网站)
2015/10/20 PHP
php实现的mysqldb读写分离操作类示例
2017/02/07 PHP
漂亮的widgets,支持换肤和后期开发新皮肤(2007-4-27已更新1.7alpha)
2007/04/27 Javascript
JavaScript入门教程(5) js Screen屏幕对象
2009/01/31 Javascript
csdn 博客中实现运行代码功能实现
2009/08/29 Javascript
JavaScript在多浏览器下for循环的使用方法
2012/11/07 Javascript
JS实现字体选色板实例代码
2013/11/20 Javascript
js 获取元素下面所有li的两种方法
2014/04/14 Javascript
使用jQuery中的when实现多个AJAX请求对应单个回调的例子分享
2014/04/23 Javascript
jquery实现标题字体变换的滑动门菜单效果
2015/09/07 Javascript
关于JavaScript作用域你想知道的一切
2016/02/04 Javascript
浅析jQuery 3.0中的Data
2016/06/14 Javascript
Bootstrap CSS组件之导航条(navbar)
2016/12/17 Javascript
JavaScript实现打地鼠小游戏
2020/04/23 Javascript
快速解决angularJS中用post方法时后台拿不到值的问题
2018/08/14 Javascript
Javascript读取上传文件内容/类型/字节数
2019/04/30 Javascript
Vue实现日历小插件
2019/06/26 Javascript
layui 数据表格 点击分页按钮 监听事件的实例
2019/09/02 Javascript
Nuxt.js实现一个SSR的前端博客的示例代码
2019/09/06 Javascript
JS造成内存泄漏的几种情况实例分析
2020/03/02 Javascript
关于vue属性使用和不使用冒号的区别说明
2020/10/22 Javascript
详解Python中DOM方法的动态性
2015/04/11 Python
在Python的Flask框架中构建Web表单的教程
2016/06/04 Python
儿童学习python的一些小技巧
2018/05/27 Python
Python3匿名函数用法示例
2018/07/25 Python
python按行读取文件并找出其中指定字符串
2019/08/08 Python
River Island美国官网:英国高街时尚品牌
2018/09/04 全球购物
为什么要使用servlet
2016/01/17 面试题
医校毕业生自我鉴定
2014/01/25 职场文书
医学生临床实习自我评价
2014/03/07 职场文书
医学生求职信
2014/07/01 职场文书
工作粗心大意检讨书
2014/09/18 职场文书
python 开心网和豆瓣日记爬取的小爬虫
2021/05/29 Python
MySQL三种方式实现递归查询
2022/04/18 MySQL