Selaa lähdekoodia

强化心跳机制

liangl 11 vuotta sitten
vanhempi
commit
b95e929beb

+ 55 - 2
applications/Demo/Bootstrap/Gateway.php

@@ -71,6 +71,12 @@ class Gateway extends Man\Core\SocketWorker
     protected $workerConnections = array();
     
     /**
+     * 客户端心跳信息
+     * [client_id=>not_response_count, client_id=>not_response_count, ..]
+     */
+    protected $pingInfo = array();
+    
+    /**
      * gateway 发送心跳时间间隔 单位:秒 ,0表示不发送心跳,在配置中设置
      * @var integer
      */
@@ -79,12 +85,17 @@ class Gateway extends Man\Core\SocketWorker
     /**
      * 心跳数据
      * 可以是二进制数据(二进制数据保存在文件中,在配置中设置ping数据文件路径 如 ping_data=/yourpath/ping.bin)
-     * ping数据应该是客户端能够识别的数据格式,只是检测连接的连通性,客户端收到心跳数据可以选择忽略此数据包
+     * ping数据应该是客户端能够识别的数据格式,客户端必须回复心跳,不然链接会断开
      * @var string
      */
     protected $pingData = '';
     
     /**
+     * 客户端连续$pingNotResponseLimit次不回应心跳则断开链接
+     */
+    protected $pingNotResponseLimit = 1;
+    
+    /**
      * 命令字,统计用到
      * @var array
      */
@@ -103,6 +114,12 @@ class Gateway extends Man\Core\SocketWorker
      */
     public function dealInput($recv_buffer)
     {
+        // 只要客户端有回应就是没掉线
+        if(!empty($this->pingInfo[$this->connClientMap[$this->currentDealFd]]))
+        {
+            $this->pingInfo[$this->connClientMap[$this->currentDealFd]] = 0;
+        }
+        // 处理粘包
         return call_user_func_array(array('Event', 'onGatewayMessage'), array($recv_buffer));
     }
     
@@ -193,6 +210,13 @@ class Gateway extends Man\Core\SocketWorker
             $this->pingData = $ping_data_or_path;
         }
         
+        // 不返回心跳回应的限定值
+        $ping_not_response_limit = (int)\Man\Core\Lib\Config::get($this->workerName.'.ping_not_response_limit');
+        if($ping_not_response_limit > 0)
+        {
+            $this->pingNotResponseLimit = $ping_not_response_limit;
+        }
+        
         // 设置定时任务,发送心跳
         if($this->pingInterval > 0 && $this->pingData)
         {
@@ -208,6 +232,7 @@ class Gateway extends Man\Core\SocketWorker
         exit(0);
     }
     
+    
     /**
      * 接受一个链接
      * @param resource $socket
@@ -684,9 +709,37 @@ class Gateway extends Man\Core\SocketWorker
     
     /**
      * 向客户端发送心跳数据
+     * 并把没回应心跳的客户端踢掉
      */
     public function ping()
     {
-        $this->broadCast($this->pingData);
+        // 清理下线的链接
+        foreach($this->pingInfo as $client_id=>$not_response_count)
+        {
+            // 已经下线的忽略
+            if(!isset($this->clientConnMap[$client_id]))
+            {
+                unset($this->pingInfo[$client_id]);
+                continue;
+            }
+            // 上次发送的心跳还没有回复次数大于限定值就断开
+            if($not_response_count >= $this->pingNotResponseLimit)
+            {
+                $this->closeClient($this->clientConnMap[$client_id]);
+            }
+        }
+        // 向所有链接发送心跳数据
+        foreach($this->clientConnMap as $client_id=>$conn)
+        {
+            $this->sendToSocketId($conn, $this->pingData);
+            if(isset($this->pingInfo[$client_id]))
+            {
+                $this->pingInfo[$client_id]++;
+            }
+            else
+            {
+                $this->pingInfo[$client_id] = 1;
+            }
+        }
     }
 }

+ 1 - 1
applications/Demo/Event.php

@@ -50,7 +50,7 @@ class Event
         if(empty($_SESSION['name']))
         {
             $_SESSION['name'] = TextProtocol::decode($message);
-            Gateway::sendToCurrentClient("chart room login success, your client_id is $client_id, name is {$_SESSION['name']}\nuse client_id:words send message to one user\nuse words send message to all\n");
+            Gateway::sendToCurrentClient("chat room login success, your client_id is $client_id, name is {$_SESSION['name']}\nuse client_id:words send message to one user\nuse words send message to all\n");
              
             // 广播所有用户,xxx come
             return GateWay::sendToAll(TextProtocol::encode("{$_SESSION['name']}[$client_id] come"));

+ 8 - 3
workerman/conf/conf.d/Gateway.conf

@@ -39,8 +39,13 @@ lan_ip = 127.0.0.1
 ;内部通讯端口起始值,假如开启5个gateway进程,则每个进程会监听一个端口,40001 40002 40003 40004 40005
 lan_port_start = 40000
 
-;此gateway进程向客户端发送心跳时间间隔 单位:秒
+;此gateway进程向客户端发送心跳时间间隔 单位:秒,如果是0表示不发送心跳
 ping_interval = 0
 
-;发送的心跳数据,可以是字符串或者二进制数据,二进制数据需要配置成 文件路径 如ping_data=/yourpath/ping.bin
-ping_data = ../applications/ChatDemo/Bootstrap/ping.data 
+;客户端连续ping_not_response_limit次ping_interval时间内不回应心跳则断开链接
+ping_not_response_limit = 1
+
+;要发送的心跳请求数据,将心跳请求保存成文件,然后配置文件路径 如ping_data=/yourpath/ping.bin,
+;workerman会将此文件中的内容当作心跳请求发送给客户端
+;注意 心跳请求数据一定要符合你的通讯协议
+ping_data = ../applications/YourApp/ping.data