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 相关文章推荐
一个odbc连mssql分页的类
Oct 09 PHP
php的array数组和使用实例简明教程(容易理解)
Mar 20 PHP
PHP实例分享判断客户端是否使用代理服务器及其匿名级别
Jun 04 PHP
采用ThinkPHP中F方法实现快速缓存实例
Jun 13 PHP
在WordPress中使用PHP脚本来判断访客来自什么国家
Dec 10 PHP
php封装的数据库函数与用法示例【参考thinkPHP】
Nov 08 PHP
centos 7.2下搭建LNMP环境教程
Nov 20 PHP
php实现的简单中文验证码功能示例
Jan 03 PHP
PHP封装的PDO数据库操作类实例
Jun 21 PHP
php 截取中英文混合字符串的方法
May 31 PHP
ThinkPHP框架实现定时执行任务的两种方法分析
Sep 04 PHP
php中加密解密DES类的简单使用方法示例
Mar 26 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开发规范手册之PHP代码规范详解
2011/01/13 PHP
php设计模式之命令模式使用示例
2014/03/02 PHP
Linux中为php配置伪静态
2014/12/17 PHP
php求一个网段开始与结束IP地址的方法
2015/07/09 PHP
CentOS7.0下安装PHP5.6.30服务的教程详解
2018/09/29 PHP
JAVASCRIPT keycode总结
2009/02/04 Javascript
javascript写的一个链表实现代码
2009/10/25 Javascript
Javascript 面向对象 命名空间
2010/05/13 Javascript
Javascript中各种trim的实现详细解析
2013/12/10 Javascript
node.js中的fs.mkdirSync方法使用说明
2014/12/17 Javascript
jQuery中DOM树操作之使用反向插入方法实例分析
2015/01/23 Javascript
Jquery中CSS选择器用法分析
2015/02/10 Javascript
JavaScript判断变量是否为数组的方法(Array)
2016/02/24 Javascript
js获取当前日期时间及其它日期操作汇总
2016/03/08 Javascript
javascript实现简易计算器的代码
2016/05/31 Javascript
JS获取checkbox的个数简单实例
2016/08/19 Javascript
JavaScript中值类型和引用类型的区别
2017/02/23 Javascript
JavaScript实现星星等级评价功能
2017/03/22 Javascript
PostgreSQL Node.js实现函数计算方法示例
2019/02/12 Javascript
微信小程序实现多个按钮的颜色状态转换
2019/02/15 Javascript
JavaScript函数的4种调用方法实例分析
2019/03/05 Javascript
JavaScript Dom 绑定事件操作实例详解
2019/10/02 Javascript
vue-cli3使用mock数据的方法分析
2020/03/16 Javascript
Java编程迭代地删除文件夹及其下的所有文件实例
2018/02/10 Python
Python第三方Window模块文件的几种安装方法
2018/11/22 Python
代码实例讲解python3的编码问题
2019/07/08 Python
解决django同步数据库的时候app models表没有成功创建的问题
2019/08/09 Python
Django为窗体加上防机器人的验证码功能过程解析
2019/08/14 Python
Python使用matplotlib绘制三维参数曲线操作示例
2019/09/10 Python
Python 中的pygame安装与配置教程详解
2020/02/10 Python
为什么说python更适合树莓派编程
2020/07/20 Python
杰夫·班克斯男士服装网上商店:Jeff Banks
2019/10/24 全球购物
应届大学生的推荐信
2013/11/20 职场文书
护士长竞聘演讲稿
2014/04/30 职场文书
聘用合同范本
2015/09/21 职场文书
python编程实现清理微信重复缓存文件
2021/11/01 Python