Skip to content

Instantly share code, notes, and snippets.

@mudassaralichouhan
Created January 25, 2023 13:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mudassaralichouhan/59e436d80cc465068a191bca964f5a65 to your computer and use it in GitHub Desktop.
Save mudassaralichouhan/59e436d80cc465068a191bca964f5a65 to your computer and use it in GitHub Desktop.
web-socket
<?php
namespace App\WebSocket;
class Notification extends WebSocket
{
}
<?php
use App\Controllers\Auth\Authorizer;
use App\WebSocket\WebSocket;
require __DIR__ . '/bootstrap.php';
$address = gethostbyname('localhost');
$port = '9000';
$socket = new WebSocket($address, $port, 0.1);
$approved = function ($token) {
$class = Authorizer::getModal(preg_replace('/\d/', '', $token));
$id = str_replace('#', '', $token);
$_ENV['database.database'] = 'userdb_347_28385';
if ((new $class())->select('1')->find($id))
return true;
return false;
};
$filter_token = function ($request) {
preg_match('([#]+[0-9]+)', $request, $request);
return $request[0];
};
$socket->start($approved, $filter_token);
<?php
namespace App\WebSocket;
use Exception;
use Socket;
class WebSocket
{
private array $clients = [];
private Socket $server;
private int $sleep = 0;
public function __construct(string $address, int $port, int $sleep)
{
//Create TCP/IP stream socket
if (!$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) {
$error_code = socket_last_error();
$error_message = socket_strerror($error_code);
die("Couldn't create socket: [$error_code] $error_message \n");
}
echo "Socket: created ws://$address:$port \n";
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
//bind socket to specified host
if (!socket_bind($server, $address, $port)) {
$error_code = socket_last_error();
$error_message = socket_strerror($error_code);
die("Could not bind socket : [$error_code] $error_message \n");
}
echo "Socket: bind $address:$port \n";
if (!socket_listen($server)) {
$error_code = socket_last_error();
$error_message = socket_strerror($error_code);
die("Could not listen on socket : [$error_code] $error_message \n");
}
echo "Socket: listen $port \n";
$this->server = $server;
//create & add listening socket to the list
$this->clients = [$this->server];
$this->sleep = $sleep;
}
public function start($approved, $filter_token)
{
try {
//start endless loop, so that our script doesn't stop
while (true) {
// manage multiple connections
$changed = $this->clients;
// returns the socket resources in $changed array
socket_select($changed, $write, $except, 1, 10);
// check for new socket
if (in_array($this->server, $changed)) {
$server_new = socket_accept($this->server); // accept new socket
$request = socket_read($server_new, 1024); //read data sent by the socket
$upgrade = $this->handshaking($request); //perform websocket handshake
$ip = $this->getIP($server_new);
$token = $filter_token($request);
if ($approved($token)) {
$this->clients[$token] = $server_new; // add socket to client array
@socket_write($server_new, $upgrade, strlen($upgrade));
// notify all users about new connection
$this->send('system', [$ip . ' connected'], $this->clients);
echo 'Socket: Connect ' . $ip . ' with auth-token ' . $token . PHP_EOL;
} else {
echo 'Socket: denied ' . $ip . ' with auth-token ' . $token . PHP_EOL;
}
// make room for new socket
$found_socket = array_search($this->server, $changed);
unset($changed[$found_socket]);
}
// loop through all connected sockets
foreach ($changed as $token => $changed_socket) {
// check for any incoming data
while (socket_recv($changed_socket, $buf, 1024, 0) >= 1) {
//prepare data to be sent to client
$client_res = json_decode($this->unmask($buf));
if ($client_res->receiver_to ?? false) {
$client_res->receiver_to = array_unique((array)$client_res->receiver_to);
if (($req_collection = array_intersect_key($this->clients, array_flip($client_res->receiver_to))) !== [])
$this->send('client', [$client_res], $req_collection);
else
echo 'Socket: not sending... data ' . PHP_EOL;
} else {
$this->send('system', ['set attr `[receiver_to]` ids'], [$this->clients[$token]]);
echo 'Socket: not set sender information attribute' . PHP_EOL;
}
break 2;
}
$buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ);
if ($buf === false) {
// remove client for $clients array
$found_socket = array_search($changed_socket, $this->clients);
$ip = $this->getIP($changed_socket);
unset($this->clients[$found_socket]);
// notify all users about disconnected connection
$this->send('system', [$ip . 'disconnected'], $this->clients);
echo 'Socket: sleep socket for ' . $ip . PHP_EOL;
}
echo 'Socket: socket is running ' . PHP_EOL;
echo 'Socket: selected ' . count($changed) . PHP_EOL;
}
echo 'Socket: listing ' . count($this->clients) . PHP_EOL;
sleep($this->sleep);
}
} catch (Exception $e) {
echo $e->getMessage();
socket_close($this->server);
}
}
public function send(string $type, array $response, array $clients): void
{
$msg = $this->mask($type, $response);
foreach ($clients as $token => $changed_socket) {
@socket_write($changed_socket, $msg, strlen($msg));
echo 'Socket: sending... data to ' . $token . PHP_EOL;
}
}
// Unmask incoming framed message
public function unmask(string $buffer): string
{
$length = ord($buffer[1]) & 127;
if ($length == 126) {
$masks = substr($buffer, 4, 4);
$data = substr($buffer, 8);
} elseif ($length == 127) {
$masks = substr($buffer, 10, 4);
$data = substr($buffer, 14);
} else {
$masks = substr($buffer, 2, 4);
$data = substr($buffer, 6);
}
$buffer = '';
for ($i = 0; $i < strlen($data); ++$i) {
$buffer .= $data[$i] ^ $masks[$i % 4];
}
return $buffer;
}
// Encode message for transfer to client.
public function mask(string $type, array $response): string
{
$b1 = 0x80 | (0x1 & 0x0f);
$res = json_encode(['type' => $type, 'data' => $response]);
$length = strlen($res);
if ($length <= 125)
$header = pack('CC', $b1, $length);
elseif ($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif ($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header . $res;
}
public function handshaking($received_header): string
{
$lines = preg_split("/\r\n/", $received_header);
foreach ($lines as $line) {
$line = chop($line);
if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'] ?? '';
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
//hand shaking header
return "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
}
private function getIP(Socket $socket)
{
socket_getpeername($socket, $ip);
return $ip;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment