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
isset和empty的区别
Jan 15 PHP
php 截取字符串并以零补齐str_pad() 函数
May 07 PHP
Smarty的配置与高级缓存技术分享
Jun 05 PHP
解析MySql与Java的时间类型
Jun 22 PHP
PHP中模拟处理HTTP PUT请求的例子
Jul 22 PHP
PHP的Socket通信之UDP通信实例
Jul 02 PHP
php将远程图片保存到本地服务器的实现代码
Aug 03 PHP
php实现将HTML页面转换成word并且保存的方法
Oct 14 PHP
PHP实现上传图片到 zimg 服务器
Oct 19 PHP
php实现支持中文的文件下载功能示例
Aug 30 PHP
Yii框架中用response保存cookie,用request读取cookie的原理解析
Sep 04 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常用Output和ptions/Info函数集介绍
2013/06/19 PHP
PHP5中实现多态的两种方法实例分享
2014/04/21 PHP
一个JS翻页效果
2007/07/23 Javascript
web css实现整站样式互相切换
2013/10/29 Javascript
引用其它js时如何同时处理多个window.onload事件
2014/09/02 Javascript
jQuery固定元素插件scrolltofixed使用指南
2015/04/21 Javascript
JavaScript中Function()函数的使用教程
2015/06/04 Javascript
jquery性能优化高级技巧
2015/08/24 Javascript
Bootstrap基本插件学习笔记之模态对话框(16)
2016/12/08 Javascript
Bootstrap中datetimepicker使用小结
2016/12/28 Javascript
性能优化之代码优化页面加载速度
2017/03/01 Javascript
js实现音频控制进度条功能
2017/04/01 Javascript
ES5学习教程之Array对象
2017/04/01 Javascript
详解在AngularJS的controller外部直接获取$scope
2017/06/02 Javascript
Angular实现类似博客评论的递归显示及获取回复评论的数据
2017/11/06 Javascript
bootstrap响应式工具使用详解
2017/11/29 Javascript
微信小程序通过一个json实现分享朋友圈图片
2019/09/03 Javascript
JavaScript实现背景自动切换小案例
2019/09/27 Javascript
Vue中错误图片的处理的实现代码
2019/11/07 Javascript
使用vue实现一个电子签名组件的示例代码
2020/01/06 Javascript
redux处理异步action解决方案
2020/03/22 Javascript
es6函数name属性功能与用法实例分析
2020/04/18 Javascript
jQuery 淡入/淡出效果函数用法分析
2020/05/19 jQuery
vue打包静态资源后显示空白及static文件路径报错的解决
2020/09/02 Javascript
python中nan与inf转为特定数字方法示例
2017/05/11 Python
Django + Uwsgi + Nginx 实现生产环境部署的方法
2018/06/20 Python
对Python 3.5拼接列表的新语法详解
2018/11/08 Python
详解Python中is和==的区别
2019/03/21 Python
pytorch  网络参数 weight bias 初始化详解
2020/06/24 Python
英语自荐信范文
2013/12/11 职场文书
遗产继承公证书
2014/04/09 职场文书
人力资源管理专业自荐信
2014/06/24 职场文书
寒假社会实践个人总结
2015/03/06 职场文书
2016年庆祝六一儿童节活动总结
2016/04/06 职场文书
2021好看的国漫排行榜前十名 《完美世界》上榜,《元龙》排名第一
2022/03/18 国漫
彻底卸载VMware虚拟机的超详细步骤记录
2022/07/15 Servers