Проблемы с неблокирующими серверными сокетами при использовании опроса в MicroPython
Я пытаюсь собрать простой термометр, который показывает температуру на OLED-дисплее, а также через HTTP-запросы на ESP8266 с использованием MicroPython.
Поллер Объект был использован для предотвращения WebSocket от блокирования петли (поэтому измерения и OLED - дисплей могут быть обновлены).
#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()
Кажется, какое-то время он работает нормально, но я обнаружил две проблемы, и я был бы признателен за вашу помощь:
- Несмотря на то, что я подключаюсь к нему только один раз, 2 или 3 запроса отображаются как полученные в терминале оболочки, как вы можете видеть ниже. Почему это происходит и как я могу с этим справиться? Может ли быть так, что браузер достаточно долго ждал, чтобы отправить второй или третий запрос?
MPY: мягкая перезагрузка Соединение успешно (192.168.1.74, 255.255.255.0, 192.168.1.1, 192.168.1.1) b'29,0,24,0 ' Получен входящий запрос на подключение Начать обработку Получил соединение от ('192.168.1.64', 58581) Content = b'GET / HTTP / 1.1 \ r \ nHost: 192.168.1.74 \ r \ nСоединение: keep-alive \ r \ nCache-Control: max-age= 0 \ r \ nDNT: 1 \ r \ nUpgrade-Insecure- Запросы: 1 \ r \ nАгент пользователя: Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36 (KHTML, как Gecko) Chrome / 87.0.4280.88 Safari / 537.36 Edg / 87.0.664.66 \ r \ nПринять: текст /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-Кодирование: 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 ' Получен входящий запрос на подключение Начать обработку Получил соединение от ('192.168.1.64', 58582) Content = b'GET /favicon.ico HTTP / 1.1 \ r \ nHost: 192.168.1.74 \ r \ nСоединение: keep-alive \ r \ nПрагма: no-cache \ r \ nCache-Control: no-cache \ r \ nПользователь -Агент: Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36 (KHTML, например, 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 '
- После некоторого времени работы я больше не смогу подключиться к нему, так как он не будет отвечать. Что-то явно не так с моим подходом? Вот что я получил из консоли:
Получен входящий запрос на подключение Начать обработку Получил соединение от ('192.168.1.64', 59158) Content = b'GET / HTTP / 1.1 \ r \ nHost: 192.168.1.74 \ r \ nСоединение: keep-alive \ r \ nCache-Control: max-age= 0 \ r \ nDNT: 1 \ r \ nUpgrade-Insecure- Запросы: 1 \ r \ nАгент пользователя: Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36 (KHTML, как Gecko) Chrome / 87.0.4280.88 Safari / 537.36 Edg / 87.0.664.66 \ r \ nПринять: текст /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-Кодирование: 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 ' Получен входящий запрос на подключение Начать обработку Получил соединение от ('192.168.1.64', 59157) Content = b'GET /favicon.ico HTTP / 1.1 \ r \ nHost: 192.168.1.74 \ r \ nСоединение: keep-alive \ r \ nПрагма: no-cache \ r \ nCache-Control: no-cache \ r \ nПользователь -Агент: Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36 (KHTML, например, 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 ' Получен входящий запрос на подключение Начать обработку Получил соединение от ('192.168.1.64', 59160) Content = b'' Отслеживание (последний вызов последний): Файл "main.py", строка 104, в OSError: [Errno 104] ECONNRESET MicroPython v1.13 от 11.09.2020; Модуль ESP с ESP8266 Для получения дополнительной информации введите «help()». >>>
Строка 104 соответствует:
conn.sendall(response)
Спасибо!
1 ответ
Несмотря на то, что я подключаюсь к нему только один раз, 2 или 3 запроса отображаются как полученные в терминале оболочки, как вы можете видеть ниже. Почему это происходит и как я могу с этим справиться? Может ли быть так, что браузер достаточно долго ждал, чтобы отправить второй или третий запрос?
Это зависит от того, как браузер подключается к вашему серверу. Может быть несколько запросов, которые ищет браузер, или у браузера есть значение тайм-аута для сокета, подключающегося к вашему серверу. У меня нет никаких веб-знаний, но похоже, что два запроса разной информации. Как эта информация обрабатывается, следует передать
web_page()
. Похоже, вы отправляете веб-страницу целиком, а не конкретный контент, который она ищет.
После некоторого времени работы я больше не смогу подключиться к нему, так как он не будет отвечать. Что-то явно не так с моим подходом?
Возможно, вы заблокировали создание любых новых сокетов. Также обратите внимание, что даже если вы правильно закрыли сокет, у сокета все еще могут быть данные для отправки. Он был отмечен как закрытый, но ОС, возможно, еще не закрыла его.
Вы на правильном пути, используя
select.poll()
. На первый взгляд кажется, что регистрация вашего с помощью (select.poll) будет обрабатывать будущие подключения. Это не то, что происходит. Вы регистрируете только один сокет. В
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.