I have a PHP program where I connect to a Rabbit MQ server and retrieve messages. I have put this functionality inside a function:
function get_messages()
{
$connection = new AMQPConnection();
$connection->setLogin($rabbit_username);
$connection->setPassword($rabbit_passwd);
$connection->setHost($rabbit_host);
while (!$connection->connect())
{
echo "## Trying to connect to Rabbit MQ...\n";
sleep(1);
}
$amqpchn = new AMQPChannel($connection);
$mq = new AMQPQueue($amqpchn);
$mq->setName("myqueue");
$mq->setFlags(AMQP_DURABLE|AMQP_PASSIVE);
$mq->declare(); // must declare then bind
$mq->bind("my.exchange","my.routing");
// do stuff
}
This works fine. However when I try to run the function get_messages() from inside a thread (just one thread), the code gets stuck at $connection->connect(). It cannot connect to the Rabbit server.
Any ideas why this happens?
Thanks in advance
Related
As a simple example, say I simply want to increment a counter when someone connects. The code I have is
use Swoole\WebSocket\Server;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
$server = new Server("0.0.0.0", 9502);
$users = [];
$server->on("Start", function(Server $server){
echo "Swoole WebSocket Server is started at http://127.0.0.1:9502\n";
});
$server->on('Open', function(Server $server, Swoole\Http\Request $request){
global $count;
array_push($users, $request->fd);
var_dump($users);
});
Note: fd is simply the connection ID of the user (maybe short for file descriptor?), so I'm basically trying to get a simple array of all the users that have connected - the problem is though, this value isn't persistant between requests - so when the 2nd user connects, the array becomes empty again
Does anyone know of a way to fix this? I know I could use a database, but that seems very wasteful/inefficient when I want to create a realtime site - so that's why I want to store/access all the data within the php script, if possible...without using any external storage
Not tested but basicly do:
<?php
use Swoole\WebSocket\Server;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use Swoole\Table;
$server = new Server("0.0.0.0", 9502);
$table = new Table(1024);
$table->column('user_id', Swoole\Table::TYPE_STRING, 64);
$table->create();
$server->on("Start", function(Server $server){
echo "Swoole WebSocket Server is started at http://127.0.0.1:9502\n";
});
$server->on('Open', function(Server $server, Swoole\Http\Request $request) use ($table) {
$table->set($request->fd,['user_id'=>$request->fd]);
var_dump($table->count());
});
Read more about the possibilities here: openswoole.com/docs/modules/swoole-table
I'm trying to write gameserver based on Workerman.
Main idea is: list of workers accept messages from clients and puts them to the queue (RabbitMQ), another group of workers get messages from queue, do some calculation and update GameWorld instance accordingly.
GameWorld instance itself is created on start of the main process, workers are created after creating of the GameWorld object. So, I wrote dummy class Server:
namespace Server;
use \Workerman\Worker;
use \Workerman\Lib\Timer;
class Server {
private $name;
public function setName(string $name){
$this->name = $name;
}
public function printName(){
echo $this->name;
}
}
Also I wrote two simple workers just for testing concept of updating object from different workers.
First (start_worker1.php):
use \Workerman\Worker;
use \Workerman\Lib\Timer;
use \Server\Server;
global $ws_worker;
$ws_worker = new Worker('Websocket://0.0.0.0:8000');
$ws_worker->name = 'FirstWorker';
$ws_worker->onWorkerStart = function($ws_worker)
{
$ws_worker->server = new Server();
$ws_worker->server->setName("FirstName");
echo "worker1 started\n";
$ws_worker->is_started = TRUE;
var_dump($ws_worker);
};
Here I created new Server object and give it a name, also changed property $ws_worker->is_started to TRUE (default value is False).
Second(start_worker2.php):
use \Workerman\Worker;
use \Workerman\Lib\Timer;
global $ws_worker;
$worker = new Worker('Websocket://0.0.0.0:8001');
$worker->name = 'SecondWorker';
// here I'm checking, if I had an object of the first worker
// and actually it outputs all the data about first worker object,
// but $ws_worker->server is NULL and $ws_worker->is_started = FALSE
// This is confusing me so much..
var_dump($ws_worker);
// here I'm trying to detect when first worker is started
// but it return false all the time..
while(!$ws_worker->is_started){
var_dump($ws_worker->is_started);
sleep(1);
}
$worker->onWorkerStart = function() use($ws_worker){
echo "worker2 started\n";
var_dump($ws_worker);
$ws_worker->sever->setName("NewName");
};
Here I created second worker and tried to access server property of the first worker object, but with no success..
All this stuff is started this way:
use Workerman\Worker;
use Server\Server;
if(strpos(strtolower(PHP_OS), 'win') === 0)
{
exit("start.php does not support windows, please use start_for_win.bat\n");
}
if(!extension_loaded('pcntl'))
{
exit("Error! <pcntl> extension not found! Please install pcntl extension.");
}
if(!extension_loaded('posix'))
{
exit("Error! <posix> extension not found! Please install posix extension.");
}
define('GLOBAL_START', 1);
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/start_worker1.php';
require_once __DIR__ . '/start_worker2.php';
// Run all services
Worker::runAll();
I guess that in worker2 I can access an worker1 object before function $ws_worker->onWorkerStart is evaluated, but I have no idea, how to access worker1 object in real time ( I mean - get access to current state of the object).
I'm new to PHP OOP style programming, I should say. So please show me where is my mistake. Detailed explanation greatly appreciated.
Hello Stack Overflow,
I am building a browser-based text only multi-player RPG written in PHP with Ratchet as the backbone.
What I have so far: It works very well. I have implemented a simple and effective command interpretor that does a good job of transferring data between the client and server. I'm able to easily perform database operations and instantiate outside classes inside my Server class to use to pass information back to the client.
Where I've gotten stuck: For some reason, my brain broke trying to implement ticks, which in the context of my game, is a set of events that happens every 45 seconds. It's basically the heartbeat of the game, and I can't move forward without having a reliable and graceful implementation of it. The tick needs to do a multitude of things, including (but not limited to): sending messages to players, updating player regen, memory handling, and so on. Generally, all these actions can be coded and placed in an Update class.
But I can't figure out how to get the tick to actually happen. The tick itself, just a function that occurs every 45 seconds inside my react loop, it should start when the server starts. It absolutely needs to be server-side. I could technically implement it client-side and sync with values in a database but I do NOT want to go down that road.
I feel like this should be easier than my brain is making it.
What I've tried:
I've tried running a simple recursive function that constructs my update class on a timer using sleep(45), but again, this needs to start when the server starts, and if I toss an infinite looping function in the construct of my server class, the startup script never gets passed that and the game never starts.
I've tried using the onPeriodicTimer function that comes with react, but I can't figure out how to implement it..
I've tried something crazy like using node js to send a message to my server every 45 seconds and my interpreter catches that particular message and starts the tick process. This is the closest I've gotten to a successful implementation but I'm really hoping to be able to do it without a client having to connect and talk to the server, it seems hackey.
I've tried ZeroMQ to achieve the same goal as above (a client that sends a message to my server that triggers the update) but again, I don't want to have to have a client listener constantly connected for the game to run, and also, zeroMQ is a lot to deal with for something so small.. I had no luck with it.
There has to be a better way to achieve this. Any help would be appreciated.
For reference, here is a basic outline of out my socket application is working. To start, I used the "Hello World" tutorial on the Ratchet website.
So I have a startup.php script that I run to initialize the Server class, which accepts messages from connected clients. onMessage, an interpretor class is instantiated which parses the message out and looks for the command the client passed in a database table which loads the corresponding Class and Method for that command, that data is based back to the onMessage function, the class and method for the command is called, and the result is passed back to the client.
TLDR: How do I add a repeating function to a Ratchet websocket server that can send messages to connected clients every 45 seconds?
Here's the Server class:
class Server implements MessageComponentInterface
{
public $clients;
public function __construct()
{
$this->clients = new \SplObjectStorage;
//exec("nodejs ../bin/java.js", $output);
}
public function onOpen(ConnectionInterface $conn)
{
$conn->connected_state = 0;
$this->clients->attach($conn);
// Initiate login
$login = new Login('CONN_GETNAME');
if($login->success)
{
$conn->send($login->output);
$conn->connected_state = $login->new_state;
$conn->chData = new Character();
}
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg)
{
if($msg == 'do_tick')
{
echo "a tick happened <br>";
}
else
{
if($from->connected_state == 'CONN_CONNECTED' || $msg == 'chardump')
{
$interpretor = new Interpret($msg);
if($interpretor->success)
{
$action_class_var = $interpretor->class;
$action_method_var = $interpretor->function;
$action_class = new $action_class_var($this->clients, $from, $interpretor->msg);
$action = $action_class->{$action_method_var}();
foreach($this->clients as $client)
{
if($action->to_room)
{
if($from != $client)
{
$client->send($action->to_room);
}
}
if($action->to_global)
{
if($from != $client)
{
$client->send($action->to_global);
}
}
if($action->to_char)
{
$client->send($action->to_char);
}
}
}
else
{
$from->send('Huh?');
}
}
else
{
$login = new Login($from->connected_state, $msg, $from);
$from->connected_state = $login->new_state;
if($login->char_data && count($login->char_data)>0)
{
foreach($login->char_data as $key=>$val)
{
$from->chData->{$key} = $val;
}
}
$from->send($login->output);
}
}
}
public function onClose(ConnectionInterface $conn) {
$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();
}
Perhaps an onTick function added to this class that gets called every X seconds? Is that possible?
To broadcast the message to everyone in intervals of 45 seconds (or any other number), you must control the event loop which Ratchet uses.
You need to add a timed event, various vendors call this timed event, timer event, repeatable event, but it always behaves the same - a function fires after X amount of time.
Class that you are after is documented at this link
Alternatively, you can use icicle instead of Ratchet. I personally prefer it, I don't have any particular reason for the preference - both libraries are excellent in my opinion, and it's always nice to have an alternative.
Interestingly enough, you tried to use ZeroMQ - it's a transport layer and it's definitely one of the best libraries / projects I've ever used. It plays nicely with event loops, it's definitely interesting for developing distributed systems, job queues and similar.
Good luck with your game! If you'll have any other questions regarding WS, scaling to multiple machines or similar - feel free to ping me in the comments below this answer.
Thank you, N.B.!
For anyone that might be stuck in a similar situation, I hope this helps someone out. I had trouble even figuring out what terms I should be googling to get to the bottom of my problem, and as evidenced by the comments below my original question, I got flack for not being "specific" enough. Sometimes it's hard to ask a question if you're not entirely sure what you're looking for!
Here is what the game's startup script looks like now, with an implemented "tick" loop that I've tested.
<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use React\Socket\Server as Reactor;
use React\EventLoop\Factory as LoopFactory;;
require dirname(__DIR__) . '/vendor/autoload.php';
foreach(new DirectoryIterator(dirname(__DIR__) .'/src/') as $fileInfo)
{
if($fileInfo->isDot() || $fileInfo->isDir())
{
continue;
}
require_once(dirname(__DIR__) . '/src/' . $fileInfo->getFilename());
}
$clients = null;
class Server implements MessageComponentInterface
{
public function __construct(React\EventLoop\LoopInterface $loop)
{
global $clients;
$clients = new \SplObjectStorage;
// Breathe life into the game
$loop->addPeriodicTimer(40, function()
{
$this->doTick();
});
}
public function onOpen(ConnectionInterface $ch)
{
global $clients;
$clients->attach($ch);
$controller = new Controller($ch);
$controller->login();
}
public function onMessage(ConnectionInterface $ch, $args)
{
$controller = new Controller($ch, $args);
if($controller->isLoggedIn())
{
$controller->interpret();
}
else
{
$controller->login();
}
}
public function onClose(ConnectionInterface $conn)
{
global $clients;
$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();
}
public function doTick()
{
global $clients;
$update = new Update($clients);
}
}
$loop = LoopFactory::create();
$socket = new Reactor($loop);
$socket->listen(9000, 'xx.xx.xx.xxx');
$server = new IoServer(new HttpServer(new WsServer(new Server($loop))), $socket, $loop);
$server->run();
I've been using Chat-API (https://github.com/WHAnonymous/Chat-API) since it was WhatsAPI, and yet I don't know for sure how to receive messages properly.
Right now, I have a cron file that runs once every minute with this basic structure:
$wa = new WhatsProt($WA_NUMBER, $WA_NICKNAME);
$wa->connect();
$wa->loginWithPassword($WA_PASSWORD);
$wa->pollMessage();
$data = $wa->getMessages();
foreach ($data as $item) {
$from_number = $item->getAttribute("from");
$from_nickname = $item->getAttribute("notify");
if ($item->getAttribute("type") == "text") {
$msg = $item->getChild("body")->getData();
} else {
$msg = $item->getChild("media")->getAttribute("url");
}
...
}
$wa->disconnect();
I've also tried running a PHP script constantly in the background like this:
while (true) {
$wa->pollMessage();
$data = $wa->getMessages();
...
}
The first option is more reliable than the second one, but neither is the right solution.
Is there any way making use of sockets to connect to Whatsapp servers as a phone would do? I mean, open a socket and keep it open, triggering a function every time a new message is received (using XMPP protocol).
I am trying to create an FTP connection in PHP and trying to access it from other PHP files. Basically my goal is to create an FTP connection and make it available like a session when logging in and then access it from my other PHP files to do other tasks like file upload-download.
My connection class is as follows,
<?php
if($_GET['q'] == 'ftp_connect')
{
$connection = new connect_to_ftp;
echo $connection->ftp_connection();
}
elseif($_GET['q'] == 'get_connection_id')
{
$connection = new connect_to_ftp;
echo $connection->get_connection();
}
class connect_to_ftp
{
public $ftp_server = "";
public $username = "";
public $password = "";
public $connectionID, $login_result, $response;
public function ftp_connection()
{
$this->connectionID = ftp_connect($this->ftp_server);
if($this->connectionID==true)
{
$this->response = array("connection_error" => "Connected to ".$this->ftp_server);
$this->login_result = ftp_login($this->connectionID, $this->username, $this->password);
if($this->login_result==true)
{
$this->response = array("connection_error" => "Login successful to ".$this->ftp_server);
}else
{
$this->response = array("connection_error" => "Failed to login to ".$this->ftp_server." Check username and password");
}
}else
{
$this->response = array("connection_error" => "Could not connect to ".$this->ftp_server);
}
return json_encode($this->response);
}
public function get_connection()
{
return $this->connectionID;
}
}
?>
When I call ftp_connection() using ajax, it successfully connects to the ftp account but later on when I call get_connection() to return me the connection id, it returns me null instead.
Short answer: You can't
Longer answer: file handles, including sockets only exist within the process which opens them (this is not strictly true, it is possible to pass file handles between processes on Linux, but it is very contentious functionality and as with most esoteric things should not be used unless you fully understand the ramifications, and the functionality is not exposed in php).
The options are to either
1) create a connection, carry out the necessary operations then close it within the instance of a php script. Unfortunately FTP is even more badly thought than SMTP but nearly as deeply ingrained. So lots of people have invented built-in security "features" to try to address it's shortcomings. A common one is rate limiting of connections.
2) run an event based daemon to act as the FTP client and connect to that using php. However if you don't understand files and processes then you have a lot of learning to do before you can write such a programming.
Did I mention that FTP sucks? Learn how to use SFTP.