2016-07-24 115 views
2

我正在设计棘轮websockets中的聊天室,以尽可能快地响应。 它知道用户什么时候离开页面,以及类似的东西。 但是,如果一个用户/客户端失去了与服务器的连接,问题是客户端不能让服务器知道它已经断开连接,因为它已经断开连接并且不能向服务器发送消息。那么,如何跟踪聊天客户端何时失去互联网连接并且不再上网?棘轮Websockets - 检测断开连接的客户端

两个可能的解决方案,我能想到的:

  • 服务器轮询客户端每15分钟到半小时检查,看看谁在线上。没有回应的客户会断开连接。这可能不会中断一切在PHP中发生?如果是的话如何?我在哪里放置代码?我从LoopInterface看到addPeriodicTimer()的一些信息,但我不确定这是否能完成这项工作,或者函数适合我的代码。 它也叫它sleep()函数,因为那样做不好。我还是想其他任务在后台发生的事情,而这个功能是一个计时器(如果可能的话在PHP)

  • onClose()方法在PHP 这可检测出用户在任何环境真的断开连接?如果是这样,当这个事件发生时,我怎么知道哪个用户被断开了?它只传递一个ConnectionInterface并且没有消息。

对不起,即时通讯仍然是新的棘轮库,仍然试图找出如何实现这一任务。

我的server.php代码:为app.js

// JavaScript Document 

var chat = document.getElementById("chatwindow"); 
var msg = document.getElementById("messagebox"); 
var refInterval = 0; 
var timeup = false; 
var awaytimer; 
var socket = new WebSocket("ws://52.39.48.172:8080"); 
var openn = false; 

function addMessage(msg){ 
     "use strict"; 
     chat.innerHTML += "<p>" + msg + "</p>"; 


} 

msg.addEventListener('keypress', function(evt){ 
    "use strict"; 
    if(evt.charCode != 13) 
     return; 

    evt.preventDefault(); 

    if(msg.value == "" || !openn) 
     return; 

    socket.send(JSON.stringify({ 
      msg: msg.value, 
      uname: nme, 
      uid: id, 
      tag: "[msgsend]" 


    })); 


    msg.value = ""; 



}); 

socket.onopen = function(evt) { 
    openn = true; 


    socket.send(JSON.stringify({ 
     uname: nme, 
     uid: id, 
     tag: "[connected]" 



    })); 




}; 

socket.onmessage = function(evt) { 
    var data = JSON.parse(evt.data); 

    if(data.tag == "[connected]") 
    { 
     addMessage(data.uname + " has connected..."); 

    } 
    else if(data.tag == "[bye]") 
    { 
     addMessage(data.uname + " has left the room..."); 

     if(data.uname == nme) 
      socket.close(); 
    } 
    else if(data.tag == "[msgsend]") 
    { 
     addMessage(data.uname + ": " + data.msg); 
    } 
}; 



    window.onfocus = refPage; 
    function refPage() 
    { 

     if(timeup == true) 
     { 


     if(refInterval == 1) 
     { 
      refInterval = 0; 
      location.reload(); 
     } 

     } 
     else 
     { 
      clearTimeout(awaytimer); 
     } 

     timeup = false; 

    } 

    window.onblur = timelyExit; 
    function timelyExit() 
    { 
     refInterval = 1; 

     // change this to trigger some kind of inactivity timer 
     awaytimer = setTimeout(function(){socket.send(JSON.stringify({ 
     uname: nme, 
     uid: id, 
     tag: "[bye]"      
     })); timeup=true; }, 900000); 


    } 


    window.onoffline = window.onunload = window.onbeforeunload = confirmExit; 
    function confirmExit() 
    { 
    socket.send(JSON.stringify({ 
     uname: nme, 
     uid: id, 
     tag: "[bye]" 

    })); 

    socket.close(); 
    } 


socket.onclose = function() { 

    openn = false; 


      //cant send server this message because already closed. 
    /* 
    socket.send(JSON.stringify({ 
     uname: nme, 
     uid: id, 
     tag: "[bye]" 

    })); 

    */ 

    socket.close(); 


}; 

代码chat.php

<?php 


require($_SERVER['DOCUMENT_ROOT'].'/var/www/html/vendor/autoload.php'); 

use Ratchet\Server\IoServer; 
use Ratchet\Http\HttpServer; 
use Ratchet\WebSocket\WsServer; 

$server = IoServer::factory(new HttpServer(new WsServer(new Chat)), 8080); 




$server->run(); 
?> 

代码

<?php 

error_reporting(E_ALL^E_NOTICE); 
session_id($_GET['sessid']); 
    if(!session_id) 
     session_start(); 


