无法在移动设备上打开 websocket

Posted

技术标签:

【中文标题】无法在移动设备上打开 websocket【英文标题】:Can't open websocket on mobile devices 【发布时间】:2016-12-09 04:46:17 【问题描述】:

我正在尝试设置一个 websocket,并在我的树莓派上运行服务器。下面的代码是根据我找到的 here 的一个例子稍微修改的。

我围绕这个示例构建了一个完整的网页,允许我控制 gpio 并将消息发送到插入 pi 的串行设备。该站点和此示例都可以在我的笔记本电脑上完美运行(Windows 10 使用 Chrome 或 Firefox)。

但是,当我从手机连接时(android 5.0.1 使用 Chrome for android)。它似乎永远不会打开套接字。在示例代码中,它只显示“消息到这里。

我的第一个想法是 android 上的 chrome 不支持 websockets,但我能够在该站点 http://www.websocket.org/echo.html 上连接和回显消息。所以看起来功能就在那里。

还有什么会阻止套接字打开?

pysocket.py

import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web

class WSHandler(tornado.websocket.WebSocketHandler):

  def check_origin(self, origin):
    return True

  def open(self):
    print 'New connection was opened'
    self.write_message("Welcome to my websocket!")

  def on_message(self, message):
    print 'Incoming message:', message
    self.write_message("You said: " + message)

  def on_close(self):
    print 'Connection was closed...'

application = tornado.web.Application([
  (r'/ws', WSHandler),
])

if __name__ == "__main__":
  http_server = tornado.httpserver.HTTPServer(application)
  http_server.listen(8888)
  tornado.ioloop.IOLoop.instance().start()

pysocket.php

<!doctype html>
<html>
  <head>
    <title>WebSockets with Python & Tornado</title>
    <meta charset="utf-8" />
    <style type="text/css">
      body 
        text-align: center;
        min-width: 500px;
      
    </style>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script>
      $(function()
        var ws;
        var logger = function(msg)
          var now = new Date();
          var sec = now.getSeconds();
          var min = now.getMinutes();
          var hr = now.getHours();
          $("#log").html($("#log").html() + "<br/>" + hr + ":" + min + ":" + sec + " ___ " +  msg);
          //$("#log").animate( scrollTop: $('#log')[0].scrollHeight, 100);
          $('#log').scrollTop($('#log')[0].scrollHeight);
        

        var sender = function() 
          var msg = $("#msg").val();
          if (msg.length > 0)
            ws.send(msg);
          $("#msg").val(msg);
        

        ws = new WebSocket("ws://raspberrypi-mike:8888/ws");
        ws.onmessage = function(evt) 

          logger(evt.data);
        ;
        ws.onclose = function(evt)  
          $("#log").text("Connection was closed..."); 
          $("#thebutton #msg").prop('disabled', true);
        ;
        ws.onopen = function(evt)  $("#log").text("Opening socket..."); ;

        $("#msg").keypress(function(event) 
          if (event.which == 13) 
             sender();
           
        );

        $("#thebutton").click(function()
          sender();
        );
      );
    </script>
  </head>

  <body>
    <h1>WebSockets with Python & Tornado</h1>
    <div id="log" style="overflow:scroll;width:500px; height:200px;background-color:#ffeeaa; margin:auto; text-align:left">Messages go here</div>

    <div style="margin:10px">
      <input type="text" id="msg" style="background:#fff;width:200px"/>
      <input type="button" id="thebutton" value="Send" />
    </div>

    <a href="http://lowpowerlab.com/blog/2013/01/17/raspberrypi-websockets-with-python-tornado/">www.LowPowerLab.com</a>
  </body>
</html>

【问题讨论】:

你试过用树莓派的IP代替raspberrypi-mike吗? 我没有。今晚我会试试的。我想,因为我的笔记本电脑可以连接主机名,所以它正在工作。我的手机需要 IP 而不是主机名有什么特别的原因吗? 用 ip 替换主机名确实解决了问题。现在我很好奇为什么我的手机浏览器无法解析主机名。 我已经添加了一个答案和解释,我认为正在发生的事情。 【参考方案1】:

添加了一些额外的代码

我建议使用 node.js:

var express = require("express");
var app = express();
var http = require("http").Server(app);
var path = require("path");
var io = require('socket.io')(http);
var SerialPort = require('serialport');
var gpio = require('rpio');

http.listen(3000);

var serialPort = new SerialPort.SerialPort("/dev/ttyAMA0", 
    baudrate: 115200,
    dataBits: 8,
    parity: "none",
    stopBits: 1,
    flowControl: false
);

