|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2015-12-30 22:36 UTC] gohanman at gmail dot com
Description:
------------
I'm trying to set a connection timeout on a TCP port. I specifically want to verify whether a remote service is alive. If the host does not respond within the specified timeout, I want to mark it as down and continue with execution. If you happen to be running something on port 15674 substitute any closed port. I *want* the connection to fail in a predictable way.
In PHP 5.3 - 5.6, socket_connect blocks for roughly one second and then returns false. In PHP 7, socket_connect blocks for infinity as far as I can tell. The longest I've let it run so far is about 15 minutes but its clearly ignoring every built in execution limit. Restarting Apache is the only thing that halts the script's execution.
I'm using php.ini-production with the following modules enabled: sockets, opcache, curl, gettext, intl, mysqli, pdo_mysql, openssl, and xsl. Other than enabling modules I haven't made any changes to the config.
Apache is 2.4.18 (Apache Lounge build). Both PHP and Apache are x86. The same code does work as expected on the same machine in PHP 5.3 - PHP 5.6 so I don't think it's strictly a Windows 10 problem. I'm running the older PHP versions with different, older VC9 and VC11 Apache builds of course.
Test script:
---------------
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($sock, SOL_SOCKET, SO_SNDTIMEO, array('sec' => 1, 'usec' => 0));
socket_set_block($sock);
$test = @socket_connect($sock,'localhost',15674);
socket_close($sock);
echo "Done";
Expected result:
----------------
$test gets boolean value true or false without more or less one second (with reasonable allowance for whatever precision the underlying system can actually provide).
Actual result:
--------------
socket_connect blocks forever
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Tue Oct 28 11:00:01 2025 UTC |
There is no bug here. The difference in behaviour you see is due to the way the machine at the other end deals with the attempt to connect. "localhost" would hopefully drop the connection - actively refuse it, while some other host (say google.com for example) will not. Setting send/recv timeout doesn't work for connect - you are neither reading nor writing, but awaiting readiness (for reading and writing) ... Here's one way of performing a socket_connect() with a reliable timeout: <?php function socket_create_connect($domain, $type, $protocol, $host, $port, $timeout) { $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if (!$sock) { $errno = socket_last_error(); throw new \RuntimeException( socket_strerror($errno), $errno); } socket_set_nonblock($sock); if (!socket_connect($sock, $host, $port)) { switch ($errno = socket_last_error()) { case SOCKET_EINPROGRESS: if (!socket_select($read, $write = [$sock], $except = [], $timeout, 0)) { throw new \RuntimeException( socket_strerror($errno), $errno); } break; default: throw new \RuntimeException( socket_strerror($errno), $errno); } } socket_set_block($sock); return $sock; } $sock = socket_create_connect(AF_INET, SOCK_STREAM, SOL_TCP, "google.com", 15674, 3); socket_close($sock); echo "Done";I bumped into this again today and the behavior still seems slightly odd. Having recently run into a similar issue with PDO using mysqlnd I though that the ini setting for default_socket_timeout would apply here. It doesn't seem to. As krakjoe noted, all of this only applies when the host you're connecting to is silently dropping packets to a closed port rather than actively rejecting them. // This times out after a second ini_set('default_socket_timeout', 1); $fp = stream_socket_client('tcp://localhost:15674', $errno, $error); // This does not ini_set('default_socket_timeout', 1); $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_block($sock); $test = socket_connect($sock,'localhost',15674); The documentation does rightly note that default_socket_timeout applies to "socket based streams" but given the name of the setting it's at the very least unintuitive that it doesn't also apply to actual sockets.