• 企业400电话
  • 网络优化推广
  • AI电话机器人
  • 呼叫中心
  • 全 部 栏 目

    网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    PHP长连接实现与使用方法详解
    POST TIME:2021-10-18 05:10

    本文实例讲述了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 socket用法总结》、《php字符串(string)用法总结》、《PHP数学运算技巧总结》、《php面向对象程序设计入门教程》、《PHP数组(Array)操作技巧大全》、《PHP数据结构与算法教程》、《php程序设计算法总结》及《PHP网络编程技巧总结》

    希望本文所述对大家PHP程序设计有所帮助。

    您可能感兴趣的文章:
    • PHP使用Redis长连接的方法详解
    • PHP扩展模块memcached长连接使用方法分析
    • 基于php实现长连接的方法与注意事项的问题
    • PHP set_time_limit(0)长连接的实现分析
    • 基于HTTP长连接的"服务器推"技术的php 简易聊天室
    • PHP编程实现的TCP服务端和客户端功能示例
    • php实现TCP端口检测的方法
    • 详解PHP Swoole长连接常见问题
    上一篇:搜索附近的人PHP实现代码
    下一篇:PHP使用Redis长连接的方法详解
  • 相关文章
  • 

    关于我们 | 付款方式 | 荣誉资质 | 业务提交 | 代理合作


    © 2016-2020 巨人网络通讯

    时间:9:00-21:00 (节假日不休)

    地址:江苏信息产业基地11号楼四层

    《增值电信业务经营许可证》 苏B2-20120278

    X

    截屏,微信识别二维码

    微信号:veteran88

    (点击微信号复制,添加好友)

     打开微信