php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #52848 Processing out-of-band data doesn't work
Submitted: 2010-09-15 04:13 UTC Modified: 2014-04-01 10:17 UTC
Votes:1
Avg. Score:3.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:0 (0.0%)
From: php dot net at phrozenbyte dot de Assigned:
Status: Not a bug Package: Streams related
PHP Version: 5.3.3 OS: Ubuntu 10.04 Lucid Lynx
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: php dot net at phrozenbyte dot de
New email:
PHP Version: OS:

 

 [2010-09-15 04:13 UTC] php dot net at phrozenbyte dot de
Description:
------------
I'm not sure what's going wrong (stream_socket_sendto() or stream_socket_recvfrom()) but proccessing out-of-band data doesn't work correctly. Only the last byte is send as out-of-band data, all other data is send as usally.

Test script:
---------------
Server:
<?php
$server = stream_socket_server('tcp://127.0.0.1:1234');
$socket = stream_socket_accept($server);
echo "OOB-Data 1/2: '".stream_socket_recvfrom($socket, 1500, STREAM_OOB)."'\n";
echo "OOB-Data 2/2: '".stream_socket_recvfrom($socket, 1500, STREAM_OOB)."'\n";
echo "Data 1/2: '".stream_socket_recvfrom($socket, 1500)."'\n";
echo "Data 2/2: '".stream_socket_recvfrom($socket, 1500)."'\n";
fclose($socket);
fclose($server);
?>

Client:
<?php
$socket = stream_socket_client('tcp://127.0.0.1:1234');
stream_socket_sendto($socket, '123456789', STREAM_OOB);
fclose($socket);
?>

Expected result:
----------------
OOB-Data 1/2: '123456789'
OOB-Data 2/2: ''
Data 1/2: ''
Data 2/2: ''

Actual result:
--------------
OOB-Data 1/2: '9'
OOB-Data 2/2: ''
Data 1/2: '12345678'
Data 2/2: ''

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-09-15 14:15 UTC] cataphract@php.net
I can reproduce it in Linux.

In Windows, the server gets no data at all (the first stream_socket_recvfrom keeps blocking indefinitely).
 [2010-09-15 14:44 UTC] cataphract@php.net
Sorry, correction: Windows presented the same behavior.
 [2010-09-15 16:13 UTC] cataphract@php.net
-Status: Open +Status: Bogus
 [2010-09-15 16:13 UTC] cataphract@php.net
"In Transmission Control Protocol (TCP), the OOB data block is always one byte. Therefore, if you send multiple-byte OOB data, only the last byte of the OOB data is retrieved. The remaining data is treated like normal data."

See also http://support.microsoft.com/kb/830597
 [2010-09-15 16:15 UTC] cataphract@php.net
-Status: Bogus +Status: To be documented
 [2010-09-15 16:15 UTC] cataphract@php.net
It should be documented in stream_socket_sendto and stream_socket_recvfrom, that OOB data can be only one byte long.
 [2010-09-15 20:41 UTC] php dot net at phrozenbyte dot de
Ok, thanks. As cataphract at php dot net mentioned first there are situations on which even that single byte isn't shown. I'm not able to reproduce the bug, the only possibility is to repeat running my test server and client. After some trys the script will result in
OOB-Data 1/2: ''
OOB-Data 2/2: ''
Data 1/2: '12345678'
Data 2/2: ''
 [2010-09-15 22:17 UTC] php dot net at phrozenbyte dot de
When you're sending data out-of-band (one or more bytes make no difference) and the client socket is in $read and $except of stream_select() the socket will be available every time in both arrays.
 [2010-09-16 02:06 UTC] srinatar@php.net
few additional comments

on Linux/Solaris socket implementations, when you send with MSG_OOB flags set, only the last byte is recvd. the subsequent recv call will flush out the rest of the bytes.

for example, in your example,if you edit it like below you will notice this more clearly:

client script

<?php
$socket = stream_socket_client('tcp://127.0.0.1:1234');
stream_socket_sendto($socket, '123456789', STREAM_OOB);
fclose($socket);
?>

server (with 2 recv calls even though client is sending only one send call.)
<?php
$server = stream_socket_server('tcp://127.0.0.1:1234');
stream_set_timeout($server, 180);
$socket = stream_socket_accept($server);
echo "Data: '".stream_socket_recvfrom($socket, 100, STREAM_OOB)."'\n";
echo "Data: '".stream_socket_recvfrom($socket, 100)."'\n";
?>

Hope this clarifies
I don't think this is an issue with PHP.
 [2010-09-16 04:43 UTC] php dot net at phrozenbyte dot de
Yes, I understand that.
The issue is in a more special case: When the client sends a single byte (!) with OOB flag, the server will receive 2 recv calls - the first one including the single byte with OOB flag and the second one is empty which results in feof() = true.
That means: Even when you send only a single byte with OOB flag, in which case you can't read non-OOB-data, stream_select() triggers that you can read non-OOB-data. And this is a issue with php I think.


