Can I run a web server and WEBREPL in parallel? #13161
Replies: 3 comments 11 replies
-
I have microdot running in async mode and a socket client-server also in async mode with no problems.. I think what your describe is that one of your process is in sync mode .. (problably WEBREPL) . I saw somewhere that max port open by default is 5 but that's not your problem here... |
Beta Was this translation helpful? Give feedback.
-
MicroPython's webrepl is implemented with a special socket option. For example,
This socket option 20 is implemented in modsocket.c, line 503 #if MICROPY_PY_SOCKET_EVENTS
// level: SOL_SOCKET
// special "register callback" option
case 20: {
if (args[3] == mp_const_none) {
if (self->events_callback != MP_OBJ_NULL) {
socket_events_remove(self);
self->events_callback = MP_OBJ_NULL;
}
} else {
if (self->events_callback == MP_OBJ_NULL) {
socket_events_add(self);
}
self->events_callback = args[3];
}
break;
}
#endif this is to register a socket "readable" event callback on a socket. In "modsocket.c" line 119, the // Polls all registered sockets for readability and calls their callback if they are readable
void socket_events_handler(void) {
...
// Call the callbacks
for (socket_obj_t *s = socket_events_head; s != NULL; s = s->events_next) {
if (FD_ISSET(s->fd, &rfds)) {
mp_call_function_1_protected(s->events_callback, s);
}
}
... In mpconfigport.h we have these related definitions:
The ...
void mp_hal_delay_ms(uint32_t ms) {
uint64_t us = (uint64_t)ms * 1000ULL;
uint64_t dt;
uint64_t t0 = esp_timer_get_time();
for (;;) {
mp_handle_pending(true);
MICROPY_PY_SOCKET_EVENTS_HANDLER
MP_THREAD_GIL_EXIT();
uint64_t t1 = esp_timer_get_time();
dt = t1 - t0;
... So in order for webrepl to work properly, the Now, in which modules of the MicroPython system does the
There is at least one module that we can use in our script so that "modtime.c" line 119 ...
STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) {
#ifdef MICROPY_PY_TIME_CUSTOM_SLEEP
mp_time_sleep(seconds_o);
#else
#if MICROPY_PY_BUILTINS_FLOAT
mp_hal_delay_ms((mp_uint_t)(1000 * mp_obj_get_float(seconds_o)));
#else
mp_hal_delay_ms(1000 * mp_obj_get_int(seconds_o));
#endif
#endif
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_time_sleep_obj, time_sleep);
STATIC mp_obj_t time_sleep_ms(mp_obj_t arg) {
mp_int_t ms = mp_obj_get_int(arg);
if (ms >= 0) {
mp_hal_delay_ms(ms);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_time_sleep_ms_obj, time_sleep_ms);
... So calling When A few things to note:
Therefore, mixing a socket event based task like webrepl with standard blocking socket functions or an async/await program will prevent socket event callbacks from running properly. You can program useful network applications based on socket events, such as webrepl, telnet, ftpserver. Below is a simple program that allows a server to send logs to multiple logging clients. The import network
import socket as soc
import time
network.WLAN(network.AP_IF).active(False)
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.config(pm=0) # disable power management
wlan.connect("YOUR_SSID", "YOUR_PASSWD")
time.sleep(5)
print(wlan.ifconfig())
ip = wlan.ifconfig()[0]
clients = []
def _con_handler(srv):
global clients
cs,ca = srv.accept()
print('Serving:', ca)
clients.append(cs)
def LOG():
global ip
if ip != '0.0.0.0':
# start listening for telnet connections on port 2323
print("Starting,", ip, "listening on port 2323")
srv = soc.socket(soc.AF_INET, soc.SOCK_STREAM)
srv.setsockopt(soc.SOL_SOCKET, soc.SO_REUSEADDR, 1)
port = 2323 # default telnet port
ai = soc.getaddrinfo(ip, port)
addr = ai[0][4]
srv.bind(addr)
srv.listen(5) # cache 5 clients connection request
srv.setblocking(False)
# we are using socket event (read) callback!
srv.setsockopt(soc.SOL_SOCKET, 20, _con_handler)
return True
else:
print('No wifi, please try again')
return False
if LOG():
while True:
if len(clients) > 0:
print('#clients:', len(clients))
w = f'The time now is {time.time()}'
# w = input('==> ')
for cs in clients:
try:
cs.write(w); cs.write('\r\n')
except Exception as e:
print('Socket write error:', e)
print('Remove', cs, 'from list of clients')
clients.remove(cs)
print('Sleep 5 sec.')
time.sleep(5)
else:
print('No clients yet. Sleep 5 sec.')
time.sleep(5) You can then use a telnet client, for example, to test it:
Sorry for the lengthy explanation, but I hope this helps to clarify a bit why your setup is not working as expected, and at the same time we have learned a bit about this mysterious Option 20. |
Beta Was this translation helpful? Give feedback.
-
Tried that too, but the behavior largely stays the same. webrepl shows me the password dialog and then hangs. Subsequent connection attempts are rejected, ` WebREPL connection from: ('192.168.3.56', 60865) Concurrent WebREPL connection from ('192.168.3.56', 60870) rejected ` Need to read up more on what @shariltumin posted, and see if I can modify the example to use web servers instead of telnet servers. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I have tried many ways to run both a web server (on port 80) and webrepl (on port 8266). So far without success. The web server (doesn't matter if synchronous or asynchronous) always works, webrepl sometimes shows its page, sometimes lets me log in, but after that it either doesn't respond, or immediately disconnects. Tried to put webrepl.start before, after, in the middle of the web server code. All to no avail. If the web server is not started then webrepl runs just fine, including file transfers. Are the sockets conflicting? Is something blocking?
Beta Was this translation helpful? Give feedback.
All reactions