micropython中给socket设置回调

我一直在寻找不让 socket 套接字阻塞 micropython 主进程的方法。通常的做法是启动一个socket套接字专属的线程来 acceptrecv 因为有些开发版(例如esp8266)是不支持多线程的, 而且一直挂着个线程对于某些需要低功耗的场景来说显然是不明智的。

于是我在 webrepl 的代码中找到了这个:

listen_s.setsockopt(socket.SOL_SOCKET, 20, accept_handler)
micropython/webrepl.py at 4d9e657f0ee881f4a41093ab89ec91d03613744d · micropython/micropython (github.com)

webrepl给socket对象绑定了一个回调函数, 我一直好奇这个 20 是什么意思, 所以我一直查啊查, 一直找不到结果, 直到我给开发组提交了 issue

How do you make webrepl automatically listen to socket requests in the background without blocking? · Issue #7375 · micropython/micropython (github.com)

简单地翻译过来就说说, 这个 20 的值是 micropython开发组自己定义的, 主要的功能就是给 socket 套接字绑定一个回调函数, 类似于 select, 在套接字变为可读状态的时候便会触发这一个函数(对于主socket套接字而言, 可读意味着可被accept), 当这个 callback 函数被调用的时候, 会打断现行的操作, 而且你可以使用全部的单片机资源(内存等等), 当这个 callback 返回之后, 开发版会自动回到之前的工作中, 所以理论上你这个 callback 的执行时间不应该太久, 而且不可能死循环住.作者还同时给了一个例子:

robert-hh/FTP-Server-for-ESP8266-ESP32-and-PYBD: Small FTP server for ESP8266/ESP32/PYBD on the MicroPython platform (github.com)

所以, 我给大家写了一个例子:

import socket, time

def accept_handler (sck:socket.socket):
    """
    When a request come in and wait to accpet, 
    this function will be call to accept the request.
    """
    client, addr = sck.accept ()
    client.setsockopt(socket.SOL_SOCKET, 20, process_handler)
    # set a readable callback

def process_handler (sck:socket.socket):
    """
    When the client sent a data, 
    this will be call to recv the data.
    """
    # recv the data and send it bcak.
    data = sck.recv (1024)
    sck.send (data)

if __name__ == "__main__":
    server = socket.socket (socket.AF_INET,socket.SOCK_STREAM)
    server.setsockopt (socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) # tcp quick re-use.
    server.setsockopt(socket.SOL_SOCKET, 20, accept_handler)
    server.listen (5)

    try:
        while True:
            time.sleep (1)
    except Exception:
        server.close ()

这个例子将会在接受到请求时自动接受请求, 并给他设置一个回调函数.

被设置的回调函数将会在客户发送数据的时候被调用, 用来读取数据, 我们这里把客户端发送来的数据原路返回.

Leave a Reply