...

View Full Version : Asynchronous GET/POST Requests (PHP5)



vroom
09-10-2012, 12:26 AM
The following PHP5 snippet defines an AsyncTCP class that will allow you to open and manage multiple requests to remote servers simultaneously.

While I obviously didn't invent the concept I have wrapped what I've seen within a class, added the ability to use either GET or POST, and created a very simple test script to show how it works (to follow).


<?php

// ##########################################################################
// Name: AsyncTCP.php
// Date: 09-Sep-2012
// Prog: gvroom [at] gmail.com (http://hackedcpu.com/)
//
// Desc: Provide asynchronous TCP/IP communication. Script will continue to
// run and allow retrieval of message (if desired) after some or all of
// the base functionality has completed.
//
// Note: This is not expected to be used (advanced) in a tight loop. Caller
// is expected to use sleep() or microsleep() to release CPU.
//
// Source: http://www.codingforums.com/showthread.php?t=272694
// ##########################################################################

class AsyncTCP
{
// Connection parameters
// ---------------------
private $transport;
private $host;
private $port;
private $uri;
private $postdata;
private $timeout;

// Connected socket details
// ------------------------
private $s;
private $status;
private $error;
private $written;
private $data;

// ***********************************************************************
// The timeout variable is used for connections only. Socket timeouts
// are defined in the advance function.
// ***********************************************************************
public function __construct($transport,$host,$port,$uri,$postdata='',$timeout=7)
{
$this->transport = $transport; // tcp, ssl
$this->host = $host; // domain.com, www.domain.com
$this->port = $port; // 80, 8080, 443
$this->uri = $uri; // /xmlapi.php /x.php?token=abc
$this->postdata = $postdata; // properly encoded
$this->timeout = $timeout; // 5, 20

$this->s = null; // no connection yet
$this->status = 'ready'; // no status yet
$this->error = 0; // no errors yet
$this->written = 0; // not written yet
$this->data = ''; // accumulated response
}

// ***********************************************************************
// Simple status checks
// ***********************************************************************
public function isReady() { return ( $this->status == 'ready'); }
public function isDone() { return ( $this->status == 'done' || $this->error > 0); }
public function isWritten() { return ( $this->written > 0 ); }
public function wasError() { return ( $this->error > 0 ); }
public function getStatus() { return ( $this->status ); }
public function getData() { return ( $this->data); }

// ***********************************************************************
// Connect to the remote server. Unfortunately, based on the time taken
// to connect this appears not be an asynchronous event... though the read
// and write management certainly is.
// ***********************************************************************
public function connect()
{
$errno = 0;
$errstr = '';
$this->s = stream_socket_client($this->transport."://".$this->host.":".$this->port
,$errno
,$errstr
,$this->timeout
,STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);

$this->status = 'connected';
if ( $errno )
{
$this->error = $errno;
$this->status = 'connect failed: $errrstr ($errno)';
}

return $this->error;
}

// ***********************************************************************
// Manage the connection. Basically, advance through a series of actions
// to send a request and get the response.
// ***********************************************************************
public function advance()
{
static $sockets = array();
static $seconds = 0;
static $usec = 100;
static $readlen = 8192;

$sockets['id'] = $this->s;
$read = $sockets;
$write = $sockets;
$except = null;

$n = stream_select($read, $write, $except, $seconds, $usec);

if ( $n )
{
// Perform available read operations
// ---------------------------------
foreach ($read as $r)
{
$chunk = fread($r, $readlen);
if ( $chunk === FALSE )
$this->status = 'connect or read error';
else
{
$this->data .= $chunk;
$this->status = 'reading';
}

if ( strlen($chunk) < $readlen )
$this->status = 'done';
}

// Perform pending write operations
// --------------------------------
foreach ($write as $w)
{
if ( !$this->written )
{
// Adjust for GET or POST requests
// -------------------------------
$method = 'GET';
$host = $this->host;
$port = $this->port;
$uri = $this->uri;
$headers = "Host: $host\r\n";

if ( strlen($this->postdata) )
{
$method = 'POST';
$length = strlen($this->postdata);
$headers .= "Content-type: application/x-www-form-urlencoded\r\n";
$headers .= "Content-length: $length\r\n";
}
$headers .= "\r\n";

// Build the message bundle
// ------------------------
$msg = "$method $uri HTTP/1.0\r\n";
$msg .= $headers;
$msg .= $this->postdata;

fwrite($w,$msg);
$this->status = 'sent; waiting for response';
$this->written = 1;
}
}

} // if ( n )

return $this->status;

} // function advance()

} // class AsyncTCP

vroom
09-10-2012, 12:29 AM
Here is the test script with sample output to follow...


<?php

// ####################################
// ### TEST TCP AND SSL CONNECTIONS ###
// ####################################

$atime = microtime(TRUE);

echo "initialize object data:<br>\n";
$elapsed = sprintf("%0.7f",microtime(TRUE) - $atime);
echo "... elapsed: $elapsed<br>\n";
echo "<br>\n";

