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 在线翻译函数代码
May 07 PHP
php GeoIP的使用教程
Mar 09 PHP
PHP Directory 函数的详解
Mar 07 PHP
Codeigniter实现处理用户登录验证后的URL跳转
Jun 12 PHP
php中socket通信机制实例详解
Jan 03 PHP
使用 PHPStorm 开发 Laravel
Mar 24 PHP
nginx+thinkphp下解决不支持pathinfo模式
Jul 01 PHP
使用PHP实现微信摇一摇周边红包
Jan 04 PHP
PHP使用Pear发送邮件(Windows环境)
Jan 05 PHP
PHP实现添加购物车功能
Mar 06 PHP
ThinkPHP5.0框架使用build 自动生成模块操作示例
Apr 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
PHP设计模式之责任链模式的深入解析
2013/06/13 PHP
ThinkPHP调用百度翻译类实现在线翻译
2014/06/26 PHP
PHP抽象类基本用法示例
2018/12/28 PHP
jQuery 使用个人心得
2009/02/26 Javascript
javascript 树控件 比较好用
2009/06/11 Javascript
JS判断页面加载状态以及添加遮罩和缓冲动画的代码
2012/10/11 Javascript
ie8模式下click无反应点击option无反应的解决方法
2014/10/11 Javascript
js实现同一个页面多个渐变效果的方法
2015/04/10 Javascript
Hallo.js基于jQuery UI所见即所得的Web编辑器
2016/01/26 Javascript
BootStrap智能表单实战系列(九)表单图片上传的支持
2016/06/13 Javascript
轻松掌握JavaScript代理模式
2016/08/26 Javascript
Node.js发送HTTP客户端请求并显示响应结果的方法示例
2017/04/12 Javascript
jquery插件开发之选项卡制作详解
2017/08/30 jQuery
vue+webpack实现异步加载三种用法示例详解
2018/04/24 Javascript
详解vuex之store拆分即多模块状态管理(modules)篇
2018/11/13 Javascript
[49:17]DOTA2-DPC中国联赛 正赛 Phoenix vs Dynasty BO3 第三场 1月26日
2021/03/11 DOTA
利用Python实现网络测试的脚本分享
2017/05/26 Python
python3+PyQt5图形项的自定义和交互 python3实现page Designer应用程序
2020/07/20 Python
Django组件之cookie与session的使用方法
2019/01/10 Python
基于PyQt4和PySide实现输入对话框效果
2019/02/27 Python
基于腾讯云服务器部署微信小程序后台服务(Python+Django)
2019/05/08 Python
Python正则表达式匹配日期与时间的方法
2019/07/07 Python
Django为窗体加上防机器人的验证码功能过程解析
2019/08/14 Python
python实现静态web服务器
2019/09/03 Python
tornado+celery的简单使用详解
2019/12/21 Python
使用Python构造hive insert语句说明
2020/06/06 Python
基于Python实现下载网易音乐代码实例
2020/08/10 Python
使用canvas压缩图片上传的方法示例
2020/02/07 HTML / CSS
MONNIER Frères英国官网:源自巴黎女士奢侈品配饰电商平台
2018/12/06 全球购物
Java面试题及答案
2012/09/08 面试题
开工典礼策划方案
2014/05/23 职场文书
学生检讨书范文
2014/10/30 职场文书
一年级语文上册复习计划
2015/01/17 职场文书
2016年学校“3.12”植树节活动总结
2016/03/16 职场文书
一定要知道的 25 个 Vue 技巧
2021/11/02 Vue.js
win10+RTX3050ti+TensorFlow+cudn+cudnn配置深度学习环境的方法
2022/06/25 Servers