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 5昨天隆重推出--PHP 5/Zend Engine 2.0新特性
Oct 09 PHP
php 小乘法表实现代码
Jul 16 PHP
php面向对象全攻略 (十四) php5接口技术
Sep 30 PHP
最新用php获取谷歌PR值算法,附上php查询PR值代码示例
Dec 25 PHP
PHP中file_exists与is_file,is_dir的区别介绍
Sep 12 PHP
封装ThinkPHP的一个文件上传方法实例
Oct 31 PHP
Laravel SQL语句记录方式(推荐)
May 26 PHP
关于php支持的协议与封装协议总结(推荐)
Nov 17 PHP
PHP微信H5支付开发实例
Jul 25 PHP
PHP实现用session来实现记录用户登陆信息
Oct 15 PHP
php中访问修饰符的知识点总结
Jan 27 PHP
php获取目录下所有文件及目录(多种方法)(推荐)
May 14 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 select,radio和checkbox默认选择的实现方法
2010/05/15 PHP
PHP date()格式MySQL中插入datetime方法
2019/01/29 PHP
简短几句 通俗解释javascript的闭包
2011/01/17 Javascript
JS 实现导航栏悬停效果(续2)
2013/09/24 Javascript
浅析hasOwnProperty方法的应用
2013/11/20 Javascript
js获取input长度并根据页面宽度设置其大小及居中对齐
2014/08/22 Javascript
简述JavaScript对传统文档对象模型的支持
2015/06/16 Javascript
bootstrap中添加额外的图标实例代码
2017/02/15 Javascript
Javascript 详解封装from表单数据为json串进行ajax提交
2017/03/29 Javascript
JavaScript实现实时更新系统时间的实例代码
2017/04/04 Javascript
angularjs $http实现form表单提交示例
2017/06/09 Javascript
浅谈pc端rem字体设置的问题
2017/08/03 Javascript
微信小程序跳转到其他网页(外部链接)的实现方法
2019/09/20 Javascript
JS实现的定时器展示简单秒表、页面弹框及跳转操作完整示例
2020/01/26 Javascript
[01:16:12]完美世界DOTA2联赛PWL S2 FTD vs Inki 第一场 11.21
2020/11/23 DOTA
Python+Selenium自动化实现分页(pagination)处理
2017/03/31 Python
Python实现文件信息进行合并实例代码
2018/01/17 Python
对Python中range()函数和list的比较
2018/04/19 Python
python使用turtle库与random库绘制雪花
2018/06/22 Python
python 类之间的参数传递方式
2019/12/20 Python
Pycharm小白级简单使用教程
2020/01/08 Python
python加密解密库cryptography使用openSSL生成的密匙加密解密
2020/02/11 Python
python 画条形图(柱状图)实例
2020/04/24 Python
原生 JS+CSS+HTML 实现时序图的方法
2019/07/31 HTML / CSS
工程招投标邀请书
2014/01/30 职场文书
会计试用期自我评价怎么写
2014/09/18 职场文书
司机工作自我鉴定
2014/09/19 职场文书
教师党员自我剖析材料
2014/09/29 职场文书
学生上课说话检讨书
2014/10/25 职场文书
2015年考研复习计划
2015/01/19 职场文书
长城导游词400字
2015/01/30 职场文书
党员进社区活动总结
2015/05/07 职场文书
新闻简讯格式及范文
2015/07/22 职场文书
WordPress多语言翻译插件 - WPML使用教程
2021/04/01 PHP
JS ES6异步解决方案
2021/04/29 Javascript
Redis+Lua脚本实现计数器接口防刷功能(升级版)
2022/02/12 Redis