$c1 = new AsyncTCP('tcp','www.google.com','80','/');
$elapsed = sprintf("%0.7f",microtime(TRUE) - $atime);
echo "c1 " . $c1->getStatus() . "<br>\n... elapsed: $elapsed<br>\n";

$c2 = new AsyncTCP('ssl','gmail.google.com','443','/');
$elapsed = sprintf("%0.7f",microtime(TRUE) - $atime);
echo "c2 " . $c2->getStatus() . "<br>\n... elapsed: $elapsed<br>\n";

echo "<br>\n";
for ( ;; )
{
if ( $c1->isReady() )
{
echo "... issueing c1 connect ";
$c1->connect();
$elapsed = sprintf("%0.7f",microtime(TRUE) - $atime);
echo "... c1 " . $c1->getStatus() . " at elapsed $elapsed<br>\n<br>\n";
}

if ( $c2->isReady() )
{
echo "... issueing c2 connect ";
$c2->connect();
$elapsed = sprintf("%0.7f",microtime(TRUE) - $atime);
echo "... c2 " . $c2->getStatus() . " at elapsed $elapsed<br>\n<br>\n";
}

if ( !$c1->isDone() )
$c1->advance();
if ( !$c2->isDone() )
$c2->advance();

$elapsed = sprintf("%0.7f",microtime(TRUE) - $atime);
echo "elapsed: $elapsed<br>\n";
echo "... c1: " . $c1->getStatus() . "<br>\n";
echo "... c2: " . $c2->getStatus() . "<br>\n";

if ( $c1->isDone() && $c2->isDone() )
break;

usleep(5000);
}

echo "<br>\n";
echo "c1:<br>\n" . htmlentities($c1->getData()) . "<br><br>\n";
echo "c2:<br>\n" . htmlentities($c2->getData()) . "<br><br>\n";

