Please consider the following Ratchet PHP WS server creation. It listens for WS client connections as well as messages from ZMQ. Upon reception of ZMQ messages, it forwards the info to some client WS connections based on business logic behind.
I only use an insecure websocket here as I use Apache to reverse-proxy for SSL.
The problem is that it works perfectly when accessed from a browser, but when accessed by a command line WS client, every once in a while I get the following warning, at which point the socket closes:
It didn't happen until I added the Origin checking mechanism. Should I stick to Ratchet's origin checking, or use some other mechanism?
My server creation code (is it even correct?):
// React loop
$loop = React\EventLoop\Factory::create();
$pusher = new MyApp\Api;
// ZMQ
$context = new React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind("tcp://127.0.0.1:7777");
$pull->on('message', array($pusher, 'onZMQ'));
// WS
$webSock_insecure = new React\Socket\Server(
"127.0.0.1:8080",
$loop
);
$wsSrv = new Ratchet\WebSocket\WsServer($pusher);
$wsSrv->enableKeepAlive($loop, 1);
$checkedWsSrv = new Ratchet\Http\OriginCheck($wsSrv, ['localhost']);
$checkedWsSrv->allowedOrigins[] = '';
$checkedWsSrv->allowedOrigins[] = 'mysite.com';
$webServer_insecure = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer($checkedWsSrv),
$webSock_insecure
);
$loop->run();
Related
The situation:
There is a long-running task that needs to be launched asynchronously. The details of this task aren't really all that important (the basics are that multiple VMs are going to be provisioned and a complex network setup), and this is being handled by a python script that will be running on a different server. We decided to go with WebSockets for the communication back and forth between the web server and the client, and I have the bi-directional communication there working. The web server will be sending requests to the other server, and will receive HTTP POST notifications back from the python script when each machine is up and running, and a final HTTP POST back when the entire network is up.
All of this works. The framework we're using on the web server is Laravel 4, and the web socket server was built using Ratchet, and implemented in an artisan command. My issue is I'm not sure how to relay the HTTP POSTs to a Laravel controller from the python script to the WebSocket server so that it in turn can relay that information back to the client.
Below is the implementation of Ratchet's MessageComponentInterface::onMessage method, which is using an Observer pattern to notify any subscribed listeners of a message event.
// Class: SocketMessenger
public function onMessage(ConnectionInterface $from, $msg) {
$respondTo = null;
foreach($this->_clients as $client) {
if($client->resourceId == $from->resourceId) {
$respondTo = $client;
break;
}
}
$msg = json_decode($msg, true);
if(!$respondTo || !$msg || !isset($msg['type'])) {
echo 'Malformed Socket Message Received, Rejecting.';
return;
}
$type = $msg['type'];
// Notify any subscribed listeners with this message type of the
// incoming message. Any response received will be relayed back to
// the client.
if(isset($this->_listeners[$type])) {
foreach( $this->_listeners[$type] as $listener ) {
$response = $listener->notify( $respondTo, $msg );
if($response) {
$respondTo->send( $response );
}
}
}
}
The WebSocket server is constructed within a service provider like this:
// Class: SocketServiceProvider
$app->bind( 'Ratchet\MessageComponentInterface', function( $app, $params ) {
$port = $params && isset($params[0]) ?
$params[0] : self::DEFAULT_PORT
;
$messenger = new SocketMessenger;
// Setup the Ratchet Server.
$server = IoServer::factory(
new HttpServer(
new WsServer(
$messenger
)
),
$port
);
// Subscribe socket listeners to the socket messenger
$messenger->subscribe( new TopologyMessageListener, TopologyMessageListener::TYPE );
return $server;
} );
Is there any way take requests to a Laravel controller and notify the running WebSocket server whenever a given controller's method is hit?
The answer here appears to be that I need to create a WebSocket client in PHP that will send messages to the WebSocket server, the same as a client written in JavaScript would. For any interested viewers struggling with the same issue, I was able to find a few WebSocket Clients written in PHP:
https://github.com/gabrielbull/php-websocket-client
https://github.com/Devristo/phpws
https://github.com/symbiose/php-websocket-client (<-- This is the one I wound up going with)
I'll be leaving this question open for awhile in case anyone comes up with a different idea / better client library to use.
After a user signs up on my website i need to send a soap request in a method that is not blocking to the user. If the soap server is running slow I don't want the end user to have to wait on it. Is there a way I can send the request and let my main PHP application continue to run without waiting from a response from the soap server? If not, is there a way to set a max timeout on the soap request, and handle functionality if the request is greater than a max timeout?
Edit:
I would ideally like to handle this with a max timeout for the request. I have the following:
//ini_set('default_socket_timeout', 1);
$streamOptions = array(
'http'=>array(
'timeout'=>0.01
)
);
$streamContext = stream_context_create($streamOptions);
$wsdl = 'file://' . dirname(__FILE__) . '/Service.wsdl';
try{
if ( file_get_contents( $wsdl ) ) {
$this->_soapClient = new SoapClient($wsdl,
array(
'soap_version' => SOAP_1_2,
'trace' => true,
'stream_context' => $streamContext
)
);
$auth = array('UserName' => $this->_username, 'Password' => $this->_password);
$header = new SoapHeader(self::WEB_SERVICE_URL, "WSUser", $auth);
$this->_soapClient->__setSoapHeaders(array($header));
}//if
}
catch(Exception $e){
echo "we couldnt connect". $e;
}
$this->_soapClient->GetUser();
I set the timeout to 0.01 to try and force the connection to timeout, but the request still seems to fire off. What am I doing wrong here?
I have had the same issues and have implemented solution !
I have implemented
SoapClient::__doRequest();
To allow multiple soap calls using
curl_multi_exec();
Have a look at this asynchronous-soap
Four solutions:
Use AJAX to do the SOAP -> Simplest SOAP example
Use AJAX to call a second PHP file on your server which does the SOAP (best solution imo)
Put the SOAP request to the end of your PHP file(s) (not the deluxe solution)
Use pcntl_fork() and do everything in a second process (I deprecate that, it might not work with every server configuration)
Depending on the way you implement this, PHP has plenty of timeout configurations,
for example socket_set_timeout(), or stream_set_timeout() (http://php.net/manual/en/function.stream-set-timeout.php)
I'm establishing an SMPP connection via PHP using this free library. To receive a message, I'm using the following code, given in the example:
<?php
$GLOBALS['SMPP_ROOT'] = dirname(__FILE__); // assumes this file is in the root
require_once $GLOBALS['SMPP_ROOT'].'/protocol/smppclient.class.php';
require_once $GLOBALS['SMPP_ROOT'].'/transport/tsocket.class.php';
// Construct transport and client
$transport = new TSocket('your.smsc.com',2775);
$transport->setRecvTimeout(60000); // for this example wait up to 60 seconds for data
$smpp = new SmppClient($transport);
// Activate binary hex-output of server interaction
$smpp->debug = true;
// Open the connection
$transport->open();
$smpp->bindReceiver("USERNAME","PASSWORD");
// Read SMS and output
$sms = $smpp->readSMS();
echo "SMS:\n";
var_dump($sms);
// Close connection
$smpp->close();
?>
It works perfectly well, when I run the script in the browser window and send the SMS from my phone within given 60 seconds, but I don't quite understand how to make it work for a long time. I mean, like in real-life situation, when it should run on the background and trigger some events when receiving an SMS. How do I do that? Because now, I need to refresh the page every time to get an SMS, and it only works once. Thanks in advance.
If your solution needs to run within a browser, you shouldn't connect to the SMPP server directly from your script. It would lead to a single user scenario.
You should put a endless loop around the readSMS call and make it a console application which runs as a daemon. Then you write the result of readSMS into a database and read this from your web application. With this you could use html refresh or some fancy ajax querying the database and presenting the incoming sms.
Usually SMPP receiver connections run in blocking mode on the socket (no timeout), because either you receive either a SMS, or an enquire_link (which needs to be answered by a enquire_link_resp - your library does this automatically). Whenever you read a SMS, process it (put it in the database) and call readSMS again - it will block until the next SMS comes in.
You can try this.
<?php
set_time_limit(0);
$GLOBALS['SMPP_ROOT'] = dirname(__FILE__); // assumes this file is in the root
require_once $GLOBALS['SMPP_ROOT'].'/protocol/smppclient.class.php';
require_once $GLOBALS['SMPP_ROOT'].'/transport/tsocket.class.php';
// Construct transport and client
$transport = new TSocket('your.smsc.com',2775);
$transport->setRecvTimeout(60000); // for this example wait up to 60 seconds for data
$smpp = new SmppClient($transport);
// Activate binary hex-output of server interaction
$smpp->debug = true;
// Open the connection
$transport->open();
$smpp->bindReceiver("USERNAME","PASSWORD");
while(1) {
// Read SMS and output
$sms = $smpp->readSMS();
echo "SMS:\n";
var_dump($sms);
}
// Close connection
$smpp->close();
?>
Try to use another library
composer require glushkovds/php-smpp
To receive sms:
<?php
require_once 'vendor/autoload.php';
$service = new \PhpSmpp\Service\Listener(['your.smsc.com'], 'login', 'pass');
$service->listen(function (\PhpSmpp\SMPP\Unit\Sm $sm) {
if ($sm instanceof \PhpSmpp\Pdu\DeliverSm) {
var_dump($sm->message);
}
});
I`m making an little php server that has 2 clients, on connects to the php (device a) and controls the other (device b).
device a makes a get request to the php. now i want to push that command to device b. is there any way to echo to device b? or to make an request to the device b?
(i only need to send one character to device b)
Pushing to device is possible but depends on your device.
A solution would be websockets, see the following links for further reading:
http://www.websocket.org/
http://code.google.com/p/phpwebsocket/
Another solution would be longpolling which is easy to implement in php:
http://en.wikipedia.org/wiki/Push_technology#Long_polling
Very simple implementation of longpolling on server-side:
$ts = time();
while(true) {
// if there's something new, send the response to the server
// if not, continue with the loop
if($response = getSuperAwesomeResponse()) {
print $response;
break;
}
// timeout after 60 seconds
if( ($ts + 60) > time()) {
break;
}
sleep(1);
}
On the client-side you just need to send some sort of ajax calls
No, unless device B is running a server of some kind (any software that accepts incoming connections really). If that's the case then you can easily make HTTP requests to the device (e.g. even with file_get_contents) or have your own custom connection protocol (with sockets). There are also other options that allow you the same functionality but work in slightly different ways.
If the device is not running any servers then the best you can do is poll the server continuously to see if there are any commands for it. This is easier to set up (the server is already there) but also not efficient.
Device B could open a client connection to the server and wait for incomming data. If data comes in, the client running on device B could echo it.
PHP offers access to network sockets, see http://www.php.net/manual/en/book.sockets.php
Some PHP example code making use of LibEvent and ZMQ, which allows a higher level of access to sockets and queues:
Event-driven Server:
<?php
// create base and event
$base = event_base_new();
$event = event_new();
// Allocate a new context
$context = new ZMQContext();
// Create sockets
$rep = $context->getSocket(ZMQ::SOCKET_REP);
// Connect the socket
$rep->bind("tcp://127.0.0.1:5555");
// Get the stream descriptor
$fd = $rep->getsockopt(ZMQ::SOCKOPT_FD);
// Define event callback function
$fnc = function ($fd, $events, $arg) {
static $msgs = 1;
echo "CALLBACK FIRED" . PHP_EOL;
if($arg[0]->getsockopt (ZMQ::SOCKOPT_EVENTS) & ZMQ::POLL_IN) {
echo "Got incoming data" . PHP_EOL;
var_dump ($arg[0]->recv());
$arg[0]->send("Got msg $msgs");
if($msgs++ >= 10) event_base_loopexit($arg[1]);
}
};
// set event flags
event_set($event, $fd, EV_READ | EV_PERSIST, $fnc, array($rep, $base));
// set event base
event_base_set($event, $base);
// enable event
event_add($event);
// start event loop
event_base_loop($base);
ZeroMQ Client:
<?php
// Create new queue object
$queue = new ZMQSocket(new ZMQContext(), ZMQ::SOCKET_REQ, "MySock1");
$queue->connect("tcp://127.0.0.1:5555");
// Assign socket 1 to the queue, send and receive
var_dump($queue->send("hello there!")->recv());
Source, Talk, Video (~22:00).
I'm trying to write a simple web service server and client using nusoap. I think my server works fine because a VB.NET client can call it. However, the PHP client has a very long loading time and does not give any output.
server1.php
<?
require_once("nusoap/lib/nusoap.php");
$ns="localhost/";
$server = new soap_server();
$server->configureWSDL('TaxCalculator', $ns);
$server->wsdl->schemaTargetNamespace = $ns;
$server->register('TaxCalc', array('amount'=>'xsd:string'),array('return'=>'xsd:string'),$ns);
function TaxCalc($amount) {
$tax = $amount * 0.5;
return new soapval('return', 'xsd:string', $tax);
}
$server->service($HTTP_RAW_POST_DATA);
?>
client1.php
<?
require_once('nusoap/lib/nusoap.php');
$client=new soapclient('http://localhost/server1.php?wsdl', 'wsdl');
echo $client->call('TaxCalc', array('amount'=>'15.00'));
?>
I'm using PHP Version 5.2.6 and nusoap version 0.9.5.
I'd say use whatever works.
I have used nusoap.php,v 1.114 and it works for me.
The nuspoap_client call I have is different from what you have above. Yours does not have a paramter for what port to use.
$client = new nusoap_client("http://127.0.0.1:1024/soap/IApp", false,
$proxyhost, $proxyport, $proxyusername, $proxypassword);
The 2nd parameter above says not to use a wsdl file.
I am guessing that maybe your VB client uses the same approach.
Your code looks like it wants to use a local wsdl file.
Alternatively maybe there is a permissions issue where VB is allowed to access the soap port and PHP is not. (doubt it as everything is local)