Skip to content

Instantly share code, notes, and snippets.

@mudassaralichouhan
Last active January 31, 2023 11:46
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/48091facebc319a878d7c9d79aa21be5 to your computer and use it in GitHub Desktop.
Save mudassaralichouhan/48091facebc319a878d7c9d79aa21be5 to your computer and use it in GitHub Desktop.
web socket example
<?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();
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);
}
}
};
<?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