isMasked函数:
//判断是否是经过掩码处理的帧 function isMasked($byte) { return (ord($byte) & 0x80) > 0; }
getPayloadLen函数:
//获取负载的长度 //首字节如果小于126,则长度为首字节的值 //首字节如果等于126,则长度为后面紧跟的两个字节表示的字节数 //如果首字节是127,则长度为后面紧跟8字节表示的字节数 function getPayloadLen($data) { $first = ord($data[0]) & 0x7F; $second = (ord($data[1]) << 8) + ord($data[2]); $third = (ord($data[3]) << 40) + (ord($data[4]) << 32) + (ord($data[5]) << 24) + (ord($data[6]) << 16) + (ord($data[7]) << 8) + ord($data[8]); if ($first < 126) { return $first; } else if ($first === 126) { return $second; } else { return ($second << 48) + $third; } }
getPayLoad函数:
//获取负载的内容,根据帧的结构 //第0字节为结束标记及帧类型 //第1字节是否掩码标识位及负载长度的首字节 //根据协议要求,浏览器发送的数据必须经过掩码处理,所以偏移量至少是1字节的帧首字节+4字节的掩码长度 //根据帧长度的不同,表示长度的字节数可能为1、3或9 //所以根据不同的情况截取不同长度的数据即为负载内容 function getPayload($data, $len) { $offset = 5; if ($len < 126) { return substr($data, $offset + 1, $len); } else if ($len < 65536) { return substr($data, $offset + 3, $len); } else { return substr($data, $offset + 9, $len); } }
getMask函数:
//获取掩码的值 //仍然是根据不同的偏移位置截取 function getMask($data, $len) { $offset = 1; if ($len < 126) { return substr($data, $offset + 1, 4); } else if ($len < 65536) { return substr($data, $offset + 3, 4); } else { return substr($data, $offset + 9, 4); } }
getFrameType函数:
//获取帧类型 function getFrameType($byte) { return ord($byte) & 0x0F; }
isFin函数:
//判断是否为结束帧 function isFin($byte) { return (ord($byte[0]) & 0x80) > 0; }
isControlFrame函数:
//判断是否为控制帧,控制帧包含关闭帧、PING帧和PONG帧 function isControlFrame($frameType) { return $frameType === self::FRAME_CLOSE || $frameType === self::FRAME_PING || $frameType === self::FRAME_PONG; }
parseBinaryFrame、parseTextFrame、parseRawFrame函数:
//处理负载的掩码,将其还原 function parseRawFrame($payload, $mask) { $payloadLen = strlen($payload); $dest = ''; $maskArr = array(); for ($i = 0; $i < 4; $i++) { $maskArr[$i] = ord($mask[$i]); } for ($i = 0; $i < $payloadLen; $i++) { $dest .= chr(ord($payload[$i]) ^ $maskArr[$i % 4]); } return $dest; } function parseTextFrame($payload, $mask) { return $this->parseRawFrame($payload, $mask); } function parseBinaryFrame($payload, $mask) { return $this->parseRawFrame($payload, $mask); }
closeFrame函数:
//创建并发送关闭帧 function closeFrame($socketId, $closeCode = 1000, $closeMsg = 'goodbye') { $closeCode = chr(intval($closeCode / 256)) . chr($closeCode % 256); $frame = $this->createFrame($closeCode . $closeMsg, self::FRAME_CLOSE); $this->socketSend($socketId, $frame); $this->disconnect($socketId); }
sendPing、sendPong函数:
function sendPing($socketId, $data = 'ping') { $frame = $this->createFrame($data, self::FRAME_PING); $this->socketSend($socketId, $frame); } function sendPong($socketId, $data = 'pong') { $frame = $this->createFrame($data, self::FRAME_PONG); $this->socketSend($socketId, $frame); }
createFrame函数:
//封装帧头的相关标识位、长度等信息 function createFrame($data, $type, $fin = 0x01) { $dataLen = strlen($data); $frame = chr(($fin << 7) + $type); if ($dataLen < 126) { $frame .= chr($dataLen); } else if ($dataLen < 65536) { $frame .= chr(126); $frame .= chr(intval($dataLen / 256)); $frame .= chr(intval($dataLen % 256)); } else { $frame .= chr(127); $hexLen = str_pad(base_convert($dataLen, 10, 16), 16, '0', STR_PAD_LEFT); for ($i = 0; $i < 15; $i += 2) { $frame .= chr((intval($hexLen[$i], 16) << 8) + intval($hexLen[$i + 1], 16)); } } $frame .= $data; return $frame; }