PHP Sockets in Realtime - php

Background
A desktop application on a user's computer gets the phone number from the modem and sends it to a PHP script once a phone call is recieved. At the moment, I am able to receive the data/packet on the specified port via PHP. I then have a scraper that connects to 411 databases and returns the address for the specified phone number.
Problem
After I retrieve the phone number in PHP through sockets, how can I automatically update the 411 parser page with the new phone number?
Code
socket_listener.php
set_time_limit(0);
$address = "127.0.0.1";
$port = 10629;
// create socket and bind with listener event
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, $address, $port);
socket_listen($socket);
do {
$accept = socket_accept($socket);
$read = socket_read($accept, 1024) or die("Could not read input\n");
// parse phone number
$phone = substr($read, 19, 15);
$file = fopen("phone_numbers.txt", "a");
fwrite($file, $phone . "\r\n");
fclose($file);
} while (true);
phone_numbers.txt
(425) 555-1212
(123) 456-7890
Current Solution
My current solution is pretty quirky.
modem -> desktop application -> socket_listener.php -> data.txt -> 411_scraper.php
socket_listener.php listens on the port for incoming packets 24/7 and appends new phone numbers it receives to a text file
411_scraper.php checks text file for updates every 5 seconds. If file version is changed, then it reads the last phone number
Run code to query 411.com and retrieve data using phone number
Desired Solution
socket_listener.php listens on port for incoming packet containing phone number
Page automatically updates with new data retrieved from 411
Things I've Been Looking At
I've looked at node.js, Ratchet (www.socketme.com), and Pusher (http://pusher.com/) but they are all above my level of understanding. Pusher and Ratchet seems promising but I have not jumped into them yet.

I would use ajax or something along those lines. PHP isn't a real time thing. When you navigate to a website which uses PHP the code is executed before the page is rendered. It's processing the code on the server computer and giving you back the result as a string which you can send to the requesting client.
You're going to have the hold the data somewhere persistently. Once the php script is done executing the data is for that execution gone. Unless you use sessions.

Related

How to check whether server IP status is up or down in PHP

I am creating a web application where I will be adding 10-20 IP addresses and setting a cron job for every 1 hour, that can be done easily. The problem is that I want to check whether an IP is currently working or not. If the IP is not working then I will get a message by email or some other mode. I have found the below code on the Internet:
$ip= '103.117.231.160';
$port = 80;
$fp = #fsockopen($ip, $port, $errno, $errstr, 2);
if (!$fp) {
echo 'offline';
} else{
echo 'online';
}
I have added many random IPs, but it's giving me online for the status. Is the above script fine or do I need to change the script? How can I test that an IP is working or not? Can anyone provide sample test IPs so I can make sure the script is working fine?
I just need correct output via email that my server is down.

Task progress from a ZeroMQ worker

Fairly new to ZeroMQ. I have a simple REQ/REP queue like below. I am using PHP but that doesn't matter as any language binding would be fine for me.
This is client to request a task
$ctx = new ZMQContext();
$req = new ZMQSocket($ctx, ZMQ::SOCKET_REQ);
$req->connect('tcp://localhost:5454');
$req->send("Export Data as Zip");
echo $i . ":" . $req->recv().PHP_EOL;
And this is a worker to actually perform the task.
$ctx = new ZMQContext();
$srvr = new ZMQSocket($ctx, ZMQ::SOCKET_REP);
$srvr->bind("tcp://*:5454");
echo "Server is started at port $port" . PHP_EOL;
while(true)
{
$msg = $srvr->recv();
echo "Message = " . $msg . PHP_EOL;
// Do the work here, takes 10 min, knows the count of lines added and remaining
$srvr->send($msg . " is exported as zip file" . date('H:i:s'));
}
As the task of exporting data takes about 10 min, I want to connect to the server from a different client and get the progress/ percentage of the task done.
I am wondering if that's even a valid approach.
I tried this approach where REQ/REP part works but I get nothing in PUB/SUB part
Server part
$ctx = new ZMQContext();
$srvr = new ZMQSocket($ctx, ZMQ::SOCKET_REP);
$srvr->bind("tcp://*:5454");
// add PUB socket to publish progress
$c = new ZMQContext();
$p = new ZMQSocket($c, ZMQ::SOCKET_PUB);
$p->bind("tcp://*:5460");
echo "Server is started at port 5454" . PHP_EOL;
$prog = 0;
while(true)
{
$p->send($prog++ . '%'); // this part doesn't get to the progress client
$msg = $srvr->recv();
echo "Message = " . $msg . PHP_EOL;
sleep(2);// some long task
$srvr->send($msg . " Done zipping " . date('H:i:s'));
}
Progress client
$ctx = new ZMQContext();
$stat = new ZMQSocket($ctx, ZMQ::SOCKET_SUB);
$stat->connect('tcp://localhost:5460');
while (true){
echo $stat->recv() . PHP_EOL; //nothing shows here
}
Request client
$ctx = new ZMQContext();
$req = new ZMQSocket($ctx, ZMQ::SOCKET_REQ);
$req->connect('tcp://localhost:5454');
for($i=0;$i<100;$i++){
$req->send("$i : Zip the file please");
echo $i . ":" . $req->recv().PHP_EOL; //works and get the output
}
The concept is feasible, some tuning needed:
All PUB counterparties have to setup any non-default subscription, via, at least an empty subscription .setsockopt( ZMQ_SUBSCRIBE, "" ) meaning receive all TOPICs ( none "filter"-ed out ).
Next, both PUB-side and SUB sides ought get .setsockopt( ZMQ_CONFLATE, 1 ) configured, as there is of no value to populate and feed all intermediate values into the en-queue/de-queue pipeline, once the only value is in the "last", most recent message.
Always, the non-blocking mode of the ZeroMQ calls ought be preferred ( .recv( ..., flags = ZMQ_NOBLOCK ) et al ) or the Poller.poll() pre-tests ought be used to sniff first for a (non)-presence of a message, before spending more efforts on reading its context "from" ZeroMQ context-manager. Simply put, there are not many cases, where blocking-mode service calls may serve well in a production-grade system.
Also some further tweaking may help the PUB side, in case a more massive "attack" comes from the un-restricted pool of SUB-side entities and PUB has to create / manage / maintain resources for each of these ( unrestricted ) counterparties.
You need only use PUB/SUB if there is more than one client wanting to receive the same progress updates. Just use PUSH/PULL for a simple, point to point transfer that works over tcp.
Philosophical Discussion
With problems such as this to solve there's two approaches.
Use additional sockets to convey additional message types,
Use just two sockets, but convey more than one message type through them
You're talking about doing 1). It might be worth contemplating 2), though I must emphasise that I know next to nothing of PHP and so don't know if there are language features that encourage one to have separate request and progress clients.
If you do, your original client needs a loop (after it has sent the request) to receive multiple messages, either progress update messages or the final result. Your server, whilst it is doing its 10 minute lookup, will send regular progress update messages, and the final result message at the end. You would probably use PUSH/PULL client to server, and the same again for the progress / result from the server back to the client.
It is architecturally more flexible to follow 2). Once you have a means of sending two or more message types through a single socket and of decoding them at the receiving end, you can send more. For example, you could decide to add a 'cancel' message from the client to the server, or a partial results message from the server back to the client. This is much easier to extend than to keep adding more sockets to your architecture simply because you want to add another message flow between the client and server. Again, I don't know enough about PHP to say that this would definitely be the right way of doing it in that language. It certainly makes a lot of sense in C, C++.
I find things like Google Protocol Buffers (I prefer ASN.1) very useful for this kind of thing. These allow you to define the types of messages you want to send, and (at least with GPB), combine them together inside a single 'oneof' (in ASN.1 one uses tagging to tell different messages apart). GPB and ASN.1 are handy because then you can use different languages, OSes and platforms in your system without really having to worry about what it is being sent. And being binary (not text) they're more efficient across network connections.

