I've got a node websockets server all set up with a chat service working great. But I want a LAMP server to be able to send periodic messages to users connected to the websocket server. (Either in response to user browser actions or cron jobs). So I needed some PHP code for sending a request to the node server (both on Google Compute Engine).
I found via this answer: https://stackoverflow.com/a/22411059/947374 ...a link to a forum where someone got this pretty well worked out: https://forum.ripple.com/viewtopic.php?f=2&t=6171&p=43313#p43313
My PHP code now looks like:
//local address of node server
$host='XXX.XXX.XXX.XXX';
$port=80;
//location where THIS script is running FROM
$local="http://localhost";
//json the data to send
$data=json_encode($data);
//some very particular headers
$head = "GET / HTTP/1.1"."\r\n".
"Upgrade: WebSocket"."\r\n".
"Connection: Upgrade"."\r\n".
"Origin: $local"."\r\n".
"Host: $host"."\r\n".
"Sec-WebSocket-Version: 13"."\r\n".
"Sec-WebSocket-Key: asdasdaas76da7sd6asd6as7d"."\r\n".
"Content-Length: ".strlen($data)."\r\n"."\r\n";
//WebSocket handshake
$sock = fsockopen($host, $port, $errno, $errstr, 2);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
//see the second link above for what hybi10Encode() is doing
fwrite($sock, hybi10Encode($data)) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000);
fclose($sock);
The data gets sent from PHP to node. Beautiful. Praise the Lord.
Only problem is the connection appears to stay open for about 60 seconds. The data gets sent from PHP to node immediately, the webchat responds right away like I want it to. But the browser tab requesting the PHP script spins for those 60 seconds and won't take another request until the first has finished.
I had thought the inclusion of fclose() would make it so the connection doesn't stay open like this.
I also tried changing the headers to: Connection: Close AND/OR tried GET / HTTP/1.0 (As per some advice of other threads with similar issues) This didn't help either.
Any advice on how I can get the PHP script to close the connection immediately or have the node server drop the connection from the LAMP server as soon as it's done processing its request?
Do you actually get to fclose()?
The code could be stuck on fread() if you have it on blocking mode (http://php.net/manual/en/function.stream-set-blocking.php) and don't send 2000 bytes or eof- in otherwords fread will just time out.
To test:
- put echo 'Closing'; before fclose so you know you're getting there
- add stream_set_blocking ($sock, false);
- shorten the socket timeout (http://php.net/manual/en/function.stream-set-timeout.php)
Small warning: if you set non-blocking, you may need to loop around $headers = fread($sock, 2000); as that will return immediately even if no data present. Create a while loop that loops unlit you manually time it out, or you get the response you expect.
WebSockets is a two-way persistent connection. The connection is expected to stay open until either the client explicitly closes it, or the server does. As you're not sending a close packet (the second parameter to hybi10Encode would be 'close'), the server is waiting around for further communication then hanging up on you after silence.
So, how do you do this? Well, quoting RFC 6455 § 7.1.1:
To Close the WebSocket Connection, an endpoint closes the underlying TCP connection. An endpoint SHOULD use a method that cleanly closes the TCP connection, as well as the TLS session, if applicable, discarding any trailing bytes that may have been received. An endpoint MAY close the connection via any means available when necessary, such as when under attack.
The underlying TCP connection, in most normal cases, SHOULD be closed first by the server, so that it holds the TIME_WAIT state and not the client (as this would prevent it from re-opening the connection for 2 maximum segment lifetimes (2MSL), while there is no corresponding server impact as a TIME_WAIT connection is immediately reopened upon a new SYN with a higher seq number). In abnormal cases (such as not having received a TCP Close from the server after a reasonable amount of time) a client MAY initiate the TCP Close. As such, when a server is instructed to Close the WebSocket Connection it SHOULD initiate a TCP Close immediately, and when a client is instructed to do the same, it SHOULD wait for a TCP Close from the server.
The clean close there means the preferrable way of doing it is sending a close frame with an appropriate status code, like 1000.
I suspect that's not happening because of your second fread(..., 2000). The server hasn't sent you 2000 bytes, so fread is waiting for it. You may need to do a chunked read there, or something more specific to the data coming back. I would not spin in a tight loop.
Also, check the return code of fclose. If it's failing, that would indicate a problem to investigate (though I doubt that would be the case here).
I'd also suggest sending a hybi10Encode(1000, 'close') or some such after you have successfully fread all the data you want in the response to cleanly close the connection per the RFC.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With