Client:
---------------
<?php
$socket = stream_socket_client('tcp://127.0.0.1:1234');
stream_socket_sendto($socket, 'a', STREAM_OOB);
fclose($socket);
?>

Server:
---------------
<?php
$server = stream_socket_server('tcp://127.0.0.1:1234');
$socket = stream_socket_accept($server);
$read = array($server, $socket); $write = array(); $except = array($socket);
stream_select($read, $write, $except, null);
echo 'number of $read sockets: '.count($read)."\n";
echo 'number of $except sockets: '.count($except)."\n";
?>

Expected result:
---------------
number of $read sockets: 0
number of $except sockets: 1

Actual result:
---------------
number of $read sockets: 1
number of $except sockets: 1
 [2010-09-16 12:40 UTC] cataphract@php.net
There's still no bug. Here's a portion of the strace on the server:

socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
listen(3, 32)                           = 0
poll([{fd=3, events=POLLIN|POLLERR|POLLHUP}], 1, 25000) = 1 ([{fd=3, revents=POLLIN}])
accept(3, {sa_family=AF_INET, sin_port=htons(42533), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4
select(5, [3 4], [], [4], NULL)         = 2 (in [4]], except [4])

So you call see that the call to select() actually returned the socket handle in both fd_set structures, as PHP reported.

The reason for returning the socket handle in readfs is that the connection was closed on the client side. If you don't close the connection, you get your expected result.

Keep the same code on the server and use this for the client:

<?php
$socket = stream_socket_client('tcp://127.0.0.1:1234');
stream_socket_sendto($socket, 'a', STREAM_OOB);
sleep(10);
fclose($socket);

You now see:

number of $read sockets: 0
number of $except sockets: 1
 [2010-09-16 19:24 UTC] php dot net at phrozenbyte dot de
I keeped the server script and replaced the client script with yours. The result:
number of $read sockets: 1
number of $except sockets: 1

There's definitly a issue... Keep the client and try the following server script:
<?php
$now = time();
$server = stream_socket_server('tcp://127.0.0.1:1234');
echo time()-$now.": init\n";

do {
	$read = $write = $except = array();
	$read[] = $server;
	if(isset($client)) {
		$read[] = $client;
		$except[] = $client;
	}

	echo time()-$now.": select\n";
	stream_select($read, $write, $except, null);
	echo time()-$now.': $read contains: '; var_dump($read);
	echo time()-$now.': $except contains: '; var_dump($except);

	foreach($except as $sock) { // $sock === $client
		echo time()-$now.": oob-data: '".stream_socket_recvfrom($client, 1500, STREAM_OOB)."'\n";
	}

	foreach($read as $sock) {
		if($sock === $server) {
			$client = stream_socket_accept($server);
			echo time()-$now.": client connected\n";
		} else { // $sock === $client
			if(feof($client)) {
				echo time()-$now.": client disconnected\n";
				break 2;
			}
			
			$data = stream_socket_recvfrom($client, 1500);
			echo time()-$now.": data: '".$data."'\n";
		}
	}
} while(true);
?>

The script returns the following (notice the time!):
0: init
0: select
0: $read contains: array(1) {
  [0]=>
  resource(5) of type (stream)
}
0: $except contains: array(0) {
}
0: client connected
0: select
0: $read contains: array(1) {
  [0]=>
  resource(6) of type (stream)
}
0: $except contains: array(1) {
  [0]=>
  resource(6) of type (stream)
}
0: oob-data: 'a'
10: client disconnected

That's wrong! At the second call of stream_select() the $read array shouldn't contain any socket, only the $except array does because there's data to read. The time delay between "0: oob-data: 'a'" and "10: client disconnected" is a result of recv()s blocking. stream_select() shouldn't fill $read when there's no data to read. The client-side disconnect affects nothing, that happens (as you can see) 10 seconds later.
 [2010-09-17 00:05 UTC] cataphract@php.net
I can't reproduce this on Debian Lenny:

0: init
0: select
16: $read contains: array(1) {
  [0]=>
  resource(2) of type (stream)
}
16: $except contains: array(0) {
}
16: client connected
16: select
16: $read contains: array(0) {
}
16: $except contains: array(1) {
  [0]=>
  resource(3) of type (stream)
}
16: oob-data: 'a'
16: select
26: $read contains: array(1) {
  [0]=>
  resource(3) of type (stream)
}
26: $except contains: array(0) {
}
26: client disconnected
 [2010-09-17 00:06 UTC] cataphract@php.net
Maybe this is a timing issue. Try to start the client only a few seconds later.
 [2010-09-17 14:16 UTC] php dot net at phrozenbyte dot de
This doesn't help. Maybe it's an issue in my software repository.... I will fill a bug report on launchpad. Thanks for your help and patience!
 [2014-04-01 10:17 UTC] mike@php.net
-Status: Open +Status: Not a bug
 [2014-04-01 10:17 UTC] mike@php.net
I cannot decipher an issue here, if there's still one re-open with a proper description, please.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Wed Oct 23 02:01:31 2019 UTC