iOS application push notifications

We have an application built in PHP that sends out push notifications to both Android and iOS. Our problem is there are some device ids for iOS that seem to completely halt the sending of the all other iOS push notifications in our script, they even say that they have been sent without errors but it stops all notifications in the loop afterwards.
If we then remove the offending device id from our database the sending script works for all devices after the offending device. It is very strange and cant seem to figure out why.
Does anyone have any experience with this? Is it to do with sending to a device ID that doesnt exist anymore will stop apple from completing our script on that specific connection?
Here is our sending script in PHP (this works for all devices apart from the odd rogue device id):
$tHost = 'gateway.push.apple.com';
$tPort = 2195;
$tCert = "path_to_our_cert.pem";
$tPassphrase = 'passphrase';
$tAlert = $this->title; //assign a title
$tBadge = 8;
$tSound = 'default';
$tPayload = $this->body_plain; //assign a body
// Create the message content that is to be sent to the device.
$tBody['aps'] = array ('alert' => $tAlert,'badge' => $tBadge,'sound' => $tSound,);
$tBody ['payload'] = $tPayload;
$tBody = json_encode ($tBody);
$tContext = stream_context_create ();
stream_context_set_option ($tContext, 'ssl', 'local_cert', $tCert);
stream_context_set_option ($tContext, 'ssl', 'passphrase', $tPassphrase);
$tSocket = stream_socket_client ('ssl://'.$tHost.':'.$tPort, $error, $errstr, 30, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $tContext);
if (!$tSocket) exit ("APNS Connection Failed: $error $errstr" . PHP_EOL);
//Loop through all devices to send
foreach($this->device->devices as $item){
if($item->os != "iOS") continue;
if(session::getAdministratorStaffSchoolID() != $item->school_id) continue;
$tToken = $item->device_id;//assign the device id
$tMsg = chr (0) . chr (0) . chr (32) . pack ('H*', $tToken) . pack ('n', strlen ($tBody)) . $tBody;
$tResult = fwrite ($tSocket, $tMsg, strlen ($tMsg));
}
fclose ($tSocket);
Does anyone have any ideas regarding this?
Many thanks
So, just a thought, but you're using the legacy format to send the notification:
$tMsg = chr (0) . chr (0) . chr (32) . pack ('H*', $tToken) . pack ('n', strlen ($tBody)) . $tBody;
And:
Error response. With the legacy format, if you send a notification
packet that is malformed in some way—for example, the payload exceeds
the stipulated limit—APNs responds by severing the connection. It
gives no indication why it rejected the notification. The enhanced
format lets a provider tag a notification with an arbitrary
identifier. If there is an error, APNs returns a packet that
associates an error code with the identifier. This response enables
the provider to locate and correct the malformed notification.
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Appendixes/LegacyFormat.html
So maybe APNS is just dropping the connection on you? That's why all the remaining notifications actually don't get through. Take a closer look at those payloads. And might be a good time to move to the new format.
AFAIK APNs will close the connection as soon as any error occurs (invalid device token, malformed payload, etc.). Your code that sends out Push needs to be prepared to recover from this situation, reconnect and then restart the work.
Take a look at the 3rdParty libraries out there to execute this job for you. You'll rely on the community work.
I can referral Pushy which is written in Java. I have used it in 2 different projects and I can say it works fine and it's an very active project (a lot of discussions and updates there).
I don't know any of them using PHP, but probably there is one

SMPP connection

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);
}
});

PHP - echo do other client

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).

Categories