views:

57

answers:

2

I'm writing a small script to see if certain ports on certain devices are in use or are open. My code is:

for($c=1;$c<=16;$c++){
  echo "<tr><td>Pod " . $c . "</td>";
  for ($d=5000;$d<=5010;$d++){
    $tmp=fsockopen("10.0.0." . $c,$d,$erstr, $errno, 1);
    if($tmp){
      echo "<td class='o'>OPEN</td>";
      fclose($tmp);
    }
    else{
      echo "<td class='u'>IN USE</td>";
    }
  }
    ob_flush();
    flush();
  echo "</tr>\n";
}
echo "</table>";

Obviously this is a lot of connections, and currently it's taking about ten seconds to run. I was wondering if there's any way I can get this to be a little bit faster? Thanks for the help!

+2  A: 

If a given port is not listening/accepting you will suffer all the TCP timeouts on the SYN packet re-transmissions sent out during the three-way handshake. This is the design of the TCP - we can't change that.

One thing I can recommend is switching from streams to the socket functions and trying non-blocking connect - create your 160 sockets, set them to non-blocking, initiate all connections, wait on them in select with a decreasing timeout, flag the ones that return readable. Do that in a loop until you spent, say, a whole second. Now you get a list of open TCP host/port pairs, and a list of likely closed ones, and you spent a fixed amount of time.

Disclaimer: I never do networking in PHP, so this might be totally useless.

Nikolai N Fetissov
This looks like an additional way to save time, so I'm going to try an implement these ideas with the code Mike posted above. Thank you both!
Airjoe
+2  A: 

One way to speed this up tremendously is to get asynchronous. Right now if one of the hosts is slow, it will slow down the entire pipeline because you're doing one right after another. PHP doesn't really have an event-based AIO (select), or even threading. It does, however, have fork in a linux environment. The following example hasn't been tested, but is a general idea for how to do asynchronous IO in php:

 <?php

 $childrenArr = array();
 $childrenLeft = array();

 for($c=1;$c<=16;$c++){
   for ($d=5000;$d<=5010;$d++){
     $pid = pcntl_fork();
     if ($pid == -1) {
         die("Could not fork");
     } else if ($pid) {
         $childrenArr[$pid] = array($c, $d);
         $childrenLeft[$pid] = 1;
     } else {
         $tmp=fsockopen("10.0.0." . $c,$d,$erstr, $errno, 1);
         exit(($tmp) ? 1 : 0);
     }
   }
 }

 $results = array();

 while (count($childrenLeft) > 0) {
      $oldPid = pcntl_waitpid(-1, $status, WNOHANG);
      if (pcntl_wifexited($status )) {
              unset($childrenLeft[$oldPid]);
              list($c, $d) = $childrenArr[$oldPid];
              $results[$c . "_" . $d] = pcntl_wexitstatus($status);
      }
      usleep(100);
 }

 for($c=1;$c<=16;$c++){
   echo "<tr><td>Pod " . $c . "</td>";
   for ($d=5000;$d<=5010;$d++){
     if ($results[$c . "_" . $d]) {
       echo "<td class='o'>OPEN</td>";
     }
     else {
       echo "<td class='u'>IN USE</td>";
     }
   }
   ob_flush();
   flush();
   echo "</tr>\n";
 }
 echo "</table>";
Mike Axiak
This runs much faster! I've found a couple problems which I'll try and work out- firstly, running the PHP in the browser doesn't give me the "IN USE" or "OPEN" data, but running it from the CLI does. Right now I'm just redirecting output to an HTML file and running that, no big deal. Thanks for the great base!
Airjoe