How to create asynchronous socket server using php? - php

I'm trying to create a Asynchronous socket server to handle socket client, send message to client when it's needs. I use reactphp library to implement it but I can't do correct, my server still is blocked. I'm new in this library, please help.
require 'vendor/autoload.php';
$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$socket->on('connection', function ($conn) {
$conn->pipe($conn);
});
echo "Socket server listening on port 4000.\n";
echo "You can connect to it by running: telnet localhost 4000\n";
$socket->listen(4200);
$loop->run();
// code bottom doesn't run because of blocking socket

$loop->run(); runs the event loop and will never return unless you stop the loop. The loop is your scheduler and invokes your event handlers in case events occur.
If you want to react to events, you have to register event listeners before you run the loop.

Related

Swoole error 9009 message from Server process timer, not a Worker process callback?

I use Swoole as a WebSocket server. Once per second I need to broadcast a message to all connected WS clients.
Naive approach: I set a server timer $server->tick() prior to launching a server:
$this->server->tick(1000, function () {
$message = 'hello';
foreach ($this->server->connections as $fd) {
$this->server->push($fd, $message);
}
});
Got errors:
[2020-05-05 12:23:56 #21985.2] ERROR swServer_tcp_send (ERRNO 9009)
can't send data to the connections in master process
What is the correct way to push WebSocket messages not from a Master, but from a Worker process?

Lost messages in PUSH/PULL pattern ( Ratchet + PHP + ZeroMQ push integration )

I'm creating a chat on my website, a push notifications system, users activity widget ( updating on the fly ) etc.
My website is built on PHP, so I decided to use Ratchet as a websocket server for my tasks. I've installed all required components and I learned the guide on http://socketo.me/docs/push and started to code.
This is inside a ChatMsg( $item ){...} method in the model.php file. It creates a PUSH socket-access-point archetype and sends a message with JSON-data to server via ZeroMQ after inserting a new item in a database:
$context = new ZMQContext();
$socket = $context->getSocket(ZMQ::SOCKET_PUSH, 'my pusher');
$socket->connect("tcp://localhost:5555");
$socket->send(json_encode($sData));
Next is my push-server.php, creating only one PULL socket access-point archetype and waiting for new messages, which will be transferred to the pusher script, broadcasting new notifications, chat messages and other events to clients.
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
$loop = React\EventLoop\Factory::create();
$pusher = new MyApp\Pusher;
// Listen for the web server to make a ZeroMQ push after an ajax request
$context = new React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->setSockOpt(ZMQ::SOCKOPT_HWM, 0);
$pull->bind('tcp://127.0.0.1:5555'); // Binding to 127.0.0.1 means the only client that can connect is itself
$pull->on('error', function ($e) {
$f = fopen('push-server-error.log', "a");
fwrite($f, $e->getMessage()."\n");
fclose($f);
});
$pull->on('message', array($pusher, 'onNewEvent'));
// Set up our WebSocket server for clients wanting real-time updates
$webSock = new React\Socket\Server($loop);
$webSock->listen(8081, '0.0.0.0'); // Binding to 0.0.0.0 means remotes can connect
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\WebSocket\WsServer(
new Ratchet\Wamp\WampServer(
$pusher
)
)
),
$webSock
);
$loop->run();
?>
I successfully started the push-server.php with a monitoring tool Supervisor, I set up an NGINX proxying for WebSocket traffic, set up client side scripts ( autobahn and so on ).
In general, I was going to use it all at production. First hours I modified new chat systems on my website, I tested it and it all worked perfectly.
But I faced the problem a little bit later. Some ZeroMQ messages ( only part of them, maybe 5-10% ) are lost after sending via ZeroMQ PUSH socket. At that, this problem appears when around 300-400 messages are sent since the moment the push-server.php process was started.
I am deeply convinced that this problem is inside the ZeroMQ ( NOT inside JS client side or Pusher script with business logic ) because I tried to modify the "->on(){...}" method in the push-server.php so as to display new messages on terminal ( console ) and lost messages even does not get displayed on console, i.e. the "->on(){...}" method does not catch up them.
ZeroMQ "->send()" method always returns an empty ZeroMQ socket object, when a message was successfully sent or LOST. I checked this simply by sending chat messages on my website and by getting responses ( form submitting realized with AJAX ):
var_dump($socket->send(json_encode($sData)));
What there can be this problem and how to solve it?
Server OS: CentOS 6.9 (Final)
PHP version: 5.6.31
ZMQ extension version: 1.1.3
libzmq version: 4.2.2
I created not persistent ZMQ::Context and ZMQ::Socket and my problem was solved:
$context = new ZMQContext(1, false);
$socket = $context->getSocket(ZMQ::SOCKET_PUSH);

Ratchet Websocket for raspberry pi

