CodingForums.com

CodingForums.com (http://www.codingforums.com/index.php)
-   Post a PHP snippet (http://www.codingforums.com/forumdisplay.php?f=41)
-   -   Asynchronous GET/POST Requests (PHP5) (http://www.codingforums.com/showthread.php?t=272694)

vroom 09-10-2012 12:26 AM

Asynchronous GET/POST Requests (PHP5)
 
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 Code:

<?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 ); }
   public function 
wasError()  { return ( $this->error ); }
   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->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 Code:

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

Quote:

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/accoun...&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,400 0116,4000229,4000260,4000308,4000352,4000354,4000472,4000476,4000517,4000545,4000553,4000562,4000593 ,4000616,4000849",kCSI:{e:"17259,23628,23670,30316,32690,35704,38034,39523,39976,4000116,4000229,400 0260,4000308,4000352,4000354,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){windo w.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)...

Quote:

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>


All times are GMT +1. The time now is 08:45 PM.

Powered by vBulletin®
Copyright ©2000 - 2013, Jelsoft Enterprises Ltd.