fsockopen连接保持打开

时间:2017-01-17 20:19:32

标签: php node.js websocket

我有一个节点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服务器中删除连接的任何建议?

3 个答案:

答案 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(),但他们使用这种方式需要重新加工服务器和客户端。