torneos/bin/websocket-server.php

89 lines
2.7 KiB
PHP

<?php
require __DIR__ . '/../app/Core/Database.php';
use App\Core\Database;
$server = stream_socket_server('tcp://0.0.0.0:8081', $errno, $errstr);
if (!$server) {
fwrite(STDERR, "WebSocket error: $errstr\n");
exit(1);
}
stream_set_blocking($server, false);
$clients = [];
$lastEventId = 0;
echo "WebSocket listening on 0.0.0.0:8081\n";
while (true) {
$read = array_merge([$server], array_column($clients, 'socket'));
$write = $except = [];
@stream_select($read, $write, $except, 1);
foreach ($read as $socket) {
if ($socket === $server) {
$client = stream_socket_accept($server, 0);
if ($client) {
stream_set_blocking($client, false);
$clients[(int) $client] = ['socket' => $client, 'handshake' => false];
}
continue;
}
$id = (int) $socket;
$buffer = fread($socket, 2048);
if ($buffer === '' || $buffer === false) {
fclose($socket);
unset($clients[$id]);
continue;
}
if (!$clients[$id]['handshake']) {
if (preg_match('/Sec-WebSocket-Key: (.*)\r\n/i', $buffer, $matches)) {
$key = trim($matches[1]);
$accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
fwrite($socket, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $accept\r\n\r\n");
$clients[$id]['handshake'] = true;
}
}
}
try {
$db = Database::connection();
$stmt = $db->prepare('SELECT id, match_id, event_type, points_home, points_away FROM match_events WHERE id > :id ORDER BY id ASC LIMIT 50');
$stmt->execute(['id' => $lastEventId]);
foreach ($stmt->fetchAll() as $event) {
$lastEventId = (int) $event['id'];
broadcast($clients, json_encode(['type' => 'match_event', 'data' => $event]));
}
} catch (Throwable $e) {
usleep(300000);
}
}
function broadcast(array &$clients, string $payload): void
{
$frame = frame($payload);
foreach ($clients as $id => $client) {
if (!$client['handshake']) {
continue;
}
if (@fwrite($client['socket'], $frame) === false) {
fclose($client['socket']);
unset($clients[$id]);
}
}
}
function frame(string $payload): string
{
$length = strlen($payload);
if ($length <= 125) {
return chr(129) . chr($length) . $payload;
}
if ($length <= 65535) {
return chr(129) . chr(126) . pack('n', $length) . $payload;
}
return chr(129) . chr(127) . pack('J', $length) . $payload;
}