WebSocket服务端开发(五)-WebSocketServer类帧解析整体流程介绍

本文介绍WebSocketServer类的parseFrame函数流程。

parseFrame函数:

function parseFrame($data, $socketId) {
  //判断该帧是否是经过掩码处理
  $isMasked = $this->isMasked($data[1]);

  //如果未经掩码处理,则根据协议规定,需要断开连接
  if (!$isMasked) {
    //此处使用1002状态码,表示协议错误,发送关闭帧
    $this->closeFrame($socketId, 1002, 'There is no mask!');

    //断开连接
    $this->disconnect($socketId);
    return false;
  }
  //获取负载的长度字节数
  $payloadLen = $this->getPayloadLen(substr($data, 1, 9));

  //根据负载长度获取负载的全部数据
  $payload = $this->getPayload($data, $payloadLen);

  //获取掩码值
  $mask = $this->getMask($data, $payloadLen);

  //获取帧的类型
  $frameType = $this->getFrameType($data[0]);

  //处理帧
  switch ($frameType) {
  case self::FRAME_CONTINUE:
    //后续帧,需要拼接buffer
    $this->socketListMap[$socketId]['buffer'] .= $this->parseRawFrame($payload, $mask);
    break;
  case self::FRAME_TEXT:
    //文本帧,处理方式默认保持一致,均使用parseRawFrame处理,如果由特殊需求可以重写parseTextFrame函数
    $this->socketListMap[$socketId]['buffer'] = $this->parseTextFrame($payload, $mask);
    break;
  case self::FRAME_BIN:
    //二进制帧,处理方式默认保持一致,均使用parseRawFrame处理,如果由特殊需求可以重写parseBinaryFrame函数
    $this->socketListMap[$socketId]['buffer'] = $this->parseBinaryFrame($payload, $mask);
    break;
  case self::FRAME_CLOSE:
    //发送关闭帧(应答帧)
    $this->closeFrame($socketId);
    break;
  case self::FRAME_PING:
    //发送pong帧响应,浏览器目前不提供ping、pong帧的API,此处逻辑基本不会走到,只为实现协议内容
    $this->sendPong($socketId, $this->parseRawFrame($payload, $mask));
    break;
  case self::FRAME_PONG:
    //收到pong帧不进行任何处理(正常情况下不会收到,浏览器不会主动发送pong帧)
    break;
  default:
    //其他帧类型无法处理,直接断开连接,根据协议,此处使用1003状态码关闭连接更好
    $this->disconnect($socketId);
    break;
  }
  if ($this->debug) {
    //输出调试信息
    echo "isFin:" . ((ord($data[0]) & 0x80) >> 7) . "\n";
    echo "opCode:$frameType\n";
    echo "payLoad Length:$payloadLen\n";
    echo "Mask:$mask\n\n";
  }

  //如果是结束的数据帧,返回true,否则均为false
  //当返回true时,外层调用函数会继续将执行核心业务逻辑,读取缓冲区中的数据进行处理
  //如果是false,则不进行进一步的处理(控制帧及非结束帧都不会提交到业务层处理)
  return $this->isFin($data[0]) && !$this->isControlFrame($frameType);
}