WebSocket协议聊天室

<?php

$boolCli =  preg_match("/cli/i", php_sapi_name()) ? true : false;

$arrConfig = [
        'ip'=>"192.168.33.10",
        'port'=>1337,
];

if($boolCli)
{
    $arrConfig = new WebSocket($arrConfig);
    $arrConfig->start();
}

class WebSocket
{
    private $Socket;

    private $Connect=[];

    public function __construct($arrConfig)
    {
        $this->Socket = stream_socket_server('tcp://'.$arrConfig['ip'].':'.$arrConfig['port'], $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);

        stream_set_blocking($this->Socket, 0);
    }

    public function onConnect($strPeer,$resNewConnect)
    {
        $this->Connect[$strPeer] = $resNewConnect;
    }

    public function onClose($strPeer)
    {
        echo PHP_EOL.'关闭'.$strPeer.PHP_EOL;

        fclose($this->Connect[$strPeer]);
        unset($this->Connect[$strPeer]);
    }

    public function onRecev($strRecev,$strPeer)
    {

        $intHeaderEndPos = strpos($strRecev, "\r\n\r\n");

        //首次连接是一个HTTP请求 , 所以我们用检测\r\n\r\n是否存在来判断是建立连接还是接收数据
        if (!$intHeaderEndPos)
        {
            //说明此次通讯是交互数据,所以需要解密数据

            $strRecev = $this->decode($strRecev);

            $strReturn = $strPeer.':'.$strRecev;

            $strReturn = $this->encode($strReturn);

            foreach ($this->Connect as $resTempConnect)
            {
                $this->send($resTempConnect, $strReturn);
            }
        }
        else
        {
            //说明此次通讯是连接通知, 我们需要回复一个WS协议的连接成功通知,并且将客户端发给我们的秘钥计算三列值后还给他!

            //这是一个固定的编码
            $strKey = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

            $strSecWebSocketKey = '';

            if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $strRecev, $arrMatch))
            {
                $strSecWebSocketKey = $arrMatch[1];
            }
            $strNewKey = base64_encode(sha1($strSecWebSocketKey.$strKey, true));

            $strReturn = "HTTP/1.1 101 Switching Protocols\r\n";
            $strReturn .= "Upgrade: websocket\r\n";
            $strReturn .= "Sec-WebSocket-Version: 13\r\n";
            $strReturn .= "Connection: Upgrade\r\n";
            $strReturn .= "Sec-WebSocket-Accept: " . $strNewKey . "\r\n";
            $strReturn .= "\r\n";

            $this->send($this->Connect[$strPeer], $strReturn);
        }
    }

    public function send($resTempConnect, $strReturn)
    {
        fwrite($resTempConnect, $strReturn);
    }

    public function start()
    {
        while (true)
        {
            $resNewConnect = @stream_socket_accept($this->Socket, empty($this->Connect) ? -1 : 0, $strPeer);
            if ($resNewConnect)
            {
                $this->onConnect($strPeer, $resNewConnect);
            }

            $arrRead = $this->Connect;

            if (stream_select($arrRead, $arrWrite, $arrExcept, 1))
            {
                foreach ($arrRead as $resTempConnect)
                {
                    $strPeer = stream_socket_get_name($resTempConnect, true);

                    if (feof($resTempConnect))
                    {
                        $this->onClose($strPeer);
                    }
                    else
                    {
                        $this->onRecev(fread($resTempConnect, 1024),$strPeer);
                    }
                }
            }
        }
    }

    private function decode($strData)
    {
        $strMasks = $strTempData = $strDecode = '';
        $len = ord($strData[1]) & 127;
        if ($len === 126)
        {
            $strMasks = substr($strData, 4, 4);
            $strTempData  = substr($strData, 8);
        }
        else
        {
            if ($len === 127)
            {
                $strMasks = substr($strData, 10, 4);
                $strTempData  = substr($strData, 14);
            }
            else
            {
                $strMasks = substr($strData, 2, 4);
                $strTempData  = substr($strData, 6);
            }
        }
        for ($index = 0; $index < strlen($strTempData); $index++)
        {
            $strDecode .= $strTempData[$index] ^ $strMasks[$index % 4];
        }
        return $strDecode;
    }

    private function encode($strData)
    {
        $intLen = strlen($strData);

        $strFirstByte = "\x81";

        if ($intLen <= 125)
        {
            $strEncode = $strFirstByte . chr($intLen) . $strData;
        }
        else
        {
            if ($intLen <= 65535)
            {
                $strEncode = $strFirstByte . chr(126) . pack("n", $intLen) . $strData;
            } else {
                $strEncode = $strFirstByte . chr(127) . pack("xxxxN", $intLen) . $strData;
            }
        }
        return $strEncode;
    }
}
?>
<html>
<head>
<meta charset="utf-8"/>
<script>
	var socket;
	if ("WebSocket" in window) {
		var ws = new WebSocket("ws://<?php echo $arrConfig['ip'].':'.$arrConfig['port'];?>");
		socket = ws;
		ws.onopen = function()
		{
			document.getElementById("showMes").value += '连接成功!' + "\n";
		};
		ws.onmessage = function(evt)
		{
			var received_msg = evt.data;
			document.getElementById("showMes").value += evt.data + "\n";
		};
		ws.onclose = function()
		{
			document.getElementById("showMes").value += '断开连接!' + "\n";
			alert("断开了连接");
		};
	}
	else
	{
		alert("浏览器不支持WebSocket");
	}
	function login()
	{
		var message = document.getElementById("mes").value;
		socket.send(message);
	}
</script>
</head>
<body>
	<textarea rows="3" cols="30" id="showMes" style="width: 300px; height: 500px;"></textarea><br />
	<label>消息</label>
	<input type="text" id="mes" />
	<button onclick="login();">发送</button>
</body>
</html>