python - 从 espduino 超时到 Flask

标签 python flask arduino

我在玩 esp8266使用 this library 为 Arduino 提供 WiFi .我已正确设置以 POST 到 Pushover以及 requestbin ,在调试输出和这些工具的接口(interface)之间,我可以验证请求数据是否正确发布,并且 esp8266/arduino 单元的响应状态代码正确显示 200。

我想测试设置的可靠性,所以我想我会转向 Flask。我在 for 循环中将 100 个请求从 espduino POST 到在 0.0.0.0:5000 上运行的 Flask 应用程序. POST 包含一个测试字符串(尝试粗略衡量简单的数据完整性,确保该字符串通过未损坏的字符串)以及正在发送的循环数(例如,第一个循环中的 0 ... 最后一个循环中的 99) . Flask 会跟踪这些请求并更新其输出以显示哪些请求正确通过,哪些没有通过。

除了一件事之外,设置运行良好:模块在每次请求后超时。每个 POST 似乎都有效,Flask 适本地更新输出,但 espduino 为每个请求花费整整 5 秒(默认超时)并说它得到了 0 的响应代码.

所以重申一下情况:我的 espduino 设置正确 POST 并从 requestb.in 和 pushover.net 获得 200 响应,但使用我的本地 Flask 服务器 POST 然后超时。

