pycom / pycom-micropython-sigfox

A fork of MicroPython with the ESP32 port customized to run on Pycom's IoT multi-network modules.
MIT License
199 stars 167 forks source link

Bug with ussl and socket setblocking(False) #424

Open amstaff8 opened 4 years ago

amstaff8 commented 4 years ago

Firmware version: 1.20.0.rc13 [v1.9.4-94bb382] Pycom product: WiPy 3.0

Hello. I believe there is still a bug related to usocket and ussl libraries. If I use usocket for a connection (without use ussl to wrap it) I can use setblocking(False) and read() of usocket WITHOUT block the script

If I wrap the socket (with wrap_socket of ussl module) and use read() of usocket library then the read function block my script.

I read the documentation. The setblocking(False) of usocket is equivalent settimeout(0).

So I think that wrap_socket of ussl module force the timeout of the socket, so setblocking(False) is useless. Is that correct? How I can use setblocking(False) with ussl and wrap_socket? I use setblocking(False) and setblocking(True) several times in my script before call read(), so I think I cannot set the timeout in the wrap_socket and call It before my read operations. Because it will work only before a connection I think.

salal-m commented 4 years ago

@amstaff8 can you try to reproduce this with the latest firmware version (1.20.2.rc7)? During my testing the read() call behaved as it was supposed to based on setblocking().

amstaff8 commented 4 years ago

Hi

It doesn’t work I downloaded WiPy-1.20.2.rc7.tar.gz from here https://software.pycom.io/downloads/pybytes/WiPy.html but the bug is still there

If you use wrap_socket then socket.read() will hang even if you set setblocking(False).

PS: I use the method check_msg of mqtt library, I also tried using this self-made method:

def test_read(self):
    self.sock.setblocking(False)
    res = self.sock.read(1)

It freezes the execution of the script

amstaff8 commented 4 years ago

Hi,

I found a workaround:

r, , = select.select([self.sock], [], [],0) #timeout 0: it doesn't wait if r: res = self.sock.read(1)

With select.select I can check if there is a message without wait

Lukanite commented 3 years ago

Thanks so much for the workaround! Also running into this on gpy using LTE. A fix would be great, since it breaks umqttsimple's check_msg() for subscribes when using ssl.

Replacing check_msg with this works: def checkmsg(self): r, , _ = select.select([self.sock], [], [],0) if r: return self.wait_msg()

William04A commented 3 years ago

Just stumbled across this GitHub issue today and it solved my problem after a few hours digging around the web, and also abandoning the issue earlier since we couldn't figure it out. I couldn't get umqtt.simple2 to work, which I assume would fix this issue. However, it just gave me MQTTException: 30 (SSL exception) But with the fix suggested by @Lukanite, this worked like a charm with SSL. Can't thank you enough! ❤️

I used umqtt from micropython-lib, so I assume that this is an issue for MicroPython and not PyCom unless there is something with the MQTT implementation that specifically breaks it on PyCom devices.

nznobody commented 2 years ago

I've struggled a lot with Pycom + SSL + MQTT implementations. In the end I adapter the simple2 client to work somewhat reliably. It now uses asyncio and can be seen here: https://github.com/nznobody/micropython-umqtt.uasyncio2

Regarding this particular issue though, for those using the select solution be warned: this still has issues in my experience. In particular this is mentioned here: https://github.com/nznobody/micropython-umqtt.uasyncio2/blob/c93f993cd67cec528ec9db87769e9fc0e829149e/src/umqtt/uasyncio2.py#L92

I originally adapted the simple2 client to work (without adapting to asyncio) and the same fix applied. This is not particular to asyncio. A dirty but quick (but potentially dead-locking) fix is to not use select on the read and rely on socket timeout (which seems to work somewhat).

# In connect after SSL connected perform:
+ self.sock.settimeout(self.socket_timeout)
# remove the following from _read:
- self._sock_timeout(self.poller_r, self.socket_timeout)  # calls poll with | uselect.POLLIN