PHP fsockopen doesnt return anything - php

I am modifying a PHP db wrapper for the redis database.
Here's how my function looks:
public function connect() {
$sock = #fsockopen('localhost', '6379', $errno, $errstr, 2);
if ($sock === FALSE) {
return FALSE;
}
else {
stream_set_timeout($sock, 2);
return $sock;
}
}
What I want to do is to call this function from another part in my wrapper:
if ($this->connect() !== FALSE) {
// Do stuff
}
How can I get my connect function to send a FALSE when the fsockopen isn't working?
Thanks!

From a little ways down the fsockopen() page (have to scroll almost to the bottom):
UDP sockets will sometimes appear to have opened without an error, even if the remote host is unreachable. The error will only become apparent when you read or write data to/from the socket. The reason for this is because UDP is a "connectionless" protocol, which means that the operating system does not try to establish a link for the socket until it actually needs to send or receive data.
I'm going to guess that's your problem, I guess you have to do a test read/write to see if it was really successful or not.

Try the following code and see if this works as intended:
public function connect()
{
$sock = #fsockopen('localhost', '6379', $errno, $errstr, 2);
if (!is_resource($sock))
return FALSE;
stream_set_timeout($sock, 2);
return $sock;
}

#fsockopen
You've got an # in front of your function, which is going to suppress errors. If the error is causing zero return, you're not going to get anything. Remove the # and log or display any resulting errors or warnings.

I know it might be way late to answer.
But, fsockopen kinda has a problem with 'localhost'..
try using '127.0.0.1' n i m sure it will connect. ;)

Related

Detecting peer disconnect (EOF) with PHP socket module

I'm having a weird issue with PHP's sockets library: I do not seem to be able to detect/distinguish server EOF, and my code is helplessly going into an infinite loop as a result.
Further explanation below; first of all, some context (there's nothing particularly fancy going on here):
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 8081);
for (;;) {
$read = [$socket];
$except = NULL;
$write = [];
print "Select <";
$n = socket_select($read, $write, $except, NULL);
print ">\n";
if (count($read)) {
print "New data: ";
#socket_recv($socket, $data, 1024, NULL);
$data = socket_read($socket, 1024);
print $data."\n";
}
print "Socket status: ".socket_strerror(socket_last_error())."\n";
}
The above code simply connects to a server and prints what it reads. It's a cut-down version of what I have in the small socket library I'm writing.
For testing, I'm currently using ncat -vvklp 8081 to bind a socket and be a server. With that running, I can fire up the code above and it connects and works - eg, I can type in the ncat window, and PHP receives it. (Sending data from PHP is working too, but I've excluded that code as it's not relevant.)
However, the moment I ^C ncat, the code above enters a hard infinite loop - and PHP says there's no error on the socket.
I am trying to figure out where the button is that whacks PHP upside the head and makes it realize that the peer has disconnected.
socket_get_status() is a great misnomer - it's an alias for stream_get_meta_data(), and it doesn't actually work on sockets!
feof() similarly spouts Warning: feof(): supplied resource is not a valid stream resource.
I can't find a socket_* function for detecting peer EOF.
One of the PHP manual notes for socket_read() initially dissuaded me from using that function so I used socket_recv() instead, but I eventually tried it just in case - but no dice; switching the receive call has no effect.
I have discovered that watching the socket for writing and then attempting to write to it will suddenly make PHP go "oh, wait, right" and start returning Broken pipe - but I'm not interested in writing to the server, I want to read from it!
Finally, regarding the commented part - I would far prefer to use PHP's builtin stream functionality, but the stream_* functions do not provide any means for handling asynchronous connect events (which I want to do, as I'm making multiple connections). I can do stream_socket_client(... STREAM_CLIENT_ASYNC_CONNECT ...) but then cannot find out when the connection has been established (6yo PHP bug #52811).
Okay, I figure I might as well turn the comments above into an answer. All credit goes to Ryan Vincent for helping my thick head figure this out :)
socket_recv will return 0 specifically if the peer has disconnected, or FALSE if any other network error has occurred.
For reference, in C, recv()'s return value is the length of the new data you've just received (which can be 0), or -1 to indicate an error condition (the value of which can be found in errno).
Using 0 to indicate an error condition (and just one arbitrary type of error condition, at that) is not standard and unique to PHP in all the wrong ways. Other network libraries don't work this way.
You need to to handle it like this.
$r = socket_recv($socket, $buf, $len);
if ($r === FALSE) {
// Find out what just happened with socket_last_error()
// (there's a great list of error codes in the comments at
// http://php.net/socket_last_error - considering/researching
// the ramifications of each condition is recommended)
} elseif ($r === 0) {
// The peer closed the connection. You need to handle this
// condition and clean up.
} else {
// You DO have data at this point.
// While unlikely, it's possible the remote peer has
// sent you data of 0 length; remember to use strlen($buf).
}

PHP: how to use socket_select() and socket_read() properly

