在 MicroPython 中使用轮询的非阻塞服务器套接字问题

时间:2020-12-31 09:27:14

标签: esp8266 micropython usocket

我正在尝试组装一个简单的温度计,该温度计可在 OLED 显示屏上提供温度,并使用 MicroPython 通过 ESP8266 上的 http 请求提供温度。

轮询对象已用于防止 websocket 阻塞循环(因此可以更新测量值和 OLED 显示)。< /p>

#CREATE SOCKET
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.bind(('', 80))
serverSocket.listen(5)

#REGISTER SOCKET TO THE POLLER
pollerObject = select.poll()
pollerObject.register(serverSocket, select.POLLIN)

#PERFORM FIRST MEASUREMENT AT STARTUP
last_meas_time = startup_time
sensor_readings = read_sensor()
print(sensor_readings)
display.display_measurement(str(temp),str(hum))

#LOOP FOREVER
while True:
    
    #ONE MEASUREMENT UPDATE EVERY 30s
    if(time.time() - last_meas_time >= 30):
        sensor_readings = read_sensor()
        print(sensor_readings)
        display.display_measurement(str(temp),str(hum))
        last_meas_time = time.time()
    
    #WAIT UP TO 10s FOR INCOMING CONNECTIONS
    fdVsEvent = pollerObject.poll(10000)
    
    for descriptor, Event in fdVsEvent:
        print()
        print("Got an incoming connection request")
        print("Start processing")
        # Do accept() on server socket or read from a client socket
        conn, addr = serverSocket.accept()
        print('Got a connection from %s' % str(addr))
        request = conn.recv(1024)
        print('Content = %s' % str(request))
        response = web_page()
        conn.send('HTTP/1.1 200 OK\n')
        conn.send('Content-Type: text/html\n')
        conn.send('Connection: close\n\n')
        conn.sendall(response)
        conn.close()

它似乎在一段时间内运行良好,但我发现了两个问题,非常感谢您的帮助:

  1. 即使我只连接到它一次,2 或 3 个请求在 shell 终端中显示为已收到,如下所示。为什么会发生这种情况,我该如何解决?是否可以让浏览器等待足够长的时间来发送第二个或第三个请求?
    MPY: soft reboot
    Connection successful
    ('192.168.1.74', '255.255.255.0', '192.168.1.1', '192.168.1.1')
    b'29.0,24.0'
    
    Got an incoming connection request
    Start processing
    Got a connection from ('192.168.1.64', 58581)
    Content = b'GET / HTTP/1.1\r\nHost: 192.168.1.74\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nDNT: 1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: pt-BR,pt;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,sv;q=0.5\r\n\r\n'
    
    Got an incoming connection request
    Start processing
    Got a connection from ('192.168.1.64', 58582)
    Content = b'GET /favicon.ico HTTP/1.1\r\nHost: 192.168.1.74\r\nConnection: keep-alive\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66\r\nDNT: 1\r\nAccept: image/webp,image/apng,image/*,*/*;q=0.8\r\nReferer: http://192.168.1.74/\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: pt-BR,pt;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,sv;q=0.5\r\n\r\n'
  1. 运行一段时间后,我将无法再连接到它,因为它不会响应。我的方法有什么明显的错误吗?这是我从控制台得到的:
    Got an incoming connection request
    Start processing
    Got a connection from ('192.168.1.64', 59158)
    Content = b'GET / HTTP/1.1\r\nHost: 192.168.1.74\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nDNT: 1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: pt-BR,pt;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,sv;q=0.5\r\n\r\n'
    
    Got an incoming connection request
    Start processing
    Got a connection from ('192.168.1.64', 59157)
    Content = b'GET /favicon.ico HTTP/1.1\r\nHost: 192.168.1.74\r\nConnection: keep-alive\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66\r\nDNT: 1\r\nAccept: image/webp,image/apng,image/*,*/*;q=0.8\r\nReferer: http://192.168.1.74/\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: pt-BR,pt;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,sv;q=0.5\r\n\r\n'
    
    Got an incoming connection request
    Start processing
    Got a connection from ('192.168.1.64', 59160)
    Content = b''
    Traceback (most recent call last):
      File "main.py", line 104, in 
    OSError: [Errno 104] ECONNRESET
    MicroPython v1.13 on 2020-09-11; ESP module with ESP8266
    Type "help()" for more information.
    >>> 

第 104 行对应于:

        conn.sendall(response)

谢谢!

1 个答案:

答案 0 :(得分:1)

<块引用>

即使我只连接到它一次,2 或 3 个请求在 shell 终端中显示为已收到,如下所示。为什么会发生这种情况,我该如何解决?是否可以让浏览器等待足够长的时间来发送第二个或第三个请求?

这取决于浏览器如何连接到您的服务器。浏览器可能正在寻找多个请求,或者浏览器具有连接到服务器的套接字的超时值。我没有任何网络知识,但看起来像是对不同信息的两个请求。该信息的处理方式应传递到 web_page()。您发送的似乎是整个网页,而不是它要查找的具体内容。

<块引用>

运行一段时间后,我将无法再连接到它,因为它不会响应。我的方法有什么明显的问题吗?

可能发生的情况是您有 socket.sendall() 阻止创建任何新套接字。另请注意,即使您已正确关闭套接字,该套接字仍可能有数据要发送。它已被标记为关闭,但操作系统可能尚未将其关闭。

您使用 select.poll() 走在正确的轨道上。乍一看,使用 serverSocket (select.poll) 注册您的 pollerObject 似乎可以处理未来的连接。这不是正在发生的事情。您只向 pollerObject 注册了一个套接字。 severSocket 正在为来自浏览器的传入连接获取 select.POLLIN 事件。您需要一种方法将 serverSocket 创建的新套接字添加/注册到 pollerObject,以便为其他套接字提供服务。

现在你在 micropython 中尝试做的最好的例子是制作类似于 Python 3 Selectors 中的选择器示例的东西。

import selectors
import socket

sel = selectors.DefaultSelector()

def accept(sock, mask):
    conn, addr = sock.accept()  # Should be ready
    print('accepted', conn, 'from', addr)
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)

def read(conn, mask):
    data = conn.recv(1000)  # Should be ready
    if data:
        print('echoing', repr(data), 'to', conn)
        conn.send(data)  # Hope it won't block
    else:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()

sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)

while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj, mask)

通常,您不必担心用 socket.send() 填充套接字传输缓冲区,但您应该处理它。现在,我会在 socket.sendall() 之前和之后放置一些调试打印,因为这将阻止/重试,直到所有数据都发送完毕。如果不是所有数据都已发送,则必须为写就绪事件注册套接字,并传递需要发送的剩余数据。这有点复杂。

Got an incoming connection request
Start processing
Got a connection from ('192.168.1.64', 59160)
Content = b''
Traceback (most recent call last):
  File "main.py", line 104, in 
OSError: [Errno 104] ECONNRESET
MicroPython v1.13 on 2020-09-11; ESP module with ESP8266
Type "help()" for more information.
>>> 

您在上面遇到的问题是您的套接字连接可能已超时。 TCP 让您知道连接已过期。您应该使用 try except else 子句来处理此问题。