usocket – 套接字模块

    该模块实现了相应CPython模块的子集,如下所述。有关更多信息,请参阅原始CPython文档: socket.

    该模块提供对BSD套接字接口的访问

    与CPython的区别

    为了提高效率和一致性,MicroPython中的套接字对象直接实现了stream(类文件)接口。在CPython中,您需要使用makefile()方法将套接字转换为类文件对象。 MicroPython仍支持此方法(但是无操作),因此在与CPython兼容的情况下,请务必使用它。

    套接字地址格式

    usocket模块的本机套接字地址格式是getaddrinfo函数返回的不透明数据类型,必须用它来解析文本地址(包括数字地址):

    sockaddr = usocket.getaddrinfo('www.micropython.org', 80)[0][-1]
    # You must use getaddrinfo() even for numeric addresses
    sockaddr = usocket.getaddrinfo('127.0.0.1', 80)[0][-1]
    # Now you can use that address
    sock.connect(addr)
    

    使用getaddrinfo是最有效的(在内存和处理能力方面),而且也是使用地址的可移植方式。

    但是,socket模块(注意与此处描述的本机MicroPythonusocket模块的区别)提供了与CPython兼容的方式来使用元组指定地址,如下所述。请注意,取决于MicroPython端口,可以在内置或需要从micropython-lib安装套接字模块(如“MicroPython Unix端口”的情况),并且某些端口仍然只接受元组中的数字地址格式,并要求使用getaddrinfo函数来解析域名。

    总的来说:

    • 编写便携式应用程序时始终使用getaddrinfo
    • 如果您的端口支持快速黑客和交互式使用,则下面描述的元组地址可用作快捷方式。

    socket模块的元组地址格式:

    • IPv4:(ipv4_address,port),其中ipv4_address是带有点符号数字IPv4地址的字符串,例如, “8.8.8.8”,端口号和整数端口号在1-65535范围内。请注意,域名不被接受为ipv4_address,应首先使用usocket.getaddrinfo()解析它们。

    • IPv6:(ipv6_address,port,flowinfo,scopeid),其中ipv6_address是带冒号数字IPv6地址的字符串,例如: “2001:db8 :: 1”,port是1-65535范围内的整数端口号。 flowinfo必须为0. scopeid是链路本地地址的接口范围标识符。请注意,域名不被接受为ipv6_address,应首先使用usocket.getaddrinfo()解析它们。 IPv6支持的可用性取决于MicroPython端口

    方法

    usocket.socket(af=AF_INET, type=SOCK_STREAM, proto=IPPROTO_TCP)

    使用给定的地址系列,套接字类型和协议号创建一个新套接字。请注意,在大多数情况下不需要指定proto(不推荐使用,因为一些MicroPython端口可能会省略IPPROTO_ *常量)。相反,type参数将自动选择所需的协议:

    # Create STREAM TCP socket
    socket(AF_INET, SOCK_STREAM)
    # Create DGRAM UDP socket
    socket(AF_INET, SOCK_DGRAM)
    

    usocket.getaddrinfo(host, port, af=0, type=0, proto=0, flags=0)

    将 host / port 参数转换为5元组序列,其中包含用于创建连接到该服务的套接字的所有必要参数。参数af,type和proto(与socket()函数具有相同的含义)可用于过滤返回哪种地址。如果未指定参数或为零,则可以返回所有地址组合(需要在用户端进行过滤)。

    生成的5元组列表具有以下结构:

    (family, type, proto, canonname, sockaddr)
    

    以下示例显示如何连接到给定的URL:

    s = usocket.socket()
    # This assumes that if "type" is not specified, an address for
    # SOCK_STREAM will be returned, which may be not true
    s.connect(usocket.getaddrinfo('www.micropython.org', 80)[0][-1])
    

    建议使用过滤参数:

    s = usocket.socket()
    # Guaranteed to return an address which can be connect'ed to for
    # stream operation.
    s.connect(usocket.getaddrinfo('www.micropython.org', 80, 0, SOCK_STREAM)[0][-1])
    

    与CPython的区别

    如果此函数出错,CPython会引发socket.gaierror异常(OSError子类)。 MicroPython没有socket.gaierror并直接引发OSError。请注意,getaddrinfo()的错误号形成一个单独的命名空间,可能与uerrno模块中的错误号不匹配。为了区分getaddrinfo()错误,它们用负数表示,而标准系统错误是正数(错误号可以使用来自异常对象的e.args [0]属性访问)。使用负值是临时细节,可能在将来发生变化。

    usocket.inet_ntop(af, bin_addr)

    将给定地址族af的二进制网络地址bin_addr转换为文本表示:

    >>> usocket.inet_ntop(usocket.AF_INET, b"\x7f\0\0\1")
    '127.0.0.1'
    

    usocket.inet_pton(af, txt_addr)

    将给定地址族af的文本网络地址txt_addr转换为二进制表示:

    >>> usocket.inet_pton(usocket.AF_INET, "1.2.3.4")
    b'\x01\x02\x03\x04'
    

    常量

    usocket.AF_INET usocket.AF_INET6

    解决家庭类型。可用性取决于特定的MicroPython端口

    usocket.SOCK_STREAM usocket.SOCK_DGRAM

    套接字类型。

    usocket.IPPROTO_UDP usocket.IPPROTO_TCP

    IP协议号。可用性取决于特定的MicroPython端口。注意,在调用usocket.socket()时不需要指定它们,因为SOCK_STREAM套接字类型会自动选择IPPROTO_TCPSOCK_DGRAM - IPPROTO_UDP。因此,这些常量的唯一实际用途是作为setsockopt()的参数。

    usocket.SOL_*

    套接字选项级别(setsockopt()的参数)。确切的库存取决于MicroPython端口

    usocket.SO_*

    套接字选项(setsockopt()的参数)。确切的库存取决于MicroPython端口

    类 socket

    方法

    socket.close()

    标记套接字已关闭并释放所有资源。一旦发生这种情况,套接字对象上的所有未来操作都将失败。如果协议支持,远程端将接收EOF指示。

    套接字在被垃圾收集时会自动关闭,但建议你在完成它们之后立即“关闭”它们。

    (maixpy 未实现)socket.bind(address)

    将套接字绑定到地址。套接字必须尚未绑定。

    (maixpy 未实现)socket.listen([backlog])

    使服务器接受连接。如果指定了积压,则必须至少为0(如果低,则将其设置为0);并指定在拒绝新连接之前系统将允许的未接受连接数。如果未指定,则选择默认的合理值。

    (maixpy 未实现)socket.accept()

    接受连接。套接字必须绑定到一个地址并侦听连接。返回值是一对(conn,address),其中conn是可用于在连接上发送和接收数据的新套接字对象,address是绑定到连接另一端的套接字的地址。

    socket.connect(address)

    连接到地址处的远程套接字。

    socket.send(bytes)

    将数据发送到套接字。套接字必须连接到远程套接字。返回发送的字节数,可能小于数据长度(“短写”)。

    socket.sendall(bytes)

    将所有数据发送到套接字。套接字必须连接到远程套接字。与send()不同,此方法将尝试通过连续发送数据块来发送所有数据。

    此方法在非阻塞套接字上的行为未定义。因此,在MicroPython上,建议使用write()方法,它具有相同的“无短写入”策略来阻塞套接字,并将返回在非阻塞套接字上发送的字节数。

    socket.recv(bufsize)

    从套接字接收数据。返回值是表示接收数据的字节对象。一次接收的最大数据量由bufsize指定。

    socket.sendto(bytes, address)

    将数据发送到套接字。套接字不应连接到远程套接字,因为目标套接字由地址指定。

    socket.recvfrom(bufsize)

    从套接字接收数据。返回值是一对(字节,地址),其中bytes是表示接收数据的字节对象,address是发送数据的套接字的地址。

    socket.setsockopt(level, optname, value)

    设置给定套接字选项的值。所需的符号常量在套接字模块中定义(SO_ *等)。该值可以是整数或表示缓冲区的类字节对象。

    socket.settimeout(value)

    注意:并非每个端口都支持此方法,请参阅下文。

    阻止套接字操作设置超时。 value参数可以是表示秒的非负浮点数,也可以是None。如果给出非零值,则如果在操作完成之前已经过了超时时间值,则后续的套接字操作将引发“OSError”异常。如果给出零,则套接字处于非阻塞模式。如果给出None,则套接字处于阻塞模式。

    并非每个“MicroPython端口”都支持此方法。更便携和通用的解决方案是使用uselect.poll对象。这允许同时等待多个对象(而不仅仅是在套接字上,而是在支持轮询的通用stream对象上)。例:

    # Instead of:
    s.settimeout(1.0)  # time in seconds
    s.read(10)  # may timeout
    
    # Use:
    poller = uselect.poll()
    poller.register(s, uselect.POLLIN)
    res = poller.poll(1000)  # time in milliseconds
    if not res:
        # s is still not ready for input, i.e. operation timed out
    

    与CPython的区别

    CPython在超时的情况下引发socket.timeout异常,这是一个OSError子类。 MicroPython直接引发了一个OSError。如果你使用除了OSError:来捕获异常,你的代码将在MicroPython和CPython中都有效。

    socket.setblocking(flag)

    设置套接字的阻塞或非阻塞模式:如果flag为false,则套接字设置为非阻塞,否则设置为阻塞模式。

    这个方法是某些settimeout()调用的简写:

    sock.setblocking(True) 相当于sock.settimeout(None)
    sock.setblocking(False) i相当于 sock.settimeout(0)

    socket.makefile(mode='rb', buffering=0)

    返回与套接字关联的文件对象。确切的返回类型取决于给makefile()的参数。支持仅限于二进制模式('rb','wb'和'rwb')。 CPython的参数:不支持编码,错误和换行符。

    与CPython的区别

    由于MicroPython不支持缓冲流,因此忽略缓冲参数的值,并将其视为0(无缓冲)。

    与CPython的区别

    关闭makefile()返回的文件对象也将关闭原始套接字。

    socket.read([size])

    从插槽中读取大小字节。返回一个字节对象。如果没有给出大小,它会读取插座中可用的所有数据,直到EOF;因此,在套接字关闭之前,该方法不会返回。此函数尝试读取所请求的数据(没有“短读取”)。但是,对于非阻塞套接字,这可能是不可能的,然后将返回更少的数据。

    socket.readinto(buf[, nbytes])

    将字节读入buf。如果指定了nbytes,则最多读取多个字节。否则,最多读取len(buf)字节。就像read()一样,此方法遵循“无短读”策略。

    返回值:读取并存储到buf中的字节数。

    socket.readline()

    读一行,以换行符结尾。

    返回值:读取的行。

    socket.write(buf)

    将字节缓冲区写入套接字。此函数将尝试将所有数据写入套接字(无“短写”)。但是,对于非阻塞套接字,这可能是不可能的,并且返回值将小于buf的长度。

    返回值:写入的字节数。

    exception usocket.error

    MicroPython没有此异常。

    与CPython的区别

    CPython曾经有一个socket.error异常现在已被弃用,它是OSError的别名。在MicroPython中,直接使用OSError

    例程

    例程 1: 下载图片并显示

    注意需要设置 WiFi SSID 和 密码

    import socket
    import network
    import gc
    import os
    import lcd, image
    
    fm.register(board_info.WIFI_RX,fm.fpioa.UART2_TX)
    fm.register(board_info.WIFI_TX,fm.fpioa.UART2_RX)
    uart = machine.UART(machine.UART.UART2,115200,timeout=1000, read_buf_len=4096)
    nic=network.ESP8285(uart)
    nic.connect("Sipeed_2.4G","------")
    
    sock = socket.socket()
    addr = socket.getaddrinfo("dl.sipeed.com", 80)[0][-1]
    sock.connect(addr)
    sock.send('''GET /MAIX/MaixPy/assets/Alice.bmp HTTP/1.1
    Host: dl.sipeed.com
    cache-control: no-cache
    
    ''')
    
    img = b""
    sock.settimeout(5)
    while True:
        data = sock.recv(4096)
        if len(data) == 0:
            break
        print("rcv:", len(data))
        img = img + data
    
    print(len(img))
    img = img[img.find(b"\r\n\r\n")+4:]
    print(len(img))
    print("save to /sd/Alice.bmp")
    f = open("/sd/Alice.bmp","wb")
    f.write(img)
    f.close()
    print("save ok")
    print("display")
    img = image.Image("/sd/Alice.bmp")
    lcd.init()
    lcd.display(img)
    

    例程 2: 发送图片

    import os
    import socket
    import network
    import gc
    
    fm.register(board_info.WIFI_RX,fm.fpioa.UART2_TX)
    fm.register(board_info.WIFI_TX,fm.fpioa.UART2_RX)
    uart = machine.UART(machine.UART.UART2,115200,timeout=1000, read_buf_len=4096)
    nic=network.ESP8285(uart)
    nic.connect("Sipeed_2.4G","-------")
    
    addr = ("192.168.0.183", 3456)
    sock = socket.socket()
    sock.connect(addr)
    sock.settimeout(5)
    
    f = open("/sd/Alice.bmp","rb")
    while True:
        img = f.read(2048)
        if not img or (len(img) == 0):
            break
        sock.send(img)
    f.close()
    sock.close()