Skip to content

Instantly share code, notes, and snippets.

@DaveRandom
Forked from SaitamaSama/server.php
Last active Sep 9, 2021
Embed
What would you like to do?
<?php declare(strict_types = 1);
function send_data($socket, $data)
{
echo "Sending data {$data}\n";
fwrite($socket, $data);
$response = fread($socket, 1024);
echo "Got response: {$response}\n";
}
$socket = stream_socket_client("tcp://127.0.0.1:47806");
send_data($socket, 'HELLO');
send_data($socket, 'WHAT YOU UP TO?');
send_data($socket, 'HAVING FUN?');
<?php declare(strict_types = 1);
$server = stream_socket_server("tcp://127.0.0.1:47806");
$serverId = (int)$server;
$readSockets = [$serverId => $server];
$writeSockets = $pendingWriteData = [];
// This defines our "protocol". It's just an array that maps "request" strings to "response" strings
$protocolDefinition = [
'HELLO' => 'HEY THERE',
'WHAT YOU UP TO?' => 'OH NOT MUCH',
'HAVING FUN?' => 'YEH THIS IS GREAT!',
];
$badRequestResponse = "SORRY I DIDN'T UNDERSTAND YOU :-("; // send this back to any data we don't recognise
while (true) {
// do anything you might need to do at the beginning of each loop tick
echo "Loop tick\n";
$r = $readSockets; // create copies of all watchable socket arrays
$w = $writeSockets;
$e = null; // we still don't care about OOB data. Very few applications do.
// how long (max) select() will block waiting for something to happen
$timeoutSecs = 1;
$timeoutUSecs = 0;
$selectResult = stream_select($r, $w, $e, $timeoutSecs, $timeoutUSecs);
if ($selectResult === false) {
// an E_WARNING is raised in this case, check that to find out if there's any more info about what went wrong
echo "select() returned an error\n";
break;
}
if ($selectResult > 0) {
// if something has happened on a socket then process it
echo count($r) . " sockets have readable activity\n";
foreach ($r as $socket) {
$socketId = (int)$socket;
if ($socketId === $serverId) {
$client = stream_socket_accept($server);
stream_set_blocking($client, false); // set the stream to non-blocking as soon as it's created
$clientId = (int)$client;
$readSockets[$clientId] = $client;
echo "Got a new client #{$clientId}\n";
} else {
$data = fread($socket, 1024);
if ($data === '') {
unset($readSockets[$socketId]);
echo "Client #{$socketId} disconnected\n";
continue;
}
echo "Got data from client #{$socketId}: {$data}\n";
$pendingWriteData[$socketId] = $protocolDefinition[$data] ?? $badRequestResponse;
$writeSockets[$socketId] = $socket; // this is a simple example so we won't actually try to send the
// data here so that we can force the loop to detect writable
// sockets. In the real world, we'd actually try and send the data
// immediately and only queue it in the buffer if we couldn't send
// it all.
echo "Responding with {$pendingWriteData[$socketId]}\n";
}
}
echo count($w) . " sockets are writable\n";
foreach ($w as $socket) {
$socketId = (int)$socket;
$data = $pendingWriteData[$socketId];
$dataLen = strlen($data);
echo "Sending data {$data} ({$dataLen} bytes) to client #{$socketId}\n";
$sent = fwrite($socket, $data);
if ($sent === false) {
echo "Sending data failed!\n"; // E_WARNING should tell you want happened here
unset($pendingWriteData[$socketId], $writeSockets[$socketId]);
continue;
}
echo "Sent {$sent} bytes to client #{$socketId}\n";
if ($sent === $dataLen) {
echo "Send buffer for client #{$socketId} drained\n";
unset($pendingWriteData[$socketId], $writeSockets[$socketId]);
continue;
}
// if we get here, it means that we only managed to send part of the data. This won't happen unless you are
// trying to send a lot of data (probably several KB) in one go. So here we just remove the data that we sent
// from the start of our buffer, and leave the socket to be watched for writes so we can try again.
$pendingWriteData[$socketId] = substr($pendingWriteData[$socketId], $sent);
}
}
// do anything you might need to do at the end of each loop tick
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment