远程服务器无法连接WebSocket

4
我有一个网页用于控制树莓派上的恒温器,但是当尝试从远程客户端使用Websockets时遇到了困难。在局域网中似乎工作正常。显然我漏掉了一些东西(可能是基本的东西),但我似乎无法弄清楚是什么。
树莓派的本地IP为192.168.1.134。网页(由Apache服务器提供)的URL为http://192.168.1.134:8010/thermostat.html。该页面启动了一些JavaScript,然后尝试通过ws://192.168.1.134:9000使用Websockets连接到pi的主程序。(pi上的服务器正在运行libwebsockets)。Websocket已经启动,似乎工作正常。然后我尝试从远程客户端(关闭WiFi的手机)连接http:\\23.239.99.99:8010\thermostat.html。HTML/JS文件加载正常,但Websocket尝试连接到URI ws:\\23.239.99.99:9000,这导致失败。
据我所知,NAT似乎已正确配置:
name           ext     ext    protocol    int     int     ip addr     interface
               port    port               port    port
               start   end                start   end

Thermostat3     8010   8010   TCP        8010   8010    192.168.1.134   eth3.1  
Thermostat5     8000   8000   TCP/UDP    80     80      192.168.1.134   eth3.1  
Thermostat_ws   9000   9000   TCP/UDP    9000   9000    192.168.1.134   eth3.1  

我检查过路由器和调制解调器,都没有设置任何防火墙。树莓派里也没有安装防火墙(我检查过,没有奇怪的iptables规则)。有人知道我错过了什么吗?

--- 编辑 ---

我还是卡在这个问题上了。我联系了我的ISP,他们保证他们的服务器上没有防火墙。有没有办法确定端口9000被谁阻止了?


1
两个想法:你确定ws连接使用外部IP吗?(比如它没有在你的HTML中硬编码并且没有被更新?)更有用的是-尝试使用端口443,因为它通常比其他端口“更开放”(但考虑到8010正在工作,这可能有些牵强)。 - GregHNZ
抱歉,我离开了几天,回复比较慢。这两个观点都是正确的。js代码使用ws_uri += "//" + window.location.host.replace(/\:.*$/, '') + ":9000";作为uri,我将其打印出来,它显示正确。我尝试使用443端口,但它具有相同的行为(从局域网中可以工作,但从广域网中不行...)。 - user2766918
遇到了同样的问题。当远程连接到WebSocket时,我收到了“500 handshakefailed”错误。已经寻找解决方案两周了,但没有人提出建议。 - Terry
3个回答

2
将您的Apache服务器绑定到0.0.0.0地址,以使其可以从远程计算机访问。

它可以从其他机器访问(即通过局域网连接的任何计算机),但不能从任何跨越路由器的设备访问。理论上,路由器应该将外部访问进行NAT处理,以便看起来请求是来自192.168.1.134,因此Apache不应该能够看到区别... - user2766918

1

尝试使用此工具确定端口是否无法访问(使用自定义端口):http://www.whatsmyip.org/port-scanner/

其他一切看起来都很好。作为一个合理性检查,我建议将ws端口设置为8010,以查看是否有效。我还建议使用类似Advanced Web Client的工具来隔离网络问题。


1
这很有趣。我曾经遇到过类似的问题。我设置了一个WebSocket(我使用了nodejs ws),但是一旦我试图从远程客户端访问它,我无法使用ws://yourip:port连接它,而必须使用http://yourip:port。我不知道你是否有相同的问题,我的问题是由我使用的代理引起的。

我还有一个建议,或许可以帮助你解决问题。我不知道你对安全性有多关注,但是根据我的理解,你基本上是通过WebSocket连接到你的树莓派,并告诉它改变温度。
当我做类似的项目时,我发现很难保护我的WebSocket连接。基本上我通过WebSocket发送了一个密码和命令到我的服务器,然后检查密码是否正确。否则任何人都可以加热你的房子。不酷...
因此,我不得不通过https隧道传输连接,以防止中间件攻击。
我很快就放弃了这个方案,决定采用完全不同的解决方案。基本上,我设置了一个nodejs express服务器(可以轻松配置自签名证书以使用https或在nginx / apache https服务器后使用),并使用用户名和密码进行身份验证。当有人通过/api/thermostats?id=0进行POST请求时,服务器会检查用户是否已经认证,然后从节点内部执行终端命令。
也许这个想法也符合你的要求。


是的,这就是我计划今后要做的事情。网页将通过https/wss提供服务,用户默认可以查看状态,但为了更改温度,js应用程序必须发送其凭据。这些凭据可以通过密码验证生成,也可以存储/加载在设备上的cookie中。 - user2766918

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接