Unable to connect to a non-blocking socket

HomeCoder picture HomeCoder · Mar 18, 2012 · Viewed 7.6k times · Source

This makes me nuts. I have to create a very simple non-blocking socket script in php 5.3 where a client connects to a server, both using non-blocking sockets.

I've tried phpsocketdaemon and the example from the php manual, but in both cases when I try to connect to the server I get the following error:

socket_connect() [function.socket-connect]: unable to connect [10035]:
A non-blocking socket operation could not be completed immediately

My client script where the error happens:

$service_port = 2002;
$address = '127.0.0.1';

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_nonblock($socket); 
$result = socket_connect($socket, $address, $service_port);
...

I'm using the free version of Zend Server 5.6.0 SP1 on Win 7.

Does anyone know how to fix this problem or know a simple and understandable example of a non-blocking socket client/server script?

Answer

dAm2K picture dAm2K · Mar 18, 2012

When you set your socket non blocking you cannot expect that the result of socket_connect() returns TRUE if it's connected or FALSE if not.

PHP Manual page:

If the socket is non-blocking then this function returns FALSE with an error Operation now in progress.

This is true in any language. You have to set the socket "blocking" or you have to poll/select on your file descriptor before checking if you are correctly connected or not. In PHP you may recall the socket_connect() function after a small period of time to check if it returns true, false or wait for timeout to expire.

Try this code [EDITED to fix a small error on timeout routine]:

<?php

  $service_port = 2002;
  $address = '127.0.0.1';
  $timeout = 3;

  $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  socket_set_nonblock($socket);
  $error = NULL;
  $attempts = 0;
  $timeout *= 1000;  // adjust because we sleeping in 1 millisecond increments
  $connected = FALSE;
  while (!($connected = @socket_connect($socket, $address, $service_port)) && ($attempts++ < $timeout)) {
        $error = socket_last_error();
        if ($error != SOCKET_EINPROGRESS && $error != SOCKET_EALREADY) {
              echo "Error Connecting Socket: ".socket_strerror($error) . "\n";
              socket_close($socket);
              return NULL;
        }
        usleep(1000);
  }

  if (!$connected) {
        echo "Error Connecting Socket: Connect Timed Out After " . $timeout/1000 . " seconds. ".socket_strerror(socket_last_error()) . "\n";
        socket_close($socket);
        return NULL;
  }

?>