PHP connect as client to NodeJS Web socket - php

Need my laravel app to connect to a nodejs based socket server. The function needs to pass custom headers in order to authenticate as well as keep pinging the connection every 1 minute in order to prevent disconnection from the socket server
Can anyone recommend any good libraries to use to achieve above functionality. Here, the PHP is acting as a client
Thanks in advance

Thanks for you suggestions, I ended up using ratchet/pawl client, which works well:
Example:
$headers = ['HEADER' => 'Value'];
$ip = '1.1.1.1';
\Ratchet\Client\connect($ip,[],$headers)->then(function($conn) use ($payload) {
$conn->on('message', function($msg) use ($conn) {
$response = json_decode($msg, TRUE);
var_dump($response);
$conn->close();
});
$conn->send('Hello!');
$conn->on('close', function ($code = null, $reason = null) use ($connector, $loop, $app) {
echo "Connection closed ({$code} - {$reason})\n";
});
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});

Related

Trigger a websocket from outside via PHP to send a message to clients

my goal is to somehow trigger a secure ratchet websocket, where my clients have logged in via javascript, to send an update message to them.
My secure ratchet websocket is already working fine, as well as the wss connection via javascript, the clients can connect and disconnect from the websocket.
Websocket (PHP):
use Ratchet\Http\HttpServer;
use socket\secure\IoSecureServer;
use Ratchet\WebSocket\WsServer;
use socket\http\WebHttpServer;
$http_server = IoSecureServer::factory(
new HttpServer(
new WsServer(
new WebHttpServer()
)
),
8091,
"0.0.0.0",
array(
"local_cert" => "path/to/my/cert",
"local_pk" => "path/to/my/key",
"allow_self_signed" => true,
"verify_peer" => false
)
);
$http_server->run();
Client (JS):
let web_socket = new WebSocket("wss://domainname.de:8091");
web_socket.onopen = function () {
//do some stuff
console.log("Connected to websocket");
}
web_socket.onclose = function () {
//do some stuff
console.warn("Connection closed");
}
web_socket.onerror = function () {
//do some stuff
web_socket.close();
}
web_socket.onmessage = function (ev) {
//do some stuff
}
Idea:
Now I was wondering how I could trigger this ratchet websocket:
My idea:
Through a connector I send a message to the socket, it recognizes that it was sent from the same device and accepts the content, evaluates it and sends a message with new information to all clients.
Problem:
I do not manage to reach the socket with a connector. The best attempt so far was the following:
$contextOptions = array(
'ssl' => array(
"verify_peer"=>false,
"verify_peer_name"=>false
)
);
$context = stream_context_create($contextOptions);
$fp =stream_socket_client("ssl://domainname.de:8091",$errstr,$errno,30,STREAM_CLIENT_CONNECT,$context);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
fwrite($fp, "GET / HTTP/1.1\r\nHost: domainname.de\r\nAccept: */*\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n");
fclose($fp);
}
So i get the following output:
[2022-07-02 12:29:20] (68) connected to the server
[2022-07-02 12:29:20] 68 disconnected from the server
It should be noted that the ratchet server cannot determine an IP address.
I would like to know how I can get the IP address to be recognized and still be able to send a message?
Or is there another way to tell a ratchet websocket: now you have to send out a message?
Thanks a lot for your support!

Swoole with RabbitMQ

