我有一个节点websockets服务器,所有设置的聊天服务都很棒。但我希望LAMP服务器能够向连接到websocket服务器的用户发送定期消息。 (响应用户浏览器操作或cron作业)。所以我需要一些PHP代码来向节点服务器发送请求(两者都在Google Compute Engine上)。
我通过这个答案找到了:https://stackoverflow.com/a/22411059/947374 ...一个论坛的链接,其中有人得到了这个很好的解决方案:https://forum.ripple.com/viewtopic.php?f=2&t=6171&p=43313#p43313
我的PHP代码现在看起来像:
//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);
数据从PHP发送到节点。美丽。赞美主。
唯一的问题是连接似乎保持打开约60秒。数据立即从PHP发送到节点,网络聊天就像我想要的那样立即响应。但请求PHP脚本的浏览器选项卡会旋转60秒,并且在第一个请求完成之前不会再接受其他请求。
我原以为包含fclose()会使连接不会像这样保持打开状态。
我也尝试将标题更改为: 连接:关闭 和/或尝试过 GET / HTTP / 1.0 (根据具有类似问题的其他线程的一些建议)这也无济于事。
有关如何让PHP脚本立即关闭连接或让节点服务器在处理完请求后立即从LAMP服务器中删除连接的任何建议?
答案 0 :(得分:4)
你真的到了fclose()
吗?
如果您将代码置于阻止模式(http://php.net/manual/en/function.stream-set-blocking.php)并且不发送2000字节或fread()
,则代码可能会停留在eof
上 - 换句话说,fread将只是时间进行。
测试:
- 将echo 'Closing';
放在fclose
之前,这样你才能知道自己到了那里
- 添加stream_set_blocking ($sock, false);
- 缩短套接字超时(http://php.net/manual/en/function.stream-set-timeout.php)
小警告:如果设置非阻塞,则可能需要循环$headers = fread($sock, 2000);
,因为即使没有数据,它也会立即返回。创建一个while循环,循环不亮你手动计时,或者你得到你期望的响应。
答案 1 :(得分:2)
WebSockets是一种双向持久连接。在客户端明确关闭它或服务器执行连接之前,连接应保持打开状态。由于您没有发送关闭数据包(hybi10Encode
的第二个参数将是'close'
),服务器正在等待进一步的通信,然后在沉默后挂断您。
那么,你是怎么做到的?好吧,引用RFC 6455 § 7.1.1:
要关闭WebSocket连接,端点将关闭 底层TCP连接。端点应该使用一种方法 干净地关闭TCP连接,以及TLS会话,如果 适用,丢弃可能已经存在的任何尾随字节 接收。端点可以通过任何方式关闭连接 必要时可用,例如受到攻击时。
在大多数正常情况下,底层TCP连接应该关闭 首先由服务器,以便它保持TIME_WAIT状态而不是 客户端(因为这会阻止它重新打开2的连接 最大段寿命(2MSL),而没有相应的 服务器影响作为TIME_WAIT连接立即重新打开 具有更高seq数的新SYN)。在异常情况下(例如不是 在合理数量之后从服务器收到TCP关闭 时间)客户端可以启动TCP关闭。因此,当一个服务器 被指示关闭WebSocket连接它应该启动 TCP立即关闭,并指示客户端执行此操作 同样,它应该等待服务器的TCP关闭。
干净的关闭意味着这样做的最佳方式是发送close frame,并提供适当的状态代码,例如1000。
我怀疑因为你的第二个fread(..., 2000)
而没有发生。服务器没有发送2000个字节,所以fread正在等待它。您可能需要在那里进行分块读取,或者更具体的数据返回。我不会紧紧地旋转。
另外,请检查fclose
的返回代码。如果它失败了,那就表明需要调查一个问题(尽管我怀疑这是否会出现这种情况)。
我还建议在之后发送hybi10Encode(1000, 'close')
或某些此类您已成功fread
响应中所需的所有数据,以便根据RFC清除关闭连接
答案 2 :(得分:0)
你的问题非常简单,
$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);
你的读取4000字节,但是如果消息不是4000字节长的php将等到套接字超时发生,有2个选项1选项是你的节点服务器终止连接在它的结束然后php它将停止读。
第二个是全部阅读。使用feof()
,但他们使用这种方式需要重新加工服务器和客户端。