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 xml实例 留言本
Mar 20 PHP
PHP应用JSON技巧讲解
Feb 03 PHP
PHP安全的URL字符串base64编码和解码
Jun 19 PHP
PHP使用逆波兰式计算工资的方法
Jul 29 PHP
PHP程序中使用adodb连接不同数据库的代码实例
Dec 19 PHP
PHP使用Mysqli类库实现完美分页效果的方法
Apr 07 PHP
Netbeans 8.2将支持PHP7 更精彩
Jun 13 PHP
PHP控制前台弹出对话框的实现方法
Aug 21 PHP
PHP控制反转(IOC)和依赖注入(DI)
Mar 13 PHP
利用php获得flv视频长度的实例代码
Oct 26 PHP
PHP实现用户异地登录提醒功能的方法【基于thinkPHP框架】
Mar 15 PHP
php实现微信分享朋友链接功能
Feb 18 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
require(),include(),require_once()和include_once()区别
2008/03/27 PHP
php 方便水印和缩略图的图形类
2009/05/21 PHP
php 引用(&amp;)详解
2009/11/20 PHP
PHP跳转页面的几种实现方法详解
2013/06/08 PHP
php中常见的sql攻击正则表达式汇总
2014/11/06 PHP
php如何控制用户对图片的访问 PHP禁止图片盗链
2016/03/25 PHP
学习YUI.Ext基础第一天
2007/03/10 Javascript
javascript常用方法、属性集合及NodeList 和 HTMLCollection 的浏览器差异
2010/12/25 Javascript
基于canvas实现的钟摆效果完整实例
2016/01/26 Javascript
Ajax与服务器(JSON)通信实例代码
2016/11/05 Javascript
JavaScript正则替换HTML标签功能示例
2017/03/02 Javascript
Vue 单文件中的数据传递示例
2017/03/21 Javascript
NodeJS测试框架mocha入门教程
2017/03/28 NodeJs
JS中正则表达式要注意lastIndex属性
2017/08/08 Javascript
JavaScript原型对象、构造函数和实例对象功能与用法详解
2018/08/04 Javascript
vue实现文件上传读取及下载功能
2020/11/17 Javascript
uniapp 仿微信的右边下拉选择弹出框的实现代码
2020/07/12 Javascript
[42:11]TNC vs Pain 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
python3.5 + PyQt5 +Eric6 实现的一个计算器代码
2017/03/11 Python
基于Python的关键字监控及告警
2017/07/06 Python
python+opencv实现的简单人脸识别代码示例
2017/11/14 Python
python如何保证输入键入数字的方法
2019/08/23 Python
HTML5、Select下拉框右边加图标的实现代码(增进用户体验)
2017/10/16 HTML / CSS
上班迟到检讨书
2014/01/10 职场文书
安全事故检讨书
2014/01/18 职场文书
顶撞老师检讨书
2014/02/07 职场文书
《争吵》教学反思
2014/02/15 职场文书
社区党务公开实施方案
2014/03/18 职场文书
学生保证书范文
2014/04/28 职场文书
网络技术专业求职信
2014/07/13 职场文书
干部作风建设年活动剖析材料
2014/10/23 职场文书
收入及婚姻状况证明
2014/11/20 职场文书
婚庆主持词大全
2015/06/30 职场文书
装修公司管理制度
2015/08/05 职场文书
生活小常识广播稿
2015/08/19 职场文书
Debian11 Xfce终端光标的颜色怎么设置?
2022/08/14 数码科技