I'm trying to send some data from php application to the user's browser using websockets. Therefore I've decided to use Swoole in combination with RabbitMQ.
It's the first time I'm working with websockets and after reading some posts about Socket.IO, Ratchet, etc. I've decided to halt on Swoole because it's written in C and handy to use with php.
This is how I understood the idea of enabling data transfer using websockets:
1) Start RabbitMQ worker and Swoole server in CLI
2) php application sends data to RabbitMQ
3) RabbitMQ sends message with data to worker
4) Worker receives message with data + establishes socket connection with Swoole socket server.
5) Swoole server broadcasts data to all connections
The question is how to bind Swoole socket server with RabbitMQ? Or how to make RabbitMQ to establish connection with Swoole and send data to it?
Here is the code:
Swoole server (swoole_sever.php)
$server = new \swoole_websocket_server("0.0.0.0", 2345, SWOOLE_BASE);
$server->on('open', function(\Swoole\Websocket\Server $server, $req)
{
echo "connection open: {$req->fd}\n";
});
$server->on('message', function($server, \Swoole\Websocket\Frame $frame)
{
echo "received message: {$frame->data}\n";
$server->push($frame->fd, json_encode(["hello", "world"]));
});
$server->on('close', function($server, $fd)
{
echo "connection close: {$fd}\n";
});
$server->start();
Worker which receives message from RabbitMQ, then makes connection to Swoole and broadcasts the message via socket connection (worker.php)
$connection = new AMQPStreamConnection('0.0.0.0', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('task_queue', false, true, false, false);
echo ' [*] Waiting for messages. To exit press CTRL+C', "\n";
$callback = function($msg){
echo " [x] Received ", $msg->body, "\n";
sleep(substr_count($msg->body, '.'));
echo " [x] Done", "\n";
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
// Here I'm trying to make connection to Swoole server and sernd data
$cli = new \swoole_http_client('0.0.0.0', 2345);
$cli->on('message', function ($_cli, $frame) {
var_dump($frame);
});
$cli->upgrade('/', function($cli)
{
$cli->push('This is the message to send to Swoole server');
$cli->close();
});
};
$channel->basic_qos(null, 1, null);
$channel->basic_consume('task_queue', '', false, false, false, false, $callback);
while(count($channel->callbacks)) {
$channel->wait();
}
$channel->close();
$connection->close();
New task where the message will be send to RabbitMQ (new_task.php):
$connection = new AMQPStreamConnection('0.0.0.0', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('task_queue', false, true, false, false);
$data = implode(' ', array_slice($argv, 1));
if(empty($data)) $data = "Hello World!";
$msg = new AMQPMessage($data,
array('delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT)
);
$channel->basic_publish($msg, '', 'task_queue');
echo " [x] Sent ", $data, "\n";
$channel->close();
$connection->close();
After starting both swoole server and worker I'm triggering new_task.php from command line:
php new_task.php
In command line prompt where a RabbitMQ Worker is running (worker.php) I can see that a message is delivered to the worker ("[x] Received Hello World!" message is appearing).
However in command line prompt where Swoole server is running happens nothing.
So the questions are:
1) Is the idea of this approach right?
2) What am I doing wrong?
In the callback(in worker.php) that fires when a message is received you're using swoole_http_client which is async only. This seems to results in the code never being fully executed as the callback function returns before the async code is triggered.
A synchronous method of doing the same thing will solve the problem. Here is a simple example:
$client = new WebSocketClient('0.0.0.0', 2345);
$client->connect();
$client->send('This is the message to send to Swoole server');
$recv = $client->recv();
print_r($recv);
$client->close();
Check out the WebSocketClient class and example usage at github.
You can also wrap it in a coroutine, like this:
go(function () {
$client = new WebSocketClient('0.0.0.0', 2345);
$client->connect();
$client->send('This is the message to send to Swoole server');
$recv = $client->recv();
print_r($recv);
$client->close();
});

Receiving RabbitMQ messages in PHPRatchet

I am trying to achieve mechanism where PHP pushes message to RabbitMQ (I don't want RabbitMQ to be directly exposed to user), RabbitMQ connects to RatchetPHP and Ratchet broadcasts it via websocket connections to users.
The issue I have is with accually making Ratchet server to simultanously listen for queue messages and transfer them further. Ratchet documentation assumes using ZeroMQ and after a long search through outdated documentations and libraries which do not have such methods anymore (eg. React\Stomp) I need fresh eyes from someone who has experience with these solutions.
What I have is pusher.php (standard example from RabbitMQ docs):
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('hello', false, false, false, false);
$msg = new AMQPMessage('Hello World!');
$channel->basic_publish($msg, '', 'hello');
echo " [x] Sent 'Hello World!'\n";
$channel->close();
$connection->close();
Just to simplify reproducing scenario I include also Chat class:
use Ratchet\ConnectionInterface;
use Ratchet\MessageComponentInterface;
class Chat implements MessageComponentInterface
{
protected $clients;
public function __construct()
{
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $connection)
{
// Store the new connection to send messages to later
$this->clients->attach($connection);
echo "New connection! ({$connection->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg)
{
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending message "%s" to %d other connection%s'."\n"
, $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
foreach($this->clients as $client)
{
/** #var \SplObjectStorage $client */
if($from !== $client)
{
// The sender is not the receiver, send to each client connected
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn)
{
// The connection is closed, remove it, as we can no longer send it messages
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
And Ratchet server.php (standard Ratchet example and RabbitMQ receiver example):
use PhpAmqpLib\Connection\AMQPStreamConnection;
use Src\Chat;
// RABBIT_RECEIVER
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('hello', false, false, false, false);
echo ' [*] Waiting for messages. To exit press CTRL+C', "\n";
$callback = function ($msg)
{
echo " [x] Received ", $msg->body, "\n";
};
$channel->basic_consume('hello', '', false, true, false, false, $callback);
while(count($channel->callbacks))
{
$channel->wait();
}
$channel->close();
$connection->close();
// RABBIT_RECEIVER END
$server = new \Ratchet\App('sockets.dev');
$server->route('/', new Chat());
$server->run();
Current versions are basically 2 separate mechanisms listening for messages and they work great alone (so no issue there) except that they block each other and do not transfer messages between.
Question is how to make server.php to make RabbitMQ receive message and plug it into running Ratchet server.
I figured it out so adding answer for posterity. Solution is not fully real time but it is very close, seems to have good performance and is non-blocking for Ratchet websocket server. Solution was in Ratchet custom loop and addPeriodicTimer method.
So code for server.php should look like this:
$loop = React\EventLoop\Factory::create();
$chat = new Chat($loop);
$server = new \Ratchet\App('sockets.dev', 8080, '127.0.0.1', $loop);
$server->route('/', $chat);
$server->run();
And Chat.php class (only __constructor because rest is the same):
public function __construct(LoopInterface $loop)
{
$this->loop = $loop;
$this->clients = new \SplObjectStorage();
$this->loop->addPeriodicTimer(0, function ()
{
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('hello', false, false, false, false);
echo ' [*] Checking for for messages from RabbitMQ', "\n";
$max_number_messages_to_fetch_per_batch = 10000;
do
{
$message = $channel->basic_get('hello', true);
if($message)
{
foreach($this->clients as $client)
{
$client->send($message->body);
}
$max_number_messages_to_fetch_per_batch--;
}
}
while($message && $max_number_messages_to_fetch_per_batch > 0);
$channel->close();
$connection->close();
});
}
Initiating Ratchet server will attach periodic event (in example every 0 sec) which will check RabbitMQ for new messages and process them.
Just for better performance control allowing websocket to take a breath number of messages from RabbitMQ processed in one batch was limited to 10k. Processed messages are removed from queue and next batch is processed in next iteration.
You can also fine tune frequency of updates with addPeriodicTimer interval parameter. 0 seconds is the closest to real time you will get but it may not be required for your needs and can be changed to higher value.

PHP - Combining Websocket Client & Server (Relay)

I want to create a PHP script that connects to a websocket server and then serves the responses from the remote server on a local websocket server. Basically, a websocket relay. I have working client code and I have working server code.
The problem comes when I try to combine the client with the server. I've tried taking my client code and putting it in my server code. It works except that the client is blocking so that if I disconnect from it, I can't reconnect. It happens like this:
I start my code -> server code opens local port and serves websocket -> I connect to local port with websocket testing tool -> client code connects to remote websocket server and relays responses to me -> I disconnect my tool -> client code never returns so server code can't accept new connections anymore.
My code uses the following composer packages:
"cboden/ratchet": "^0.4.0",
"ratchet/pawl": "^0.3.1"
You can see that I have commented out the client part that connects to the remote websocket server. This is the part that blocks.
<?php
// Make sure composer dependencies have been installed
require '../web/vendor/autoload.php';
require_once('../web/usersc/includes/custom_functions.php');
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use WebSocket\Client;
/**
* chat.php
* Send any incoming messages to all connected clients (except sender)
*/
class MyChat implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
/*
$loop = React\EventLoop\Factory::create();
$reactConnector = new React\Socket\Connector($loop, [
'dns' => '8.8.8.8',
'timeout' => 10
]);
$connector = new Ratchet\Client\Connector($loop, $reactConnector);
$connector('wss://example.com/websocket', [], ['Origin' => 'http://localhost'])
->then(function(Ratchet\Client\WebSocket $conn) {
$conn->on('message', function(\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($conn) {
echo "Received: {$msg}\n";
//$conn->close();
});
$conn->on('close', function($code = null, $reason = null) {
echo "Connection closed ({$code} - {$reason})\n";
});
$conn->send('Hello World!');
}, function(\Exception $e) use ($loop) {
echo "Could not connect: {$e->getMessage()}\n";
$loop->stop();
});
$loop->run();
*/
}
public function onMessage(ConnectionInterface $from, $msg) {
foreach ($this->clients as $client) {
if ($from != $client) {
$client->send($msg."sdfsfsf");
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e) {
$conn->close();
}
}
// Run the server application through the WebSocket protocol on port 8080
$app = new Ratchet\App('localhost', 8080);
$app->route('/chat', new MyChat, array('*'));
$app->run();

JAXL based chat client. Need help connecting to Gtalk or other server to test

I wish to send a message to XMPP based chat servers using php.
I am using JAXL, which seems the best (of limited) options for pure PHP server based chat.
However, I have yet to establish any connect, let alone send a message.
I am having a hard time working out if the problem is my code, my server (which is shared server, but has Cpanel, and a very helpfull host), or my settings.
The code I am using to try to connect to GTalk is;
$client = new JAXL(array(
'jid' => 'name#gmail.com',
'pass' => 'password',
'host'=> 'talk.google.com',
'port'=> 5222,
'domain'=> 'gmail.com', //unsure if this is the right setting.
'force_tls' => true,
'auth_type' => #$argv[3] ? $argv[3] : 'PLAIN',
));
//
// required XEP's
//
$client->require_xep(array(
'0199' // XMPP Ping
));
//
// add necessary event callbacks here
//
$client->add_cb('on_auth_success', function() {
global $client;
_info("got on_auth_success cb, jid ".$client->full_jid->to_string());
// fetch roster list
$client->get_roster();
// fetch vcard
$client->get_vcard();
// set status
$client->set_status("available!", "dnd", 10);
});
$client->add_cb('on_connect_error', function() {
echo 'Connect Error';
});
$client->add_cb('on_auth_failure', function() {
echo 'Auth Error';
});
$client->add_cb('on_auth_success', function() {
global $client;
echo 'connected';
$client->send_chat_msg('test2#domain.com', 'webtest');
$client->shutdown();
});
//
// finally start configured xmpp stream
//
$client->start(array(
'--with-debug-shell' => true,
'--with-unix-sock' => true
));
echo "done\n";
Triggering the php (from a browser) then results in the server getting stuck. (no "done" message, just constant loading till a timeout from the browser)
The server logs show;
strict mode enabled, adding exception handlers. Set 'strict'=>TRUE inside JAXL config to disable this[0m
error handler called with 8, Undefined index: priv_dir,
And then lots of;
unable to connect tcp://talk.google.com:5222 with error no: 110, error str: Connection timed out
So I would appreciate help with any of the following;
Any specific problems with my code
Any issues with the gtalk connection settings at the start
Alternative recommendations to investigate this issue.
Or any general advice from people that have successfully used JAXL.
Thanks,
Thomas Wrobel
Ok problem might be TCP port is closed for your hosting , try to open it with your hosting first, also try to run your code locally to see if it's work fine .
Some people reported the issue was fixed by override method connect from file XMLStream.php by this one
/**
* Connect to XMPP Host
*
* #param integer $timeout
* #param boolean $persistent
* #param boolean $sendinit
*/
public function connect($timeout = 30, $persistent = false, $sendinit = true) {
$this->sent_disconnect = false;
$starttime = time();
do {
$this->disconnected = false;
$this->sent_disconnect = false;
if($persistent) {
$conflag = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;
} else {
$conflag = STREAM_CLIENT_CONNECT;
}
$conntype = 'tcp';
if($this->use_ssl) $conntype = 'ssl';
$this->log->log("Connecting to $conntype://{$this->host}:{$this->port}");
try {
$this->socket = #stream_socket_client("$conntype://{$this->host}:{$this->port}", $errno, $errstr, $timeout, $conflag);
} catch (Exception $e) {
throw new XMPPHP_Exception($e->getMessage());
}
if(!$this->socket) {
$this->log->log("Could not connect.", XMPPHP_Log::LEVEL_ERROR);
$this->disconnected = true;
# Take it easy for a few seconds
sleep(min($timeout, 5));
}
} while (!$this->socket/* && (time() - $starttime) < $timeout*/);
if ($this->socket) {
stream_set_blocking($this->socket, 0);
if($sendinit) $this->send($this->stream_start);
} else {
throw new XMPPHP_Exception("Could not connect before timeout.");
}
}

Categories