WebSocket服务端开发(九)-WebSocketServer类事件

本文介绍WebSocketServer类中的事件。

WebSocketServer类支持以下事件:
onstarted:
服务器启动后触发,socket_listen成功执行后,服务器进入启动状态

onconnected:
与客户端建立连接完成后触发,此时连接已放入socket连接池中

onUpgradePartReceive:
当收到部分WebSocket握手时触发,仅当收到的握手包不完整时会触发该事件

onHandShakeFailure:
在握手失败后触发

onHandShakeSuccess:
握手成功后触发

ondisconnected:
关闭客户端连接后触发,此时还未将连接从socket连接池中移除,当disconnect函数的第二个参数slient为true时不触发该事件

onAfterRemoveSocket:
在移除socket连接后触发

onafterhealthcheck:
在健康检查完成后触发,默认该事件会断开不健康的连接

onerror:
遇到socket错误时触发

onshutdown:
服务器关闭时触发

  function onstarted($serverSocket) {
    if ($this->debug) {
      printf('Server start at %s', date('Y-m-d H:i:s') . "\n");
    }
  }

  function onconnected($socket) {
    if ($this->debug) {
      printf('Socket connect at %s-%s', date('Y-m-d H:i:s'), $socket . "\n");
    }
  }

  function onUpgradePartReceive($socketId) {
    if ($this->debug) {
      $buffer = $this->socketListMap[$socketId]['buffer'];
      printf('Receive Upgrade Part at %s-%s%s(%d bytes)' . "\n", date('Y-m-d H:i:s'), $socketId . "\n", $buffer, strlen($buffer));
    }
  }

  function onHandShakeFailure($socketId) {
    if ($this->debug) {
      printf('HandShake Failed at %s-%s', date('Y-m-d H:i:s'), $socketId . "\n");
    }
  }

  function onHandShakeSuccess($socketId) {
    if ($this->debug) {
      printf('HandShake Success at %s-%s', date('Y-m-d H:i:s'), $socketId . "\n");
    }
  }

  function ondisconnected($socketId) {
    if ($this->debug) {
      printf('Socket disconnect at %s-%s', date('Y-m-d H:i:s'), $socketId . "\n");
    }
  }

  function onAfterRemoveSocket($socketId) {
    if ($this->debug) {
      printf('[onAfterRemoveSocket]remove:' . $socketId . ',left:' . implode('|', array_keys($this->socketListMap)) . "\n");
    }
  }

  function onafterhealthcheck($unhealthyList) {
    foreach ($unhealthyList as $socketId) {
      $this->disconnect($socketId);
    }
  }

  function onerror($errCode, $socketId) {
    switch ($errCode) {
    case 10053:
      $this->disconnect($socketId);
      break;
    default:
      if ($this->debug) {
        echo 'Socket Error:' . $errorCode . "\n";
      }
      break;
    }
  }

  function onshutdown() {
    if ($this->debug) {
      printf('Server shutdown!');
    }
  }

WebSocket服务端开发(八)-WebSocketServer类错误处理

本文介绍WebSocketServer类错误处理相关方法。

错误信息显示

  //获取最后一次socket的错误码
  function getLastErrCode($socketId = null) {
    if (is_null($socketId)) {
      $socket = $this->serverSocket;
    } else {
      $socket = $this->socketListMap[$socketId]['socket'];
    }
    return socket_last_error($socket);
  }

  //通过错误码查找错误详情
  function getLastErrMsg($socketId = null, $errCode = null) {
    if (!is_numeric($errCode)) {
      $errCode = $this->getLastErrCode($socketId);
    }
    return '[' . $errCode . ']' . socket_strerror($errCode) . "\n";
  }

当客户端直接断开连接时(没有发送关闭帧),Windows下会出现socket错误10053,此时服务器可以直接断开此连接。在Linux服务器下,测试发现如果客户端断开连接,并不会出现10053错误(可能是服务器环境的问题),这时如果再收到连接会发现,已经断开的连接会造成死循环:
socket_select选中了断开的连接,然后从socket中读数据,Windows下调用socket_recv会返回false,而Linux下不会报错,这样会导致该连接不会从连接池中移除,然后会在下一次循环连接池时继续被选中,不停的循环。
这里解决方法是,对于有10053错误的,直接断开连接,不会出现任何问题。在接收数据后,如果数据长度大于0,则置该连接的errorCnt为0,否则errorCnt加一。判断errorCnt是否大于3次,如果大于3次,则断开该socket连接。也就是说,如果连续收到3次空内容的数据,就会断开连接。最后使用健康检查防止某些情况下没有移除连接的问题。

注意:该类只处理10053的socket错误,其他类型没有实现,可以通过覆盖onerror自定义自己的错误处理方式。