0

我有一个向共享主机数据库发送HTTP GET请求的 NodeMCU (ESP8266) 板。为了避免路由器上的端口转发,我想出了一个解决方案,我的 NodeMCU 板会定期(每 5 秒)HTTP GET向数据库发送一个请求,以查看用户是否更改了设备状态。

void loop() 
{
   HTTPClient http; //Declare object of class HTTPClient
   String getData, Link;

   //HTTP GET REQUEST

   getData = "?device="+ deviceName ;
   Link = "http://.../getStatus.php" + getData;

   http.begin(Link); 

   int httpCode = http.GET(); //Send the request

   String payload = http.getString(); //Get the response from database

   if(payload=="ON")
   {
      digitalWrite(LEDPin, HIGH);//change pin status
   }
   else if(payload=="OFF")
   {
      digitalWrite(LEDPin, LOW);//change pin status
   }

   http.end(); 

   delay(5000);//Send GET request every 5 seconds
}  

用户通过网站更改设备状态(单击按钮),然后将设备状态保存在数据库中。当设备向HTTP GET服务器发送请求时,getStatus.php文件会查询数据库以获取当前设备状态并将响应发送回设备。

<?php

    $db = mysqli_connect(...);

    if(!empty($_GET['device']))
    {
        $device_name = $_GET['device'];

        $query_status = "SELECT status FROM devices WHERE device = ?";

        $stmt = mysqli_prepare($db, $query_status);
        if ($stmt === false) {
            error_log(mysqli_error($db));
            die("Sorry, there has been a software error");
        }
        $ok = mysqli_stmt_bind_param($stmt, "s", $device_name);
        if ($ok === false) {
            error_log(mysqli_stmt_error($db));
            die("Sorry, there has been a software error");
        }
        $ok = mysqli_stmt_execute($stmt);
        if ($ok === false) {
            error_log(mysqli_stmt_error($db));
            die("Sorry, there has been a software error");
        }

        $result = $stmt->get_result();

        if ($result->num_rows > 0) 
        {
            while($row = $result->fetch_assoc()) 
            {
                echo $row["status"];
            }
        } 
    }
?>

我试图找到一个解决方案,我不必每 5 秒查询一次数据库,而是仅在用户更改设备状态时查询。

所以我的问题是如何将HTTP GET响应推迟到用户通过单击按钮更改设备状态的那一刻?

4

3 回答 3

0

“这不是一个错误,这是一个功能。”

PHP 是专门为避免此类通知而设计的,并且有充分的理由:它们是极易受到攻击的攻击媒介。如果您在生产环境中执行此操作,我强烈建议您反对这种特定的数据流。从本质上讲,您需要做的是将客户端变成接受来自另一台服务器的连接的服务器......客户端服务器没有您的服务器(可能)具有的可靠安全层。

现在,这对于我们这些真正想要这样做的人来说很烦人。但是,在不牺牲您的安全性的情况下,有一个推荐的解决方法。PHP 几乎是网络上最安全的语言。它不会很快放弃这一点。而这种情况的标准解决方案是“使用另一种语言”。

您所做的是设置所谓的推送通知服务器。它的全部目标是为不太安全的数据传输提供一个安全的接收器中间人。您让您的数据库服务器与推送服务器进行通信(这可能是 nodejs 或类似于您的数据库服务器的简单 RESTapi)。并且您有一个您的客户端使用的 javascript,它仅侦听来自该推送服务器的哈希,该哈希与为他们的通知会话提供的哈希相对应。然后,当您的推送服务器被 ping 通时,它会发送无信息的十六进制哈希(为了安全起见,已清除任何非十六进制信息),此时您的前端客户端知道“嘿,我应该轮询服务器以获取更多信息。” 在这一点上,你可以让客户知道更新了什么。

于 2019-05-07T21:09:16.077 回答
0

您需要在设备和服务器之间建立实时连接。我认为套接字是一个很好的解决方案。你可以看到这篇文章:

https://blog.tommyku.com/blog/communication-between-cpp-and-php-program-using-socket/

