Graag zou ik wat data versturen vanuit een telefoon naar een iPad aan de muur. Als "tussenpersoon" had ik mijn NAS in gedachte met daarop een PHP script met Websockets.
Als ik nu op de iPad naar http:\\IP-van-NAS\websockets.html ga, dan zie ik het resultaat van het test-script. (namelijk een teller die iedere seconde update).
Nu moet ik nog iets verzinnen om de data vanuit de telefoon (via het Websocket) naar de html van de iPad te krijgen.
Ik heb een PHP-script in gedachte dat ik ook op de NAS laat draaien, en dat als URL-parameters (via GET) de data vanuit de telefoon "pakt" en op de een of andere manier "doorgeeft" aan het Websocket. Maar hoe?
Hoe krijg ik (in een PHP script) data -die via $data = $_GET['data']; in de variabele $data zit- doorgestuurd naar dat Websocket dat (via websockets.php) op de NAS draait? (om het vervolgens dus in websockets.html zichtbaar te krijgen, dit laatste werkt dus al!)
- Je websockets.php draait in een loop. Kijk in die loop of er op een bepaalde plek een bepaald bestandje (met de "data") staat (daar neer gezet door het "data script"), en speel dat dan door via de WebSocket naar de iPad (en verwijder daarna het bestand). Gebruik hiervoor een RAM-disk, anders sta je steeds je disks te poetsen.
De nette manier:
- Vanuit het PHP script dat de data opvangt open je ook een connectie naar je websockets.php en geef je zo de informatie door. Dit kan vrij eenvoudig met een sockets (zie https://www.php.net/manual/en/ref.sockets.php ). Let op: dit is wat anders als de WebSocket! Kies een vrije poort, en laat websockets.php daar op luisteren. Vervolgens stuur je vanuit je data script de data door.
Je kan toch in je $content gewoon $_GET['data'] gebruiken?
Ik denk dat je mijn vraag niet goed snapt, want ik zou niet weten hoe dat zou moeten werken.
De bedoeling is dat ik op plek "A" (de telefoon) een PHP-script aanroep (dat op de NAS draait) waar ik als URL-parameter mijn data doorgeef.
Dus op de telefoon browse ik naar: http:\\IP-van-NAS\GeefDataDoor.php?data=HoiTest123
Vervolgens moet "iets" (ik denk de Websocket die ook op de NAS draait, dit is dan plek "B") deze data "oppakken" en doorsturen naar "C", namelijk de HTML die op de iPad geopend is (en ook op de NAS draait).
Dus de data gaat vanuit A, via B naar C.
@Rob: thanks!
Jou eerste idee had ik ook al bedacht. Niet zo mooi als jij het schetst, want ik wist niet van het bestaan van een RAM-disk. De reden dat ik mijn idee (met gewoon lompweg een bestandje wegschrijven + uitlezen) niet heb gedaan is inderdaad vanwege het "slechte" voor de hardddisk(s)!
Kan ik het niet maken door alleen maar gebruik te maken van Websockets? Dat bijvoorbeeld in dat script dat constant draait ook iets zit dat de data opvangt van GeefDataDoor.php?
Het protocol van een WebSocket is vrij complex. Dus waarom moeilijk doen als het ook makkelijk kan ;-)
In websockets.php voeg je dit toe:
<?php
//initialisatie
$port = 8085; //poort voor "interne communicatie"
print("Start listening on port $port\n");
$socket = socket_create_listen($port);
socket_set_nonblock($socket);
//in de while(true) loop:
if($client = socket_accept($socket)){ //we hebben een request
$request = socket_read($client,10000);
//dit is je rauwe request data
//afhankelijk van wat je opstuurt krijg je hier dus wat "overhead" mee
//doe een print($request) om te kijken hoe je je data hier weer uit peutert
}
?>
Vanaf je telefoon kun je nu twee dingen doen:
- Je roept je NAS op poort 8085 direct aan (als dat firewall technisch mag), dus http://IP-van-NAS:8085/websockets.php?data=HoiTest123 . In de $request uit bovenstaand stukje code vind je nu een complete HTTP request (inclusief headers) terug. Je moet de "data" er dus zelf nog uit peuteren.
- Je roept GeefDataDoor.php aan, en roept van daaruit websockets.php aan. Als je dat ook weer met een socket doet, dan bepaal je zelf het "protocol", en heb je dus geen last van "overhead" (de data die je meegeeft is de data die je aan de andere kant ontvangt).
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$length = strlen($data = $_GET['data'] ?? null);
socket_sendto($socket,$data,$lengt,0,'127.0.0.1',8085); //localhost als ze op dezelfde NAS draaien
socket_close($socket);
?>
De $request in de websockets.php zal nu precies de $data zijn die je hierboven doorgaf = direct bruikbaar.
Bedankt voor je uitbreide antwoord.
Het werkt al een beetje :-)
Met onderstaand script komen er geen foutmeldingen, maar zo te zien komt er ook niets aan bij het socket dat ik op de NAS laat draaien
via php -q websockets.php
<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$length = strlen($data = $_GET['data'] ?? null);
socket_sendto($socket,$data,$length,0,'192.168.1.202',8085); //localhost als ze op dezelfde NAS draaien
socket_close($socket);
echo $data;
?>
Warning: socket_write(): unable to write to socket [32]: Broken pipe in /volume1/web/websockets.php on line 48
Ook merk ik dat ik alleen de data kan sturen met bovenstaande link als de HTML pagina "luistert" naar het websocket.
Dit is het hele websocket.php script:
<?php
//initialisatie
$gestuurdeData = "";
$port2 = 8085; //poort voor "interne communicatie"
print("Start listening on port $port2\n");
$socket = socket_create_listen($port2);
socket_set_nonblock($socket);
$address = '192.168.1.202';
$port = 12345;
// Create WebSocket.
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($server, $address, $port);
socket_listen($server);
$client = socket_accept($server);
// Send WebSocket handshake headers.
$request = socket_read($client, 5000);
preg_match('#Sec-WebSocket-Key: (.*)\r\n#', $request, $matches);
$key = base64_encode(pack(
'H*',
sha1($matches[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
));
$headers = "HTTP/1.1 101 Switching Protocols\r\n";
$headers .= "Upgrade: websocket\r\n";
$headers .= "Connection: Upgrade\r\n";
$headers .= "Sec-WebSocket-Version: 13\r\n";
$headers .= "Sec-WebSocket-Accept: $key\r\n\r\n";
socket_write($client, $headers, strlen($headers));
// Send messages into WebSocket in a loop.
while (true) {
sleep(1);
$content = 'Now: ' . time();
$response = chr(129) . chr(strlen($content)) . $content . $gestuurdeData;
socket_write($client, $response);
//in de while(true) loop:
if($client2 = socket_accept($socket)){ //we hebben een request
$request = socket_read($client2,10000);
//dit is je rauwe request data
//afhankelijk van wat je opstuurt krijg je hier dus wat "overhead" mee
//doe een print($request) om te kijken hoe je je data hier weer uit peutert
$gestuurdeData = $request;
$message = "_test_" . $request;
$a = fopen("websocket_dump.txt", "a+");
fputs($a, $name . " / " . $message . "\r\n");
fclose($a);
print($request);
}
}
?>
Ik heb het idee dat ik er bijna ben, maar dit is toch nét iets te moeilijk voor me om dit alleen op te lossen.
Dus ik zou heel blij zijn als ik nog eens een beroep mocht doen op jou/jullie expertise.
Ik vermoed dat het aan het feit ligt dat ik vergeten was om de socket ook weer te sluiten (waardoor ie bij een volgende call nog open staat). Na regel 49 moet je daarvoor invoegen:
socket_close($client2);
(en daarvoor evt. nog een socket_write() als je nog wat terug wilt zeggen).
Dat ie alleen werkt als je "luistert" komt vermoedelijk omdat anders je while(true) loop niet (door) loopt. Waarschijnlijk geeft je socket_write dan een error/timeout. Kun je in de logs op je NAS iets terug vinden?
test123Socket created Connection established Message send successfully
En in het Putty-venster (NAS) staat dan:
Start listening on port 8085
test123PHP Warning: socket_write(): unable to write to socket [32]: Broken pipe in /volume1/web/websockets.php on line 50
De data ("test123") komt nu dus aan. Maar dus ook meteen een foutmelding erachteraan.
Het script "websockets.php" is nu zo:
<?php
//initialisatie
$gestuurdeData = "";
$port2 = 8085; //poort voor "interne communicatie"
print("Start listening on port $port2\n");
$socket = socket_create_listen($port2);
socket_set_nonblock($socket);
$address = '192.168.1.202';
$port = 12345;
// Create WebSocket.
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($server, $address, $port);
socket_listen($server);
$client = socket_accept($server);
// Send WebSocket handshake headers.
$request = socket_read($client, 5000);
preg_match('#Sec-WebSocket-Key: (.*)\r\n#', $request, $matches);
$key = base64_encode(pack(
'H*',
sha1($matches[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
));
$headers = "HTTP/1.1 101 Switching Protocols\r\n";
$headers .= "Upgrade: websocket\r\n";
$headers .= "Connection: Upgrade\r\n";
$headers .= "Sec-WebSocket-Version: 13\r\n";
$headers .= "Sec-WebSocket-Accept: $key\r\n\r\n";
socket_write($client, $headers, strlen($headers));
// Send messages into WebSocket in a loop.
while (true) {
sleep(1);
$content = 'Now: ' . time();
$response = chr(129) . chr(strlen($content)) . $content . $gestuurdeData;
socket_write($client, $response);
//in de while(true) loop:
if($client2 = socket_accept($socket)){ //we hebben een request
$request = socket_read($client2,10000);
//dit is je rauwe request data
//afhankelijk van wat je opstuurt krijg je hier dus wat "overhead" mee
//doe een print($request) om te kijken hoe je je data hier weer uit peutert
socket_write($client2, "hallo");
socket_close($client2);
$gestuurdeData = $request;
$message = "_test_" . $request;
$a = fopen("websocket_dump.txt", "a+");
fputs($a, $name . " / " . $message . "\r\n");
fclose($a);
print($request);
}
//http://192.168.1.202:8085/websockets.php?data=testData
}
?>
Er is voor dit:
Dat ie alleen werkt als je "luistert" komt vermoedelijk omdat anders je while(true) loop niet (door) loopt. Waarschijnlijk geeft je socket_write dan een error/timeout.
Niet een makkelijke oplossing te verzinnen? Of moet die code is in DIE loop zitten?
Die functie is inmiddels verouderd en afgeschreven. Ik raad met klem aan om MySQLi te gebruiken, of PDO. Maar MySQLi is het makkelijkste qua ombouw. Check wel even goed de manuals van php.net, want de notatie van die functies is in enkele gevallen afwijkend dan enkel een 'i' erbij te zetten.
Die foutmelding "on line 50" kan niet helemaal kloppen met de code die daaronder staat. Daar is regel 50 nl een commentaar regel. Kijk even op welke regel het precies fout gaat.
Bij mij werkt dit overigens gewoon (als ik even het WebSocket deel er uit laat. Ik krijg alleen de foutmelding "Undefined variable: name in ... on line 62" (en dat klopt - dat die niet bekend is).