vroom
09-10-2012, 12:34 AM
This is a run against two Google servers (both GET requests one via SSL). Notice how both connections "advance" to different status values at different times. There may be a bug in the read logic as the first response may be cut off (I'll edit the original post if/when I find it).


initialize object data:
... elapsed: 0.0000050

c1 ready
... elapsed: 0.0000379
c2 ready
... elapsed: 0.0000510

... issueing c1 connect ... c1 connected at elapsed 0.0026510

... issueing c2 connect ... c2 connected at elapsed 0.0182369

elapsed: 0.0183511
... c1: sent; waiting for response
... c2: sent; waiting for response
elapsed: 0.0234740
... c1: sent; waiting for response
... c2: sent; waiting for response
elapsed: 0.0285921
... c1: sent; waiting for response
... c2: sent; waiting for response
elapsed: 0.0337579
... c1: sent; waiting for response
... c2: done
elapsed: 0.0388761
... c1: sent; waiting for response
... c2: done
elapsed: 0.0440390
... c1: done
... c2: done

c1:
HTTP/1.0 200 OK Date: Sun, 09 Sep 2012 23:13:26 GMT Expires: -1 Cache-Control: private, max-age=0 Content-Type: text/html; charset=ISO-8859-1 Set-Cookie: PREF=ID=6772...snip...nhdt; expires=Tue, 09-Sep-2014 23:13:26 GMT; path=/; domain=.google.com Set-Cookie: NID=63=T6cC...snip...gZEw6; expires=Mon, 11-Mar-2013 23:13:26 GMT; path=/; domain=.google.com; HttpOnly P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info." Server: gws X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN <!doctype html><html itemscope="itemscope" itemtype="http://schema.org/WebPage"><head><meta content="Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for." name="description"><meta content="noodp" name="robots"><meta itemprop="image" content="/images/google_favicon_128.png"><title>Google</title><script>window.google={kEI:"liJNULy3OrK16AHIzYDQAw",getEI:function(a){var b;while(a&&!(a.getAttribute&&(b=a.getAttribute("eid"))))a=a.parentNode;return b||google.kEI},https:function(){return window.location.protocol=="https:"},kEXPI:"17259,23628,23670,30316,32690,35704,38034,39523,39976,4000116,4000229,4000260,4000308,4000352,400035 4,4000472,4000476,4000517,4000545,4000553,4000562,4000593,4000616,4000849",kCSI:{e:"17259,23628,23670,30316,32690,35704,38034,39523,39976,4000116,4000229,4000260,4000308,4000352,400035 4,4000472,4000476,4000517,4000545,4000553,4000562,4000593,4000616,4000849",ei:"liJNULy3OrK16AHIzYDQAw"},authuser:0, ml:function(){},kHL:"en",time:function(){return(new Date).getTime()},log:function(a,b,c,e){var d=new Image,h=google,i=h.lc,f=h.li,j="";d.onerror=(d.onload=(d.onabort=function(){delete i[f]}));i[f]=d;if(!c&&b.search("&ei=")==-1)j="&ei="+google.getEI(e);var g=c||"/gen_204?atyp=i&ct="+a+"&cad="+b+j+"&zx="+google.time(); var k=/^http:/i;if(k.test(g)&&google.https()){google.ml(new Error("GLMM"),false,{src:g});delete i[f];return}d.src=g;h.li=f+1},lc:[],li:0,Toolbelt:{},y:{},x:function(a,b){google.y[a.id]=[a,b];return false}};window.google.sn="webhp";window.google.timers={};window.google.startTick=function(a,b){window.google.timers[a]={t:{start:(new Date).getTime()},bfr:!(!b)}};window.google.tick=function(a,b,c){if(!window.google.timers[a])google.startTick(a);window.google.timers[a].t[b]=c||(new Date).getTime()};google.startTick("load",true);try{}catch(u){} var _gjwl=location;function _gjuc(){var e=_gjwl.href.indexOf("#");if(e>=0){var a=_gjwl.href.substring(e);if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var f=a.substring(d,b);if(f.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(f=="cad=h")return 0;c=b}_gjwl.href="/search?"+a+"&cad=h";return 1}}}return 0}function _gjp(){!(window._gjwl.hash&& window._gjuc())&&setTimeout(_gjp,500)}; window._gjp&&_gjp();</script><style>#gb{font:13px/27px Arial,sans-serif;height:30px}#gbz,#gbg{position:absolute;white-space:nowrap;top:0;height:30px;z-index:1000}#gbz{left:0;padding-left:4px}#gbg{right:0;padding-right:5px}#gbs{background:transparent;position:absolute;top:-999px;visibility:hidden;z-index:998}.gbto #gbs{background:#fff}#gbx3,#gbx4{background-color:#2d2d2d;background-image:none;_background-image:none;background-position:0 -138px;background-repeat:repeat-x;border-bottom:1px solid #000;font-size:24px;height:29px;_height:30px;opacity:1;filter:alpha(opacity=100);position:absolute;top:0;width :100%;z-index:990}#gbx3{left:0}#gbx4{right:0}#gbb{position:relative}#gbbw{right:0;left:0;position:absolute;t op:30px;width:100%}.gbtcb{position:absolute;visibility:hidden}#gbz .gbtcb{right:0}#gbg .gbtcb{left:0}.gbxx{display:none !important}.gbxo{opacity:0 !important;filter:alpha(opacity=0) !important}.gbm{position:absolute;z-index:999;top:-999px;visibility:hidden;text-align:left;border:1px solid #bebebe;background:#fff;-moz-box-shadow:-1px 1px 1px rgba(0,0,0,.2)

c2:
HTTP/1.0 200 OK Cache-Control: private, max-age=604800 Expires: Sun, 09 Sep 2012 23:13:26 GMT Date: Sun, 09 Sep 2012 23:13:26 GMT Refresh: 0;URL=https://gmail.google.com/mail/ Content-Type: text/html; charset=ISO-8859-1 X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block Content-Length: 236 Server: GSE <html><head><meta http-equiv="Refresh" content="0;URL=https://gmail.google.com/mail/" /></head><body><script type="text/javascript" language="javascript"><!-- location.replace("https://gmail.google.com/mail/") --></script></body></html>

vroom
09-10-2012, 12:36 AM
Finally, a shorter test against my own server (POST) and google (SSL)...


initialize object data:
... elapsed: 0.0000050

c1 ready
... elapsed: 0.0000410
c2 ready
... elapsed: 0.0000570

... issueing c1 connect ... c1 connected at elapsed 0.0012650

... issueing c2 connect ... c2 connected at elapsed 0.0163491

elapsed: 0.0165250
... c1: sent; waiting for response
... c2: sent; waiting for response
elapsed: 0.0229831
... c1: sent; waiting for response
... c2: sent; waiting for response
elapsed: 0.0282569
... c1: done
... c2: sent; waiting for response
elapsed: 0.0334899
... c1: done
... c2: done

c1:
HTTP/1.1 200 OK Server: nginx/1.1.19 Date: Sun, 09 Sep 2012 23:01:45 GMT Content-Type: text/html; charset=utf-8 Connection: close X-Powered-By: PHP/5.3.10-1ubuntu3.2 Set-Cookie: PHPSESSID=fag2vg36ormvo5eopd86lmu3r6; expires=Sun, 16-Sep-2012 23:01:45 GMT; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache <br> <b>H A C K E D C P U &nbsp; D E M O</b><br> <br> Enter a small amount of English language text (up to 512 bytes):<br> <form method="post" action=""> <textarea name="textin" rows="9" cols="100">this is my input text.</textarea><br> <input type="submit" name="submit" value="Go"><br> </form> Processing...<br> <br> this: 1<br> is: 1<br> my: 1<br> input: 2<br> text: 1<br> <br> 5 tokens found containing 5 unique words<br> <br> <hr> PHP processing took 0.00458884 seconds<br>

c2:
HTTP/1.0 200 OK Cache-Control: private, max-age=604800 Expires: Sun, 09 Sep 2012 23:01:48 GMT Date: Sun, 09 Sep 2012 23:01:48 GMT Refresh: 0;URL=https://gmail.google.com/mail/ Content-Type: text/html; charset=ISO-8859-1 X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block Content-Length: 236 Server: GSE <html><head><meta http-equiv="Refresh" content="0;URL=https://gmail.google.com/mail/" /></head><body><script type="text/javascript" language="javascript"><!-- location.replace("https://gmail.google.com/mail/") --></script></body></html>



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum