Skip to content

Instantly share code, notes, and snippets.

Forked from SaitamaSama/server.php
Last active Sep 9, 2021
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://");
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://");
$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 = [
$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";
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 === '') {
echo "Client #{$socketId} disconnected\n";
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]);
echo "Sent {$sent} bytes to client #{$socketId}\n";
if ($sent === $dataLen) {
echo "Send buffer for client #{$socketId} drained\n";
unset($pendingWriteData[$socketId], $writeSockets[$socketId]);
// 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