Edit: *Bug in test harness was causing me to misinterpret the results. socket_select() works exactly as you might expect: it really does wait until there is data ready on the socket. But it will report ready if you call it after the client closes the connection. Turns out it's all my fault, but I'll leave the question here in case anyone else ever suspects the behavior of socket_select().
I have a multi-threaded PHP application and I'm using sockets to communicate between the threads. The communication works pretty well, but I end up doing a lot of unnecessary reading of sockets that have no data ready. Perhaps there's something about socket programming that I'm missing.
The PHP doc for socket_select() says The sockets listed in the read array will be watched to see if a read will not block.
This is exactly the behavior I want: call socket_select() to wait until one of my child threads is trying to talk to me. But that only works for the very first time the thread writes, after I've accepted the connection. After that, socket_select() will forever say that the socket is ready, even after I've read all the data from it.
Is there some way I can mark that socket 'unready' so socket_select() won't report that it's ready until more data arrives? Or is it conventional to close that connection after I've read all the data, and await another connection request? I've done a lot of tutorial- and explanation-reading, but I haven't been able to figure out how to do this correctly. Maybe I'm missing something obvious?
In case it helps to see code, here's what I'm doing:
// Server side setup in the main thread
$this->mConnectionSocket = socket_create(AF_INET, SOCK_STREAM, 0);
$arrOpt = array('l_onoff' => 1, 'l_linger' => 0);
#socket_set_option($this->mConnectionSocket, SOL_SOCKET, SO_LINGER, $arrOpt);
#socket_set_option($this->mConnectionSocket, SOL_SOCKET, SO_REUSEADDR, true);
#socket_bind($this->mConnectionSocket, Hostname, $this->mPortNumber);
#socket_listen($this->mConnectionSocket);
#socket_set_block($this->mConnectionSocket);
.
.
.
// Then call listen(), which looks like this:
public function listen(&$outReadySockets) {
$null = null;
while(true) {
$readyArray = array_merge(array($this->mConnectionSocket), $this->mReceiverSockets);
socket_select($readyArray, $null, $null, $waitTime = null);
if(in_array($this->mConnectionSocket, $readyArray) === true) {
$this->acceptConnection();
$key = array_search($this->mConnectionSocket, $readyArray);
if($key === false) {
throw new IPCException("array_search() returned unexpected value");
} else {
unset($readyArray[$key]);
if(in_array($this->mConnectionSocket, $readyArray) === true) {
throw new IPCException("in_array() says the key is still there");
}
}
}
if(count($readyArray) > 0) {
$outReadySockets = array_merge($readyArray);
break;
}
}
}
// Client side setup in the child thread
$this->mSocket = #socket_create(AF_INET, SOCK_STREAM, 0);
#socket_set_block($this->mSocket);
#socket_connect($this->mSocket, Hostname, $this->mPortNumber);
.
.
.
#socket_write($this->mSocket, $inDataToWrite, $lengthToWrite);
// Main thread reads the socket until it's empty
$data = "";
$totalBytesRead = 0;
while($totalBytesRead < $inNumberOfBytesToRead) {
// Strange that even if we set the socket to block mode, socket_read()
// will not block. If there's nothing there, it will just return an
// empty string. This is documented in the PHP docs.
$tdata = socket_read($inSock, $inNumberOfBytesToRead);
if($tdata === false) {
throw new IPCException("socket_read() failed: " . socket_strerror(socket_last_error()));
} else {
$data .= $tdata;
$bytesReadThisPass = strlen($tdata);
if($bytesReadThisPass === 0) {
break;
}
}
$totalBytesRead += $bytesReadThisPass;
}
.
.
.
// Then calls listen() again
As I say, it works great, except that when I call listen() a second time, it tells me that the socket is still ready. That seems to be what the PHP doc is saying, but I don't want that. I want to know when there really is data there. Am I doing it wrong? Or just missing the point?
You are misusing the socket functions.
First of all, socket_read will return false on error; your code does not check for this and will treat error returns the same as empty strings (this is an artifact of the specific constructs you are using, i.e. string concatenation and strlen).
Another problem is that when a connection attempt is detected you are clearly violating the instructions for socket_select:
No socket resource must be added to any set if you do not intend to
check its result after the socket_select() call, and respond
appropriately. After socket_select() returns, all socket resources in
all arrays must be checked. Any socket resource that is available for
writing must be written to, and any socket resource available for
reading must be read from.
Instead of this, if a connection is detected the code accepts it and goes back to socket_select again; sockets ready for reading are not serviced.
Finally it looks like you are confused about what EOF means on a socket: it means that the client has closed its write end of the connection. Once socket_read returns the empty string (not false!) for the first time, it will never return anything meaningful again. Once that happens you can send data from the server's write end and/or simply close the connection entirely.

PHP - Detecting remote host disconnection

According to the documentation, socket_read() is supposed to return FALSE when the remote host has closed the connection, and an empty string '' when there is no more data to read. However, during my testing, it never returns FALSE, even when I close the remote host connection. Here is the relevant code:
$data = '';
do {
$read = socket_read($socket, 1024);
if ($read === FALSE) {
throw new SocketDisconnectException();
}
$data .= $read;
} while ($read !== '');
The SocketDisconnectException never gets thrown, even when I disconnect the remote host connection. I've double and triple checked that I'm not catching the exception and discarding it, and even thrown in an echo and exit into the conditional as a sanity check.
Has the behavior of this function changed, or am I doing something wrong?
There seems to be a bug where if you're using PHP_NORMAL_READ it will return false on remote disconnect, but PHP_BINARY_READ will return "". PHP_BINARY_READ is the default, I'd suggest trying PHP_NORMAL_READ if that works for your purposes.

PHP - Catch timeout exception from ldap_connect()

I've written a little monitoring script in PHP, which should monitor a virtual directory and it's active directories. Everything works fine but when the virtual directory service freezes is my ldap_connect() not able to connect but also doesn't get an error back. So my whole script stands still. I think that the ldap_connect function gets a timeout back (like when you try to ping an IP and it's not reachable).
That's my connect command:
$connection = ldap_connect($hostname, $port) or die("Could not connect to {$hostname});
And I haven't found something in the manual for ldap_connect() (manual) about a timelimit parameter in which you could define how long the function should try to connect until it aborts.
How ever I wasn't quite able to come up with a solution with try and catch or something like this. I also didn't wanted to use the set_time_limit() function because my script needs to be run until the end.
I appreciate every help :)
Thanks and greetings
Tim
http://www.php.net/manual/en/function.ldap-set-option.php
particular the following options :-
LDAP_OPT_NETWORK_TIMEOUT
LDAP_OPT_TIMELIMIT
http://www.php.net/manual/en/function.ldap-set-option.php
try set LDAP_OPT_REFERRALS in 0
If you don't want your PHP program to wait XXX seconds before giving up in a case when one of your corporate DC's have failed,
and since ldap_connect() does not have a mechanism to timeout on a user specified time,
this is my workaround which shows excellent practical results.
function serviceping($host, $port=389, $timeout=1)
{
$op = fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$op) return 0; //DC is N/A
else {
fclose($op); //explicitly close open socket connection
return 1; //DC is up & running, we can safely connect with ldap_connect
}
}
// ##### STATIC DC LIST, if your DNS round robin is not setup
//$dclist = array('10.111.222.111', '10.111.222.100', '10.111.222.200');
// ##### DYNAMIC DC LIST, reverse DNS lookup sorted by round-robin result
$dclist = gethostbynamel('domain.name');
foreach ($dclist as $k => $dc) if (serviceping($dc) == true) break; else $dc = 0;
//after this loop, either there will be at least one DC which is available at present, or $dc would return bool false while the next line stops program from further execution
if (!$dc) exit("NO DOMAIN CONTROLLERS AVAILABLE AT PRESENT, PLEASE TRY AGAIN LATER!"); //user being notified
//now, ldap_connect would certainly connect succesfully to DC tested previously and no timeout will occur
$ldapconn = ldap_connect($dc) or die("DC N/A, PLEASE TRY AGAIN LATER.");
Also with this approach, you get a real nice fail over functionality.
Take for an example a company with a dozen of DC-a distributed along distant places.
This way your PHP program will always have high availability if at least one DC is active at present.
You'll need to use an API that supports time-outs. Connection time-outs are not supported in a native fashion by LDAP (the protocol). The timelimit is a client-requested parameter that refers to how long the directory will spend processing a search request, and is not the same as a "connect time-out".

PHP get information if the network isn't in service

I use gravatar for images in my website, but if the network is bad, I would like to know that and use images in other ways.
So how can I know if a particular website is not in service?
No need for complex socket and other remote processing. You can verify if the returned image is indeed an image using the PHP GD library:
if( !$size = getimagesize($filename) ) {
$image = 'images/default.jpg';
}
If your goal is simply to check whether a website is down, you can do this pretty simply in php as explained here and here. Essentially you will try to open that website and if something loads you assume its up and running, should it not load you assume the website is down.
Try this:
<?php
function GetServerStatus($site, $port)
{
$fp = #fsockopen($site, $port, $errno, $errstr, 2);
if (!$fp)
return false;
else
return true;
}
?>
pass it a site (or IP) and a port and it will return false if the site is down and true if the site is up. This will at least tell you if the site is down or not.
To know that a site is down, you basically need to try and get some data from it (webpage, image, whatever) and use a timeout. If no data is returned within that time period, assume it's down.
Here's a simple way:
<?php
ini_set('default_socket_timeout', 5);
function getNetworkStatus(url) {
return ( file_get_contents(urlencode($url)) !== false )
}
?>
The problem is you can't just keep querying the remote server, as this will add a big latency to your page/application - you will need to cache this information for a period of time (database, file, whatever).

Categories