WHYT :

  • 确保 all form data is read by Flask -> 没有区别
  • 使用 gunicorn 代替内置的 Flask 服务器 -> 没有区别
  • 将响应内容类型明确更改为“text/html”-> 没有区别
  • 将响应内容类型更改为“application/json”-> 没有区别
  • 添加 ; charset=utf-8到内容类型 -> 没有区别
  • 从索引 / 更改 Flask api 端点至 /api -> 没有区别
  • 更改端口 -> 没有区别
  • 验证防火墙已关闭(托管 Flask 应用程序的 OS X 10.10.5)-> 没有区别
  • nc -l 5000并检查来自模块的传入帖子(看起来不错)
  • 从 Postman 发布并 curl 到 Flask 应用程序以检查响应(并与来自 Requestb.in/有效站点的响应进行比较)
  • 从 test_string 中删除了特殊字符
  • 更改 WSGIRequestHandler.protocol_versionHTTP/1.1 -> 没有区别

  • 我花了几天的时间来研究这个和 not made much progress .我今天最大的突破是,如果我将我的 gunicorn/Flask 设置放在 nginx 之后,我可以成功发布并获得 200 条回复,而没有对 espduino 代码进行任何更改,所以我确定 Flask 正在或没有做一些事情(我担心这可能是 espduino 对 IP 地址与域名的处理,但我认为这排除了这一点)。

    我尝试过的设置摘要:
  • POST 到 requestb.in -> POST 有效,200 响应,无超时
  • POST 到 pushover.net -> POST 有效,200 响应,无超时
  • POST 到本地 Flask 服务器 -> POST 有效,无响应,超时
  • GET 到本地 Flask 服务器 -> 无响应,超时 (未测试是否获取数据)
  • POST 到本地 gunicorn 服务 Flask 应用程序 -> POST 有效,无响应,超时
  • POST 到本地 nginx 服务 gunicorn/Flask -> POST 有效,200 响应,无超时
  • POST 到本地 gunicorn 服务 httpbin -> 无响应,超时
  • POST到本地cherrypy服务器-> 无响应,超时

  • 当前代码:
    from flask import Flask, request, make_response
    from datetime import datetime
    
    app = Flask(__name__)
    
    ESPDUINO_IP = 'XXX.XXX.XXX.XXX'
    INCOMING_TEST_STRING = 'This is my espduino test string!'
    incoming_requests = []
    
    with open('results.txt', 'w') as f:
        f.write("Test run start: {}\n".format(datetime.now()))
    
    @app.route('/api', methods=['GET', 'POST'])
    def count_requests():
        if request.method == 'POST':
            form = request.form
            incoming_ip = request.remote_addr
            if incoming_ip == ESPDUINO_IP and form['test_string'] == INCOMING_TEST_STRING:
                test_num = int(form['test_num'])
                incoming_requests.append(test_num)
                msg = "All is peachy!"
                with open('results.txt', 'a') as f:
                    f.write("{:02d}: {}\n".format(test_num, datetime.now()))
        else:
            msg = "Hey, you're not the espduino!<br>"
            msg += str(len(incoming_requests)) + " requests so far.<br>"
            missing = set(range(100)) - set(incoming_requests)
            msg += "Missing: {}<br>".format(', '.join(map(str, missing)) if missing else "None!")
            msg += '<br>'.join(map(str, incoming_requests))
    
        resp = make_response('{"this": "that"}')
        resp.headers['Content-Type'] = "application/json"
        return resp
        # return "<html><body>{}</body></html>".format(msg)
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', debug=True)
    

    这是来自 espduino 的 POST 的样子:
    $ nc -l 5000
    POST /api HTTP/1.1
    Host: XXX.XXX.XXX.XXX
    Content-Length: 55
    Connection: close
    Content-Type: application/x-www-form-urlencoded
    User-Agent: ESPDRUINO@tuanpm
    
    test_string=This is my espduino test string!&test_num=0
    

    curl -X POST -d 'test_string=This is my espduino test string!&test_num=0' localhost:5000/api 相比:
    $ nc -l 5000
    POST /api HTTP/1.1
    Host: localhost:5000
    User-Agent: curl/7.43.0
    Accept: */*
    Content-Length: 55
    Content-Type: application/x-www-form-urlencoded
    
    test_string=This is my espduino test string!&test_num=0
    

    很想听听关于可能发生的事情的任何想法。我想知道这是否可能是 WSGI 问题?

    2015 年 8 月 31 日更新:

    我仍然没有弄清楚这一点,但这绝对不是 Flask 特有的问题。正如我上面提到的,我还使用 CherryPy 以及 python3 -m http.server --bind 0.0.0.0 5000 复制了超时。 (并将 espduino 代码更改为 GET / ),以及 ruby -run -e httpd .我仍然不明白为什么 nginx、requestbin 等可以毫无问题地提供服务。

    为了回应@Miguel 关于 HOST header 没有端口的评论,我正在 fork 和构建固件来改变这一点,但与此同时,我将客户端主机和端口硬编码到一个小的 HTTP 服务器脚本中,没有运气。
    from http.server import BaseHTTPRequestHandler, HTTPServer
    
    class MyServer(BaseHTTPRequestHandler):
        # protocol_version = 'HTTP/1.1'
        # close_connection = True
        def _set_headers(self):
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
    
        def do_GET(self):
            self._set_headers()
            self.wfile.write(b"<html><body><h1>hi!</h1></body></html>")
    
        def do_HEAD(self):
            self._set_headers()
    
        def do_POST(self):
            self.client_address = ("192.168.0.4", 5000)
            self._set_headers()
            self.wfile.write(b"<html><body><h1>POST!</h1></body></html>")
            # import pdb; pdb.set_trace()
    
    def run(server_class=HTTPServer, handler_class=MyServer, port=5000):
        server_address = ('0.0.0.0', port)
        httpd = server_class(server_address, handler_class)
        print('Starting httpd...')
        httpd.serve_forever()
    
    if __name__ == "__main__":
        run()
    

    查看 tcpdump 以查看是否可以找到工作(nginx)和非工作网络数据之间的任何差异。到目前为止还没有找到任何东西,但我也是这个工具的新手。

    2015 年 9 月 8 日更新

    仍然没有弄清楚这一点,但看起来 nginx 和 Python 服务器之间的 tcpdump 明显不同。这是一个 POST 和响应示例——我已将 IP 替换为 ESPDUINO_IPOSX_IP为了清楚起见,并清理了周围的 ACK 调用等。我需要研究为什么 Python 响应会被那条奇怪的线打断——我检查了 10 多个连续的 POST/响应对,以及 每一个的 Python 响应被这样中断(在完全相同的地方),和 nginx 的响应是,所以我想知道这是否可能是问题所在。 (此外,正如您所看到的,在这一轮测试中,我将响应正文更改为文本而不是 JSON——结果没有变化。)

    nginx(工作)
    POST /api HTTP/1.1
    Host: OSX_IP
    Content-Length: 29
    Connection: close
    Content-Type: application/x-www-form-urlencoded; charset=utf-8
    User-Agent: espduino@n8henrie
    
    test_string=simple&test_num=0
    
    
    09:16:04.079291 IP OSX_IP.commplex-main > ESPDUINO_IP.49146: Flags [P.], seq 1:183, ack 211, win 65535, length 182
    
    HTTP/1.1 200 OK
    Server: nginx/1.8.0
    Date: Mon, 31 Aug 2015 15:16:04 GMT
    Content-Type: text/html; charset=utf-8
    Content-Length: 26
    Connection: close
    
    <html><body></body></html>
    

    flask (超时)
    POST /api HTTP/1.1
    Host: OSX_IP
    Content-Length: 29
    Connection: close
    Content-Type: application/x-www-form-urlencoded; charset=utf-8
    User-Agent: espduino@n8henrie
    
    test_string=simple&test_num=3
    
    09:00:19.424086 IP OSX_IP.commplex-main > ESPDUINO_IP.48931: Flags [P.], seq 1:18, ack 211, win 65535, length 17
    
    HTTP/1.0 200 OK
    
    09:00:36.382125 IP OSX_IP.commplex-main > ESPDUINO_IP.48931: Flags [FP.], seq 18:181, ack 211, win 65535, length 163
    E....F@.@..,...e.......#...k..S.P.......Content-Type: text/html; charset=utf-8
    Content-Length: 26
    Server: Werkzeug/0.10.4 Python/3.4.3
    Date: Mon, 31 Aug 2015 15:00:36 GMT
    
    <html><body></body></html>
    

    在我看来,Python 出于某种原因将响应分成两半,例如length 17的一部分和另一个 length 163 , 与 nginx 的单一响应 length 182 相比.

    2015 年 9 月 10 日更新

    有趣的是,如果我通过 mitmproxy 运行它,一切都按预期运行——甚至直接运行到没有 nginx 或 gunicorn 的 Flask 应用程序。一旦我删除了 mitmproxy,它就会回到上面的超时状态。

    最佳答案

    仍然没有解决问题,但我想我可能已经弄清楚是什么原因造成的。毕竟不是 Flask 的问题。

    不幸的是,这似乎是 esp_bridge 的一个错误。 espduino 在 esp8266 中使用其固件的库。请原谅可能不正确的术语,但据我所知,出于某种原因,它似乎没有加入 TCP 数据包。产生被拆分为单独 TCP 数据包(例如 Flask)的 HTTP 响应的服务器出现故障,而 tcpdump 可以验证 nginx 和 mitmproxy 是否正在加入拆分的 TCP 数据包并在单个数据包中返回响应,这就是它们工作的原因.

    https://github.com/tuanpmt/esp_bridge/issues/10

    更新 20160128

    我今天重温了这个问题和 found a workaround .虽然理想的解决方案是修复 esp_bridge重新组装多包响应,只要响应非常小,就可以强制 Flask 将响应写入单个数据包。

    from werkzeug.serving import WSGIRequestHandler
    
    # Your Flask code here...
    
    if __name__ == "__main__":
        WSGIRequestHandler.wbufsize = -1
        app.run()
    

    关于python - 从 espduino 超时到 Flask,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32283135/

    相关文章:

    Python 循环 : Precise way to handle mapping matching lists

    python - 为 python 包制作可选的 C 扩展的最简单方法是什么?

    python - Flask、Postman 和 Mysql 多字段插入问题

    c++ - 函数指针数组初始化

    Python cStringIO 在编写上比 StringIO 花费更多的时间(字符串方法的性能)

    python - BeautifulSoup XML 仅打印第一行

    postgresql - 如何使用 Flask 模型在 postgres 中存储图像

    python - 使用 Flask Server 在网页中显示从 Google Firebase 导入的 json 数据

    c++ - 如何通过串行发送 float

    c++ - Arduino 从单个数字输入读取脉宽频率和占空比