I have written a websocket using ratchet. The webserver is nginx and lives on a raspberry pi. The raspberry pi has been connected to the internet with a router through port forwarding. When I access the site at work, all works well. The websocket connects, all the webpages launch. When I try to access the webpage outside of work the webpage works except the websocket. The worst part is I can't even debug it because I need to be outside the local network for the websocket to fail (i.e not at work). At work, I can connect to the server using two IP addresses, the private IP address that is only accessible locally and the public IP address that can be accessed from anywhere. Both IP addresses properly launch the websocket. When I am not at work, I can only access the webserver on the Public IP address and the websocket does not work.
I have been trying to make this work for a day and a half straight now with no success. Does anybody have any suggestions? Even to help me identify the problem?
The websocket code follows the Ratchet Push Server tutorial:
<?php
require '/var/www/html/vendor/autoload.php';
$loop = React\EventLoop\Factory::create();
$pusher = new MyApp\Pusher;
$context = new React\ZMQ\Context($loop);
$pull->bind('tcp://127.0.0.1:5555');
$pull->on('message',array($pusher, 'onBlogEntry'));
$webSock = new React\Socket\Server($loop);
$webSock->listen(443, '0.0.0.0');
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\Websocket\WsServer(
new Ratchet\Wamp\WampServer(
$pusher
)
)
),
$webSock
);
$loop->run();
?>
The client side code is:
var conn = new ab.Session('ws://privateIPAddress:443',
function (){
console.log("Here");
conn.subscribe('client',function(topic,data) {
console.log("hey");
...
});
},
function() {
console.warn('Websocket connection closed');
},
{'skipSubprotocolCheck': true}
);
I suspect the issue is a security setting since both the public and private IP addresses work when I am at the work site.
This is a very late answer to my own question but in case anyone is still puzzling over the same issues. The answer is don’t use ratchet. Use nodejs with socket.io. All your troubles will fly away. Socket.io which also has a java implementation is simply a more developed package for WebSockets.

Sending message to Ratchet WebSocket Server from another script

I'm not 100% sure this is worded right, but I have a Ratchet WebSocket server working correctly as a chat service. However, I want to, when a user posts a new thread on the forums, have the server automatically post a message into the chat to notify them all of this new post.
I want to do this via a quick TCP connection upon the creation of this thread. I'm still somewhat new to sockets and this area of server coding. Is there an easy way that PHP can ignore HTTP overhead in connecting to this same-server socket and simply sending a message?
Here's the code I've tried to use as a test, but ratchet does not even say it received a connection or message (ADDR and port are correct, socket_connect returns TRUE):
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$string = "Hello, a new post has been BLAH";
$Socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$Status = socket_connect($Socket, "***", "***");
$Bytes = socket_write($Socket, $string, strlen($string));
socket_strerror(socket_last_error());
socket_close($Socket);
The solution suggested on the Ratchet site is to use ZeroMQ to have your synchronous php (web server that is doing the database work for the new post) push the message to the Ratchet server.
They have some pretty good docs at http://socketo.me/docs/push

ZeroMQ PHP Timeout

I have a problem with my PHP script communicating through ZeroMQ with a PHP Daemon running in the backend and waiting for messages. If maybe the daemon is down the requesting php waits for an endless time. If i reload the page firefox ends in a endless loop and I have to restart apache2 to kill the running request. Especially in the development when the daemon isn't finished, it is really annoying. Do somebody know how i can set a timeout, or just say, skip sennding the request when daemon is not reachable (then send admin a message, server is down and send an error back)?
I tried something like this:
$context = new ZMQContext(1);
$req = new ZMQSocket($context, ZMQ::SOCKET_REQ);
$req->connect("tcp://localhost:5557");
$read = $write = array();
// Poll socket for a reply, with timeout
$poll = new ZMQPoll();
$poll->add($req, ZMQ::POLL_OUT);
$events = $poll->poll($read, $write, 3000);
$errors = $poll->getLastErrors();
if($errors)
echo "No connection";
else
echo "connection";
... $data = ....
$req->send(json_encode($data));
2nd Question, I use PHP-Daemon from shaneharter, sometimes when the daemon does not start correctly because of errors or I shut it down with CRTL+C zeromq still reserves the address, when I restart the daemons it throws an exception, this address is already in use.
Can I easily destroy all ZeroMQ connections?
You don't need to poll just to sent a simple message. I think a PUSH socket will serve you better. Set a reasonable linger value and this will attempt to send a message to the PULL socket counterpart, whether it's listening or not.
$context = new ZMQContext();
$socket = new ZMQSocket($context, ZMQ::SOCKET_PUSH);
$socket->setSockOpt(ZMQ::SOCKOPT_LINGER, 2000);
$socket->connect("tcp://localhost:5557");
$socket->send($data);

Categories