Last active
January 31, 2023 11:46
-
-
Save mudassaralichouhan/48091facebc319a878d7c9d79aa21be5 to your computer and use it in GitHub Desktop.
web socket example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
session_start(); | |
require 'constants.php'; | |
require ROOTPATH . 'web-socket/WebSocket.php'; | |
require ROOTPATH . 'libs/classes/DB.php'; | |
$_SESSION['company_db'] = 'erp_clients'; | |
$address = gethostbyname('localhost'); | |
$port = '9000'; | |
$socket = new WebSocket($address, $port, 0.1); | |
$socket->start(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const address = '127.0.0.1'; | |
const port = '9000'; | |
let uri = "token=GhqMrFYIt2SH1bSaDTPa3x944v70kZ5CPAHs77jCpG37O1YSPlsRNdFR2TxP---1"; | |
const websocket = new WebSocket(`ws://${address}:${port}?${uri}`); | |
websocket.onopen = (res) => { | |
console.log(res.data); | |
} | |
websocket.onerror = (res) => { | |
console.log('websocket.error'); | |
listGroups(); | |
}; | |
websocket.onclose = (res) => { | |
console.log('websocket.onclose'); | |
}; | |
websocket.onmessage = (res) => { | |
const response = JSON.parse(res.data); | |
let selectGroup = document.querySelector('div[data-group-id="' + response.data[0].group_id + '"]'); | |
listGroups(); | |
if (selectGroup) { | |
group_conversation_list(selectGroup); | |
} else { | |
if (selectGroup) { | |
selectGroup = document.querySelector('div[data-group-id="' + response.data[0].group_id + '"]'); | |
group_conversation_list(selectGroup); | |
} | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class WebSocket | |
{ | |
private array $clients = []; | |
private $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(): void | |
{ | |
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 | |
$headers = $this->getHeaders($request); | |
$upgrade = $this->handshaking($headers); //perform websocket handshake | |
$ip = $this->getIP($server_new); | |
list($id, $authorizer, $token, $device_id) = self::AuthHeaders($request); | |
if ($token == NULL) { | |
echo 'Socket: denied ' . $ip . ' because not have [Authorization Token] ' . PHP_EOL; | |
} /*else if ($device_id == NULL) { | |
echo 'Socket: denied ' . $ip . ' because not have [Device Token] ' . PHP_EOL; | |
} */ else if ($this->approved($id, $authorizer, $token, $device_id) === true) { | |
$this->clients[$id] = $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 ' . $id . PHP_EOL; | |
} else { | |
echo 'Socket: denied ' . $ip . ' with auth-token ' . ((string)$id) . 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) && isset($client_res->data)) { | |
$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->data], $req_collection); | |
} else { | |
echo 'Socket: not send data ' . PHP_EOL; | |
} | |
} else { | |
$this->send('system', ['{"receiver_to": ["###id","###id",...],"data": [...]}'], [$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); | |
} | |
} | |
private 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 | |
private 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. | |
private 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 getHeaders(string $received_header): array | |
{ | |
$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]; | |
} | |
} | |
return $headers ?? []; | |
} | |
private function handshaking($received_header): string | |
{ | |
$secKey = $received_header['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; | |
} | |
function approved(string $id, string $authorizer, string $token, string $device_id = null) | |
{ | |
$allow_user_privilege = [ | |
'####' => 'customers', | |
'###' => 'employee_master', | |
]; | |
$table = $allow_user_privilege[$authorizer] ?? false; | |
try { | |
$_SESSION['company_db'] = 'erp_clients'; | |
$db = new DB(); | |
$db_name = $db->getRecord("select `db_name` from `tokens` where `token` = '$token'"); | |
$_SESSION['company_db'] = $db_name['db_name']; | |
$db = new DB(); | |
$id = str_replace('#', '', $id); | |
$in_user = $db->getRecord("select sno from `$table` where `sno` = '$id'"); | |
if ($in_user['sno'] ?? false) | |
return true; | |
} catch (Exception $e) { | |
echo $e->getMessage() . PHP_EOL; | |
return false; | |
} | |
return false; | |
} | |
public function AuthHeaders(string $request): array | |
{ | |
// if incoming request have headers | |
$headers = $this->getHeaders($request); | |
if (isset($headers['token'])) { | |
$token = $headers['token'] ?? ''; | |
preg_match('([#]+[0-9]+)', $token, $id); | |
$token = str_replace($id[0], '', $token); | |
preg_match('/([#]+)/', $id[0], $authorizer); | |
return [$id[0] ?? null, $authorizer[0] ?? null, $token ?? null, $headers['device_id'] ?? null]; | |
} | |
// if incoming request have query-string | |
preg_match('/token=([^(&\s)]*)/', $request, $client_token); | |
preg_match('/device_id=([^(&\s)]*)/', $request, $device_id); | |
if ($client_token[1] ?? false) { | |
preg_match('([-]+[0-9]+)', $client_token[1], $id); | |
$token = str_replace($id[0], '', $client_token[1]); | |
preg_match('([-]+)', $id[0], $authorizer); | |
$id = str_replace('-', '#', $id[0] ?? ''); | |
$authorizer = str_replace('-', '#', $authorizer[0] ?? ''); | |
} | |
return [$id ?? null, $authorizer ?? null, $token ?? null, $device_id[1] ?? null]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment