TCP connect by using socket return false results

2019-02-23 01:48发布

问题:

Here is the codes which i'm using to scan the port if it's open or not.

https://stackoverflow.com/a/8957053/2203703

Could anyone try to fix it please? it seems not work correctly, even if the port is off, the codes always return "successfully connected"

Here is the ip list which i have tested it with port 80

79.142.126.3 //Connection refused 
222.165.195.103 //Connection refused
64.75.193.162 //Connection refused
118.97.197.146 //Port is open
222.134.154.103 //Connection timed out 
173.0.59.170 //Port is open

And here is the output:

5 sockets connected successfully
79.142.126.3 connected successfully
222.165.195.103 connected successfully
64.75.193.162 connected successfully
118.97.197.146 connected successfully
173.0.59.170 connected successfully

Codes looks fine, but i really can't find the problem.

Any help please?

回答1:

The code you link was written a long time ago, when I had a limited understanding of many aspects of non-blocking I/O.

Really this needs an event loop, of which there are many implementations, but for this example I'll use @rdlowrey's Alert library, because it is a minimalist code that should be fairly easy to understand. You can also take a loop at React, which is higher level and provides many more features.

Note that the below example requires PHP 5.4+

<?php

// Array of addresses to test
$addresses = [
    '192.168.5.150',
    '192.168.5.152',
    'google.com',    // Important note: DNS is resolved synchronously here.
    '192.168.5.155', // this can seriously slow down the process as it can block
    '192.168.5.20',  // for a few seconds, async DNS resolution is non-trivial
    '192.168.40.40', // though
];
// The TCP port to test
$port = 80;
// The length of time in seconds to allow host to respond
$timeout = 5;

// This will hold the results
$lookups = [];

// Create a reactor
$reactor = (new \Alert\ReactorFactory)->select();

$count = count($addresses);
$completedCount = 0;

$onComplete = function($address, $result)
                  use(&$lookups, &$completedCount, $count, $reactor) {

    // Cancel the watchers for this address
    $reactor->cancel($lookups[$address]['failWatcher']);
    $reactor->cancel($lookups[$address]['writeWatcher']);
    $reactor->cancel($lookups[$address]['readWatcher']);

    // Store the result
    $lookups[$address] = $result;

    // If there's nothing left to do, stop the reactor
    if (++$completedCount == $count) {
        $reactor->stop();
    }
};

foreach ($addresses as $address) {
    // Normalise the address to lower-case, as it will be used as an array key
    $address = strtolower($address);

    if (!isset($lookups[$address])) {
        // Create a socket to connect asynchronously
        $sockAddr = "tcp://{$address}:{$port}";
        $flags = STREAM_CLIENT_ASYNC_CONNECT;
        $socket = stream_socket_client($sockAddr, $errNo, $errStr, 0, $flags);
        stream_set_blocking($socket, 0);

        // Set up a timeout to watch for failed connections
        $failWatcher = function() use($address, $onComplete, $timeout) {
            echo "{$address} connect failed: Connect timed out\n";
            $onComplete($address, false);
        };
        $failWatcherId = $reactor->once($failWatcher, $timeout);

        // Watch for the stream becoming writable (connection success)
        $writeWatcher = function() use($address, $onComplete) {
            echo "{$address} connected successfully\n";
            $onComplete($address, true);
        };
        $writeWatcherId = $reactor->onWritable($socket, $writeWatcher);

        // Watch for the stream becoming readable (success or explicit fail)
        $readWatcher = function() use($address, $onComplete, $socket) {
            if ('' === $data = fread($socket, 1024)) {
                echo "{$address} connect failed: Server refused connection\n";
                $onComplete($address, false);
            } else if ($data === false) {
                echo "{$address} connect failed: Stream read error\n";
                $onComplete($address, false);
            } else {
                echo "{$address} connected successfully\n";
                $onComplete($address, true);
            }
        };
        $readWatcherId = $reactor->onReadable($socket, $readWatcher);

        // Store the watcher IDs so they can be destroyed later
        $lookups[$address] = [
            'failWatcher'  => $failWatcherId,
            'writeWatcher' => $writeWatcherId,
            'readWatcher'  => $readWatcherId,
        ];
    }
}

// Set everything going
$reactor->run();

// $lookups is now an array of booleans indicating whether the address resulted
// in a successful connection


回答2:

This code prints out "succesfuly" anyway, also on failure..

You can see that echo "$count sockets connected successfully\n" and echo "$address connected successfully\n" is not depending on any conditions...

Only echo "Created socket for $address\n" is depending on condition, but socket can be created also when the port is closed.

I don't see any problem. the general message "X sockets connected successfully" will be printed (you can remove it...) but the speciffic message "XXXXX connected successfully" will not be printed!



回答3:

If you can afford to test ips sequentially (obviously, slower then in parallel) then you can simplify your code and have a useful response from socket_connect function:

<?php

  // An array of hosts to check
  $addresses = array(
    '79.142.126.3', //Connection refused
    '222.165.195.103', //Connection refused
    '64.75.193.162', //Connection refused
    '118.97.197.146', //Port is open
    '222.134.154.103', //Connection timed out
    '173.0.59.170' //Port is open
  );

  // The TCP port to test
  $testport = 80;
  // The length of time in seconds to allow host to respond
  $timeout = 5;

  foreach ($addresses as $address) {

    if (!$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) {
      echo "Could not create socket for $address\n";
      continue;
    }

    socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $timeout, 'usec' => 0));
    socket_set_option($sock, SOL_SOCKET, SO_SNDTIMEO, array('sec' => $timeout, 'usec' => 0));

    if (@socket_connect($sock, $address, $testport)) {
        echo "$address connected successfully\n";
    }
    else {
        echo "Unable to connect to $address\n";
    }
  }

Response:

Unable to conenct to 79.142.126.3
Unable to conenct to 222.165.195.103
Unable to conenct to 64.75.193.162
118.97.197.146 connected successfully
Unable to conenct to 222.134.154.103
173.0.59.170 connected successfully


标签: php sockets tcp