寻求帮助。

但就我个人而言,我总是更喜欢简单快捷的方法,而且我的代码更改较少。

所以 :

而是参与复杂的套接字编程,您可以使用内存中的数据结构存储,例如https://redis.io/,它的响应速度比 Mysql 快,并且在您的服务器(Php 脚本)中没有任何严重的副作用,然后您就不需要了需要更改 NodeMCU(ESP8266) 代码但 PHP 代码才能使用 Redis。

于 2019-05-05T12:56:29.397 回答
0

我想建议您当前的解决方案很好

您的设备需要知道存储在远程数据库中的值。就设备而言,值随时可能发生变化,当远程值发生变化时,设备需要更新自己的状态。

让它充当从远程服务器接收任何类型的连接或通知的主机可能会出现问题。您提到您必须设置端口转发。您还将主机服务器的安全问题添加到此设备,并且您需要担心维护一致的 IP 地址(或其他东西)。

理想的解决方案将取决于要完成的工作。为什么不想每五秒轮询一次服务器?

当然你问的实际问题是

如何 [to] 将 HTTP GET 响应推迟到用户通过单击按钮更改设备状态的那一刻?

推迟响应不是一件正常的事情,而且你会遇到各种超时问题,我不打算在这里解决(为了简洁起见),而且很难得到你的系统同时做任何其他事情,但它可以做到:

客户端设备

static DateTime lastUpdated = now();//or whatever

void loop() 
{
  HTTPClient http; //Declare object of class HTTPClient
  String getData, Link, payload;
  bool succeeded;

  //HTTP GET REQUEST
  getData = "?device=" + deviceName + "&lastUpdated=" + lastUpdated.asEpochInt(); //or whatever
  Link = "http://.../getStatus.php" + getData;

  try
  { 
    http.begin(Link); 
    int httpCode = http.GET(); //Send the request
    payload = http.getString(); //Get the response from database
    succeed = true;
  }
  catch(TimeoutError te) //or whatever
  {
    succeeded = false;
  }

  if(succeeded)
  {
    lastUpdated = now()

    if(payload=="ON")
    {
      digitalWrite(LEDPin, HIGH);//change pin status
    }
    else if(payload=="OFF")
    {
      digitalWrite(LEDPin, LOW);//change pin status
    }
  }

  http.end(); 

  //delay(5000); //send the next one right away.
}  

主机服务器

<?php

$db = mysqli_connect(...);

if(!empty($_GET['device']))
{
  $device_name = $_GET['device'];
  $threshold = DateTimeImmutable::createFromFormat('U', $_GET['lastUpdated'])

  $query_status = "SELECT status FROM devices WHERE device = ? AND lastUpdated > ?";

  $stmt = mysqli_prepare($db, $query_status);
  if ($stmt === false) {
     error_log(mysqli_error($db));
     die("Sorry, there has been a software error");
  }

  $ok = mysqli_stmt_bind_param($stmt, "s", $device_name)
    && mysqli_stmt_bind_param($stmt, "s", $threshold->format('Y-m-d H:i:s'));
  if ($ok === false) {
    error_log(mysqli_stmt_error($db));
    die("Sorry, there has been a software error");
  }

  $waiting = TRUE;
  while($waiting)
  {
    $ok = mysqli_stmt_execute($stmt);
    if ($ok === false) {
      error_log(mysqli_stmt_error($db));
      die("Sorry, there has been a software error");
    }

    $result = $stmt->get_result();

    if ($result->num_rows > 0) 
    {
      $waiting = FALSE;
      while($row = $result->fetch_assoc()) 
      {
        echo $row["status"];
      }
    }
    else {
      usleep(100);
    }
  }
}//or else what?
?>

我们可以通过将 while 循环向下推到 MySQL 中来更进一步(使情况变得更糟),但这需要学习 MySQL 存储过程的细节,而且再多的互联网点也不值得。

仅仅因为你可以,并不意味着你应该。

于 2019-05-10T22:26:24.733 回答