<?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>