io.on('connection', function(socket)
    console.log('Connected');
    socket.on("WriteSerial:get",function(data)
            var hex = new Buffer (data, "hex"); //be careful passing data
            writeSerial(serialPort, hex);
            io.emit("WriteSerial:response", "Data writen!");
    );
    socket.on("ReadGPIO:get",function(data)
            var input = readPin(data.pin); 
            io.emit("ReadGPIO:response", input); 
    ); 
    socket.on("WriteGPIO:get",function(data)
            writePin(data.pin, data.time); 
            io.emit("WriteGPIO:response", "Set!"); 
    ); 
    socket.on("unWriteGPIO:get",function(data)
            unwritePin(data); 
            io.emit("unWriteGPIO:response", "Set!"); 
    );        

app.use(express.static(path.join(__dirname, '/')));
app.get("/home",function(req,res,next)
    res.sendFile(path.join(__dirname + "/index.html"));
);

function writeSerial (port, data)   
    port.write(data, function(err) 
        if (err) 
            return console.log('Error on write: ', err.message);
         else 
            console.log('Data written: ' + data);
        
    );


function readPin(pin)
    rpio.open(pin, rpio.INPUT);
    var read = rpio.read(pin) ? 'high' : 'low';
    return read;


function writePin(pin, timeInMs)
    rpio.open(pin, rpio.OUTPUT, rpio.LOW);
    rpio.write(pin, rpio.HIGH);
    if (timeInMs > 0) 
        setTimeout(function()
            rpio.write(pin, rpio.LOW); 
        , timeInMs);
     //You can put 0 if You want it to be high until You shut it down 


function unWritePin(pin)
    if(readPin(pin) === 'high') 
        rpio.write(pin, rpio.LOW);
     else 
        console.log("Pin already low!");
    

确保您已安装正确版本的 node.js。如果不在终端中执行此操作:

sudo apt-get remove nodered && sudo apt-get remove nodejs nodejs-legacy && 卷曲-sLhttps://deb.nodesource.com/setup_4.x | sudo bash - && sudo apt-get install -y nodejs

在 '/home/pi/' 中创建一个文件夹 'server',将 server.js 添加到其中。添加我提供给 server.js 的代码。使用终端打开该文件夹:

cd /home/pi/server/

安装服务器上使用的所有模块之后:

sudo npm install express && sudo npm install http && sudo npm install path && sudo npm install socket.io && sudo npm install serialport --unsafe-perm && sudo npm install rpio --unsafe-perm

现在我们要做的就是创建客户端部分。在文件夹“/home/pi/server”中创建 index.html 文件并添加名为“js”的文件夹。在文件夹“js”中为客户端添加 socket.io.js,您可以在文件夹 '/home/pi/server/node_modules/socket.io/node_modules/socket.io-client/' 中找到.

将客户端的 socket.io.js 包含到您的 index.html 中,如下所示:

<script type="text/javascript" src="js/socket.io.js" /></script>

还将 main.js 文件添加到“js”文件夹中,您将在其中放置您的 javascript 代码并将其包含到 index.html:

<script type="text/javascript" src="js/main.js" /></script>
<script type="text/javascript" src="js/jquery.js" /></script>

我不会制作任何图形,但这里有一些 main.js 代码:

$(document).ready(function() 
    var socket = io.connect('http://your_ip_address_rpi:3000');

    $( "#myButton" ).click(function()
        io.emit("WriteSerial:get", $("#myTextHolder").val()); //"FAAF531C" this is string of hex, should be added some filter to pass error when char is not part of HEX!
    );
    $( "#myButton2" ).click(function()
        io.emit("WriteGPIO:get", "pin" : $("#myPinHolder").val(), "time" : $("#myTimeHolder").val())


要在 RPI 启动时运行服务器,请使用“sudo nano”编辑器在“exit 0”之前添加“sudo node /home/pi/server/server.js &”到“/etc/rc.local”。

它适用于任何设备。

【讨论】:

谢谢。我从未使用过 node.js,但我可以试一试。您能否详细说明它的不同之处在于它可以在我的手机上运行,​​而我的原始代码却不能? 我已经对其进行了测试,并且可以在我尝试过的所有移动设备上运行。 Node 具有更好的性能,具有处理 Web 请求和响应的内置库,因此您不需要单独的 Web 服务器或其他依赖项。正如我们所说的 Raspberry Pi 节点将保持 RPI 的稳定性。与 node 相比,PHP 使用起来“容易”,但我仍然建议在这种状态下使用 node。 我自己做了很多节点服务器,我个人更喜欢节点而不是 PHP。 还有一些功能您可以添加到节点服务器正在播放 mp3 文件,因此您可以制作诸如声音站(搜索节点模块播放声音)之类的东西,在 RPI 上添加图片以制作时间间隔或捕获许多mjpeg 图片在 1 秒内发布并像视频一样发布到客户端。如果您喜欢玩 RPI,这就是您的选择。【参考方案2】:

为了使设备的主机名能够跨网络工作,设备必须公布自己的主机名或仅响应 DNS 查询以获取自己的主机名。

无论 Raspberry Pi 使用什么实现,您的笔记本电脑都支持它,但您的手机不支持。

因此,为了能够连接,您需要在 JavaScript 代码中将主机名 raspberrypi-mike 更改为您的 Raspberry Pi 的 IP 地址。

【讨论】:

以上是关于无法在移动设备上打开 websocket的主要内容,如果未能解决你的问题,请参考以下文章

谷歌地图应用无法在移动设备上打开网页链接

Node wss Websocket 在桌面上工作,而不是在移动设备上

我无法在 iOS 移动设备上使用 Phonegap 打开弹出对话框

边栏在桌面默认打开,在移动设备上关闭

jQuery toggleClass 在移动设备上无法正常工作

Wordpress无法在移动电话中打开手机数据