$userid = $_SESSION["userid"]; 
$username = $_SESSION["username"]; 
$isadmin = $_SESSION["isadmin"]; 


use Ratchet\MessageComponentInterface; 
use Ratchet\ConnectionInterface; 

class Chat implements MessageComponentInterface 
{ 
    protected $clients; 

    public function __construct() 
    { 
     $this->clients = new \SplObjectStorage; 




    } 

    public function onOpen(ConnectionInterface $conn) 
    { 
     $this->clients->attach($conn); 

    } 

    public function onClose(ConnectionInterface $conn) 
    { 

     $this->clients->detach($conn); 
    } 

    public function onMessage(ConnectionInterface $conn, $msg) 
    { 
     $msgjson = json_decode($msg); 
     $tag = $msgjson->tag; 

     if($tag == "[msgsend]") 
     { 

      foreach($this->clients as $client) 
      { 
        $client->send($msg);  
      } 
     } 
     else if($tag == "[bye]") 
     { 

      foreach($this->clients as $client) 
      { 
        $client->send($msg);  
      } 

      onClose($conn); 
     } 
     else if($tag == "[connected]") 
     { 
      //store client information 


      //send out messages 
       foreach($this->clients as $client) 
      { 
        $client->send($msg);  
      } 



     } 







    } 

    public function onError(ConnectionInterface $conn, Exception $e) 
    { 
     echo "Error: " . $e->getMessage(); 
     $conn -> close(); 
    } 

} 

?> 

编辑:刚刚测试并确认的OnClose( )方法在互联网连接终止时不会触发。

有没有办法可以解决第一个问题?

回答

-3

免责声明:我是干练的创始人之一,只是更好的实时

这是一份实时的平台为你解决问题的确切类型。首先,您可以使用presence来检测用户是否在场。使用Ably,如果用户突然断开连接,您将在15秒内收到通知,如果明确断开连接(由客户端),您将立即得到通知。

另外,Ably解决了一些其他问题,您需要考虑如何处理message continuity when clients become disconnected for short periods

我很欣赏我的答案并不是专门回答你如何用Ratchet做到这一点的问题,但是实时平台的构建是为了在规模和可靠性上解决这些问题,所以我建议你考虑如果可以的话。

+1

听起来不错,但我目前不想为API支付费用,也不希望每天仅限于100个连接。 – emjay

+0

aww cmon。这里没有一个人使用Ratchet API来告诉我如何定期向所有客户端发送消息? – emjay

0

检测断开的客户端本最好的解决办法是基于事件的不会轮询客户都没有。这种方法将成为您的第二个解决方案,并且还可以很好地模拟WebSocket消息传递的异步特性。

,你说,在某些情况下,有些调皮的客户端可能无法通知其断开插座服务器并使其保持“挂”,可以这么说然而可能。在这种情况下,我会建议尝试socket服务器内实现轮询触发本身,而是通过单独的服务器端客户是通过cron或者其他任务调度触发并指示插座发起投票服务器发起请求以轮询所有连接的客户端。

有关构建服务器端客户端的更多信息,请参阅this question of mine,我也能找到一些解决方案。


为了确定发送断开消息,我会建议使用只是SplObjectStorageRatchet\MessageComponentInterface实现你,而是包装其他类内部的简单数组里面去了,所以这样的事情:

class MyClientList 
{ 
    protected $clients = []; 

    public function addClient(Connection $conn) 
    { 
     $this->clients[$conn->resourceId] = [ 
      'connection' => $conn, 
     ]; 

     return $this; 
    } 

    public function removeClient(Connection $conn) 
    { 
     if(isset($this->clients[$conn->resourceId])) { 
      unset($this->clients[$conn->resourceId]); 
     } 

     return $this; 
    } 

    public function registerClient(ConnectionInterface $conn, array $userData) 
    { 
     if(isset($this->clients[$conn->resourceId])) { 
      $this->clients[$conn->resourceId] = array_merge(
       $this->clients[$conn->resourceId], 
       $userData 
      ); 
     } 

     return $this; 
    } 

    public function getClientData(ConnectionInterface $conn) 
    { 
     return isset($this->clients[$conn->resourceId]) ? 
      $this->clients[$conn->resourceId] : 
      null 
     ; 
    } 
} 

在某些时候,用户首次连接后不久,您的客户端应该发送一个插座信息到服务器,并通知服务器现在就注册附加信息的客户端的身份(在你的情况你attemptin g以识别unameuid属性)。通过对连接ID进行索引,您应该能够使用此实现来在发送初始注册消息后对来自客户端的所有消息进行标识。

相关问题