Skip to content

Instantly share code, notes, and snippets.

@clue
Last active February 10, 2022 11:17
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save clue/e0425c34c0ab13e8496716aae0219245 to your computer and use it in GitHub Desktop.
Save clue/e0425c34c0ab13e8496716aae0219245 to your computer and use it in GitHub Desktop.
Non-blocking I/O for the masses (WebEngDUS)

I/O is everywhere. I/O is slow. There's no denying it. Using traditional blocking I/O calls can thus be seen as a huge contributor to slow applications. This talk discusses how non-blocking I/O can help in building high performance, event-driven, reactive, concurrent, single-threaded applications (bingo). Don't worry, no need to install Node.js and npm install half the internet. Let's build high-performance applications from scratch with whatever language you're most comfortable with!

20 minutes talk at @WebEngDUS (2018-07-12)

Slides

The slides are available on https://speakerdeck.com/clue/non-blocking-io-for-the-masses-webengdus.

These slides were used as part of a presentation at @WebEngDUS. The full presentation took around 20 minutes including live demonstration and a short Q/A followed by some very nice discussions.

The examples (source code) can be found here.

Conclusions

Given the little preparation time (I stepped in as a replacement speaker and @andygrunwald approached me earlier that day), I'm really happy with how the presentation went and this seems to be confirmed by some of the positive feedback attendees left.

Evidently, the amount of code may have been a bit overwhelming (rushed), so a future iteration of this talk would likely involve less code or more time for each example.

{
"require": {
"react/socket": "^1.0"
}
}
<?php
sleep(1);
echo 'Hello world!' . PHP_EOL;
<?php
$result = file_get_contents('http://localhost:8080/');
echo $result;
<?php
$stream = stream_socket_client('tcp://localhost:8080');
fwrite($stream, "GET / HTTP/1.0\r\n\r\n");
$result = stream_get_contents($stream);
fclose($stream);
echo $result;
<?php
$stream = stream_socket_client('tcp://localhost:8080');
fwrite($stream, "GET / HTTP/1.0\r\n\r\n");
$result = stream_get_contents($stream);
fclose($stream);
$result = explode("\r\n\r\n", $result)[1];
echo $result;
<?php
$server = stream_socket_server('tcp://localhost:8080');
$stream = stream_socket_accept($server);
$ignored = fgets($stream, 8192);
fwrite($stream, "HTTP/1.0 200 OK\r\n\r\nHello world!\n");
fclose($stream);
fclose($server);
<?php
$server = stream_socket_server('tcp://localhost:8080');
while (true) {
$stream = stream_socket_accept($server);
if (!$stream) {
continue;
}
$ignored = fgets($stream, 8192);
fwrite($stream, "HTTP/1.0 200 OK\r\n\r\nHello world!\n");
fclose($stream);
}
<?php
$stream = stream_socket_client('tcp://localhost:8080');
stream_set_blocking($stream, false);
$write = array($stream);
$null = null;
stream_select($null, $write, $null, null);
echo 'Writable' . PHP_EOL;
fwrite($stream, "GET / HTTP/1.0\r\n\r\n");
$read = array($stream);
$null = null;
stream_select($read, $null, $null, null);
echo 'Readable' . PHP_EOL;
$result = stream_get_contents($stream);
fclose($stream);
$result = explode("\r\n\r\n", $result)[1];
echo $result;
<?php
$stream1 = stream_socket_client('tcp://google.com:80');
fwrite($stream1, "GET / HTTP/1.0\r\n\r\n");
stream_set_blocking($stream1, false);
$stream2 = stream_socket_client('tcp://google.cn:80');
fwrite($stream2, "GET / HTTP/1.0\r\n\r\n");
stream_set_blocking($stream2, false);
$stream3 = stream_socket_client('tcp://localhost:8080');
fwrite($stream3, "GET / HTTP/1.0\r\n\r\n");
stream_set_blocking($stream3, false);
$await = array($stream1, $stream2, $stream3);
while ($await) {
$read = $await;
$null = null;
stream_select($read, $null, $null, null);
foreach ($read as $stream) {
unset($await[array_search($stream, $await)]);
echo 'Readable' . PHP_EOL;
$result = stream_get_contents($stream);
fclose($stream);
$result = explode("\r\n\r\n", $result)[1];
echo $result;
}
}
<?php
$server = stream_socket_server('tcp://localhost:8080');
stream_set_blocking($server, false);
$await = array($server);
while (true) {
$read = $await;
$null = null;
stream_select($read, $null, $null, null);
// new client connected
if (in_array($server, $read)) {
unset($read[array_search($server, $read)]);
$client = stream_socket_accept($server);
stream_set_blocking($client, false);
$await[] = $client;
}
// data received from client
foreach ($read as $stream) {
unset($await[array_search($stream, $await)]);
fwrite($stream, "HTTP/1.0 200 OK\r\n\r\nHello world!\n");
fclose($stream);
}
}
<?php
$stream = stream_socket_client(
'tcp://localhost:8080',
$errno,
$errstr,
0,
STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT
);
stream_set_blocking($stream, false);
echo 'Connecting...' . PHP_EOL;
$write = array($stream);
$null = null;
stream_select($null, $write, $null, null);
echo 'Connected' . PHP_EOL;
fwrite($stream, "GET / HTTP/1.0\r\n\r\n");
$read = array($stream);
$null = null;
stream_select($read, $null, $null, null);
echo 'Readable' . PHP_EOL;
$result = stream_get_contents($stream);
fclose($stream);
$result = explode("\r\n\r\n", $result)[1];
echo $result;
<?php
echo 'Resolving...' . PHP_EOL;
// this is still blocking!
$ip = gethostbyname('localhost');
echo 'Resolved' . PHP_EOL;
$stream = stream_socket_client(
'tcp://' . $ip . ':8080',
$errno,
$errstr,
0,
STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT
);
stream_set_blocking($stream, false);
echo 'Connecting...' . PHP_EOL;
$write = array($stream);
$null = null;
stream_select($null, $write, $null, null);
echo 'Connected' . PHP_EOL;
fwrite($stream, "GET / HTTP/1.0\r\n\r\n");
$read = array($stream);
$null = null;
stream_select($read, $null, $null, null);
echo 'Readable' . PHP_EOL;
$result = stream_get_contents($stream);
fclose($stream);
$result = explode("\r\n\r\n", $result)[1];
echo $result;
<?php
use React\Socket\ConnectionInterface;
use React\Socket\Server;
use React\EventLoop\Factory;
require __DIR__ . '/vendor/autoload.php';
$loop = Factory::create();
$server = new Server('127.0.0.1:8080', $loop);
$server->on('connection', function (ConnectionInterface $stream) {
$stream->on('data', function () use ($stream) {
$stream->end("HTTP/1.0 200 OK\r\n\r\nHello world!\n");
});
});
$loop->run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment