|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2005-05-08 22:18 UTC] mjpph at stardust dot fi
Description:
------------
Using stream_socket_client or stream_socket_server to open a connection fails to report correct values with stream_select. Using fread to get one byte before stream_select suddenly makes stream_select work as expected.
Without it stream_select returns 0 changed streams even though there is data waiting. Service used as an endpoint is persistent and doesn't close the connection after it's output. PHP is CLI-version.
Reproduce code:
---------------
$c = stream_socket_client("tcp://127.0.0.1:80");
while (1)
{
$streams = array($c);
if (stream_select($streams, $write=NULL, $except=NULL, 0, 5000)) {
print "Data received\n";
}
}
// IP:Port can be anything which outputs something after connection.
Expected result:
----------------
Text "Data received" after connection and output from the service. The service can be anything which just outputs something right after connect.
Actual result:
--------------
Nothing. stream_select returns 0 even though there is data waiting.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Wed Nov 05 09:00:01 2025 UTC |
I used the reproduce code to connect to my own SMTP server, which by using standard socket functions, telnet or any other method instantly outputs the welcome message when I connect. Just add IP+Port to some SMTP server and it'll work for the test. Here's the strace: read(3, "<?\n$c = stream_socket_client(\"tc"..., 8192) = 220 read(3, "", 4096) = 0 read(3, "", 8192) = 0 close(3) = 0 munmap(0x2aaaad2e0000, 4096) = 0 socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3 close(3) = 0 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR|O_LARGEFILE) fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0 connect(3, {sa_family=AF_INET, sin_port=htons(25), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress) poll([{fd=3, events=POLLIN|POLLOUT|POLLERR|POLLHUP, revents=POLLOUT}], 1, 60000) = 1 getsockopt(3, SOL_SOCKET, SO_ERROR, "\0\0\0\0", [12884901892]) = 0 fcntl(3, F_SETFL, O_RDWR) = 0 select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 1000}) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 5000}) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 5000}) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 5000}) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 5000}) .. it continues like this until I kill the script. The stream_select() never returns anything. If I do fgets() instead of the stream_select() it returns the SMTP welcome instantly.New info, I was able to get the reproduce code to actually work as it's intended on a Fedora Core 2 machine with PHP 5.0.0RC3 (cgi) (built: Jun 10 2004 16:56:32). Here's strace of it: read(3, "<?\n$c = stream_socket_client(\"tc"..., 8192) = 199 read(3, "", 4096) = 0 read(3, "", 8192) = 0 close(3) = 0 munmap(0x2a97e16000, 4096) = 0 socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3 close(3) = 0 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR|O_LARGEFILE) fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0 connect(3, {sa_family=AF_INET, sin_port=htons(25), sin_addr=inet_addr("83.145.20 0.2")}, 16) = -1 EINPROGRESS (Operation now in progress) select(4, [3], [3], [3], {60, 0}) = 1 (out [3], left {60, 0}) getsockopt(3, SOL_SOCKET, SO_ERROR, [17179869184], [4]) = 0 fcntl(3, F_SETFL, O_RDWR) = 0 select(4, [3], [], [], {0, 5000}) = 0 (Timeout) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 5000}) select(4, [3], NULL, NULL, {60, 0}) = 1 (in [3], left {60, 0}) at this point the stream_select returns as intended and script ends. The reproduce code DOESN'T work with several machines running Fedora Core 3 by using 5.0.4 as well as with 200505260030 dated snapshot. stream_select simply doesn't give me anything. Platform is Fedora Core 3 x86_64 under dual xeon 3.2ghz (Nocona, EM64T) with kernel 2.6.11-1.14_FC3smp on the machines that don't return stream_select() as it should.Also PHP 5.1.0-dev (cli) (built: May 21 2005 21:29:39) works fine under FC2. It seems this is PHP<>FC3 issue. Here's strace from the working (FC2) script: connect(3, {sa_family=AF_INET, sin_port=htons(25), sin_addr=inet_addr("<HIDDEN>")}, 16) = -1 EINPROGRESS (Operation now in progress) select(4, [3], [3], [3], {60, 0}) = 1 (out [3], left {60, 0}) getsockopt(3, SOL_SOCKET, SO_ERROR, [17179869184], [4]) = 0 fcntl(3, F_SETFL, O_RDWR) = 0 select(4, [3], [], [], {0, 5000}) = 0 (Timeout) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 4000}) Here's strace from the non-working version (FC3) connect(3, {sa_family=AF_INET, sin_port=htons(25), sin_addr=inet_addr("<HIDDEN>")}, 16) = -1 EINPROGRESS (Operation poll([{fd=3, events=POLLIN|POLLOUT|POLLERR|POLLHUP, revents=POLLOUT}], 1, 60000) = 1 getsockopt(3, SOL_SOCKET, SO_ERROR, "\0\0\0\0", [12884901892]) = 0 fcntl(3, F_SETFL, O_RDWR) = 0 select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 0}) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 5000}) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 5000}) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 5000}) .. to the infinity.Another update. I modified the test script to connect to HTTP which doesn't output anything directly but instead waits for request from the client. In this case the select timeouts on FC3 like it should, but obviously doesn't return anything either as the connection doesn't output anything. When the HTTP closes the connection the stream_select starts behaving like with the SMTP example, looping as fast as it can. If I change the port to SMTP with instant output, it loops the stream_select() like a madman without the requested 5 second delay. The working (FC2) script: select(4, [3], [], [], {0, 5000}) = 0 (Timeout) select(4, [3], [], [], {0, 5000}) = 1 (in [3], left {0, 4000}) select(4, [3], NULL, NULL, {60, 0}) = 1 (in [3], left {60, 0}) First it selects and timeouts when waiting for data, then it returns 1 for changed streams and the stream resource (3) as the changed stream. So this works as expected. Here, the non-working FC3 version: select(4, [3], [], [], {5, 5000}) = 1 (in [3], left {4, 998000}) select(4, [3], [], [], {5, 5000}) = 1 (in [3], left {5, 5000}) select(4, [3], [], [], {5, 5000}) = 1 (in [3], left {5, 5000}) It returns the same as the FC2 (except the one with the nulls) so this should indicate AND return PHP's stream_select() with the value of 1, but by checking the PHP's stream_select() return value it's always 0. It's like the system level select() works ok but the PHP function fails to return it's results correctly? Also something that bothers with me this conclusion is that there's not a single timeout in the FC3 case which could indicate some other problem as well, maybe the connection isn't established correctly?The strangest thing.. I had time to do some test compiles with the 200505260030 snapshot and I got some working. I took my usual parameters off from configure one by one, after I removed --with-openssl the script started suddenly working. Then I did test compile with ./configure --with-openssl and without any parameters. The one --with-openssl failed the script and the one without it worked just fine. It seems that the openssl support does some havoc which causes the failures. I did proper make clean / make distclean between the compiles and doublechecked the results. Linux is a Fedora Core 3, standard install with development support enabled. The test script is as follows, the host machine has sendmail running as a test service. <? $c = stream_socket_client("tcp://127.0.0.1:25"); while (1) { $streams = array($c); if (stream_select($streams, $write=NULL, $except=NULL, 5, 0)) die("Select OK\n"); } ?> Strace from the working PHP (without any configure parameters): read(3, "<?\n$c = stream_socket_client(\"tc"..., 8192) = 221 read(3, "", 4096) = 0 read(3, "", 8192) = 0 close(3) = 0 munmap(0x2a97b2c000, 4096) = 0 socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3 close(3) = 0 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR|O_LARGEFILE) fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0 connect(3, {sa_family=AF_INET, sin_port=htons(25), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress) poll([{fd=3, events=POLLIN|POLLOUT|POLLERR|POLLHUP, revents=POLLOUT}], 1, 60000) = 1 getsockopt(3, SOL_SOCKET, SO_ERROR, "\0\0\0\0", [12884901892]) = 0 fcntl(3, F_SETFL, O_RDWR) = 0 select(4, [3], [], [], {5, 0}) = 1 (in [3], left {4, 998000}) write(1, "Select OK\n", 10) = 10 close(3) = 0 close(1) = 0 close(0) = 0 ... Strace from the non-working PHP (only --with-openssl added for configure): read(3, "<?\n$c = stream_socket_client(\"tc"..., 8192) = 221 read(3, "", 4096) = 0 read(3, "", 8192) = 0 close(3) = 0 munmap(0x2a97b2f000, 4096) = 0 socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3 close(3) = 0 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR|O_LARGEFILE) fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0 connect(3, {sa_family=AF_INET, sin_port=htons(25), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress) poll([{fd=3, events=POLLIN|POLLOUT|POLLERR|POLLHUP, revents=POLLOUT}], 1, 60000) = 1 getsockopt(3, SOL_SOCKET, SO_ERROR, "\0\0\0\0", [12884901892]) = 0 fcntl(3, F_SETFL, O_RDWR) = 0 select(4, [3], [], [], {5, 0}) = 1 (in [3], left {4, 998000}) select(4, [3], [], [], {5, 0}) = 1 (in [3], left {5, 0}) select(4, [3], [], [], {5, 0}) = 1 (in [3], left {5, 0}) .. continues until the script is killedI have compared the original socket operations code, and the one in the xp_ssl.x file, and it seems that even in the non-ssl path the code is not identical. I have fixed four differences, two of which caused some functions to be called twice, and two of which are casting to *(int*) I do not claim to know the real cause for the bug, and how these changes fix the problem, but they do, and they make the code more consistent, and faster, so I think they should be committed. Here is the patch that solves this problem form me (against 5.1.2) ------------------------cut here----------------------- *** php-5.1.2/ext/openssl/xp_ssl.c 2006-01-01 13:50:10.000000000 +0100 --- php-5.1.2ssl/ext/openssl/xp_ssl.c 2006-01-31 23:00:58.000000000 +0100 *************** *** 188,194 **** } while(retry); } else { ! didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC); } if (didwrite > 0) { --- 188,195 ---- } while(retry); } else { ! didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC); ! return didwrite; } if (didwrite > 0) { *************** *** 226,231 **** --- 227,233 ---- else { nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC); + return nr_bytes; } if (nr_bytes > 0) { *************** *** 636,642 **** case PHP_STREAM_AS_FD_FOR_SELECT: if (ret) { ! *ret = (void*)sslsock->s.socket; } return SUCCESS; --- 638,644 ---- case PHP_STREAM_AS_FD_FOR_SELECT: if (ret) { ! *(int*)ret = (void*)sslsock->s.socket; } return SUCCESS; *************** *** 646,652 **** return FAILURE; } if (ret) { ! *ret = (void*)sslsock->s.socket; } return SUCCESS; default: --- 648,654 ---- return FAILURE; } if (ret) { ! *(int*)ret = (void*)sslsock->s.socket; } return SUCCESS; default: ------------------------cut here-----------------------