Skip to content

Commit 0cba3d9

Browse files
committed
Merge pull request #6 from clue/stream
Use stream based API
2 parents c45d936 + 4feac45 commit 0cba3d9

File tree

3 files changed

+39
-35
lines changed

3 files changed

+39
-35
lines changed

README.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,11 @@ for the required multicast socket options and constants.
8080

8181
These options are only available to the low level socket API (ext-sockets), not
8282
to the newer stream based networking API.
83-
Because of this, this library depends on sockets based on `ext-sockets`, provided
84-
via [clue/socket-react](https://github.com/clue/php-socket-react)
85-
and [clue/socket-raw](https://github.com/clue/php-socket-raw).
86-
8783
For the most part, React PHP is built around the general purpose stream based API
8884
and has only somewhat limited support for the low level socket API.
89-
The package [clue/socket-react](https://github.com/clue/php-socket-react)
90-
works around this for the most part.
91-
Simply put, you should try to avoid using the default `StreamSelectLoop`,
92-
as it requires a workaround to poll the socket resources via a periodic timer
93-
every 10ms.
85+
Because of this, this library uses a workaround to create stream based sockets
86+
and then sets the required socket options on its underlying low level socket
87+
resource.
9488

9589
This library also provides somewhat limited support for PHP 5.3.
9690
While this version lacks the required socket options and constants for listening

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
"require": {
1717
"php": ">=5.3",
1818
"react/event-loop": "~0.3.0|~0.4.0",
19-
"react/promise": "~1.0|~2.0",
20-
"clue/socket-react": "~0.3.0"
19+
"react/datagram": "~1.0"
2120
},
2221
"require-dev": {
2322
"clue/hexdump": "0.2.*"
2423
},
2524
"suggest": {
26-
"php": "PHP 5.4+ is required for listening on multicast addresses and IGMP announcements"
25+
"php": "PHP 5.4+ is required for listening on multicast addresses (socket options to send IGMP announcements)",
26+
"ext-sockets": "Low level socket API required for listening on multicast addresses (socket options to send IGMP announcements)"
2727
}
2828
}

src/Factory.php

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,57 +3,67 @@
33
namespace Clue\React\Multicast;
44

55
use React\EventLoop\LoopInterface;
6-
use Socket\React\Datagram\Factory as DatagramFactory;
7-
use Socket\Raw\Factory as RawFactory;
6+
use React\Datagram\Socket as DatagramSocket;
87
use BadMethodCallException;
8+
use RuntimeException;
99

1010
class Factory
1111
{
1212
private $loop;
13-
private $rawFactory;
14-
private $datagramFactory;
1513

16-
public function __construct(LoopInterface $loop, RawFactory $rawFactory = null, DatagramFactory $datagramFactory = null)
14+
public function __construct(LoopInterface $loop)
1715
{
18-
if ($rawFactory === null) {
19-
$rawFactory = new RawFactory();
20-
}
21-
22-
if ($datagramFactory === null) {
23-
$datagramFactory = new DatagramFactory($loop);
24-
}
25-
26-
$this->rawFactory = $rawFactory;
27-
$this->datagramFactory = $datagramFactory;
16+
$this->loop = $loop;
2817
}
2918

3019
public function createSender()
3120
{
32-
$socket = $this->rawFactory->createUdp4();
33-
return $this->datagramFactory->createFromRaw($socket);
21+
$stream = @stream_socket_server('udp://0.0.0.0:0', $errno, $errstr, STREAM_SERVER_BIND);
22+
if ($stream === false) {
23+
throw new RuntimeException('Unable to create sending socket: ' . $errstr, $errno);
24+
}
25+
26+
return new DatagramSocket($this->loop, $stream);
3427
}
3528

3629
public function createReceiver($address)
3730
{
3831
if (!defined('MCAST_JOIN_GROUP')) {
39-
throw new BadMethodCallException('MCAST_JOIN_GROUP not defined');
32+
throw new BadMethodCallException('MCAST_JOIN_GROUP not defined (requires PHP 5.4+)');
33+
}
34+
if (!function_exists('socket_import_stream')) {
35+
throw new BadMethodCallException('Function socket_import_stream missing (requires ext-sockets and PHP 5.4+)');
4036
}
4137

4238
$parts = parse_url('udp://' . $address);
4339

44-
$socket = $this->rawFactory->createUdp4();
40+
$stream = @stream_socket_server('udp://0.0.0.0:' . $parts['port'], $errno, $errstr, STREAM_SERVER_BIND);
41+
if ($stream === false) {
42+
throw new RuntimeException('Unable to create receiving socket: ' . $errstr, $errno);
43+
}
44+
45+
$socket = socket_import_stream($stream);
46+
if ($stream === false) {
47+
throw new RuntimeException('Unable to access underlying socket resource');
48+
}
4549

4650
// allow multiple processes to bind to the same address
47-
$socket->setOption(SOL_SOCKET, SO_REUSEADDR, 1);
51+
$ret = socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
52+
if ($ret === false) {
53+
throw new RuntimeException('Unable to enable SO_REUSEADDR');
54+
}
4855

4956
// join multicast group and bind to port
50-
$socket->setOption(
57+
$ret = socket_set_option(
58+
$socket,
5159
IPPROTO_IP,
5260
MCAST_JOIN_GROUP,
5361
array('group' => $parts['host'], 'interface' => 0)
5462
);
55-
$socket->bind('0.0.0.0:' . $parts['port']);
63+
if ($ret === false) {
64+
throw new RuntimeException('Unable to join multicast group');
65+
}
5666

57-
return $this->datagramFactory->createFromRaw($socket);
67+
return new DatagramSocket($this->loop, $stream);
5868
}
5969
}

0 commit comments

Comments
 (0)