5. 网络 - TCP 套接字

大多数互联网的构建块是 TCP 套接字。这些套接字在连接的网络设备之间提供可靠的字节流。本教程的这一部分将展示如何在几种不同的情况下使用 TCP 套接字。

5.1. 星球大战 Asciimation

最简单的方法是从互联网上下载数据。在这种情况下,我们将使用blinkenlights.nl 网站提供的Star Wars Asciimation 服务。它使用端口 23 上的 telnet 协议将数据流式传输到任何连接的人。它使用起来非常简单,因为它不需要您进行身份验证(提供用户名或密码),您可以立即开始下载数据。

首先要做的是确保我们有可用的套接字模块:

>>> import socket

然后获取服务器的IP地址:

>>> addr_info = socket.getaddrinfo("towel.blinkenlights.nl", 23)

getaddrinfo函数实际上返回一个地址列表,每个地址包含的信息比我们需要的要多。我们只想获取第一个有效地址,然后只获取服务器的 IP 地址和端口。要做到这一点,请使用:

>>> addr = addr_info[0][-1]

如果您键入addr_infoaddr 在提示符下,您将确切地看到它们保存的信息。

使用 IP 地址,我们可以创建一个套接字并连接到服务器:

>>> s = socket.socket()
>>> s.connect(addr)

现在我们已连接,我们可以下载并显示数据:

>>> while True:
...     data = s.recv(500)
...     print(str(data, 'utf8'), end='')
...

当这个循环执行时,它应该开始显示动画(使用 ctrl-C 来中断它)。

如果您想尝试一下,您还应该能够使用普通 Python 在您的 PC 上运行相同的代码。

5.2. HTTP GET 请求

下一个示例显示如何下载网页。HTTP 使用端口 80,您首先需要发送“GET”请求,然后才能下载任何内容。作为请求的一部分,您需要指定要检索的页面。

让我们定义一个可以下载和打印 URL 的函数:

def http_get(url):
    import socket
    _, _, host, path = url.split('/', 3)
    addr = socket.getaddrinfo(host, 80)[0][-1]
    s = socket.socket()
    s.connect(addr)
    s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8'))
    while True:
        data = s.recv(100)
        if data:
            print(str(data, 'utf8'), end='')
        else:
            break
    s.close()

那你可以试试:

>>> http_get('http://micropython.org/ks/test.html')

这应该检索网页并将 HTML 打印到控制台。

5.3. 简单的 HTTP 服务器

以下代码创建了一个简单的 HTTP 服务器,该服务器为单个网页提供服务,该网页包含一个包含所有 GPIO 引脚状态的表格:

import machine
pins = [machine.Pin(i, machine.Pin.IN) for i in (0, 2, 4, 5, 12, 13, 14, 15)]

html = """<!DOCTYPE html>
<html>
    <head> <title>ESP8266 Pins</title> </head>
    <body> <h1>ESP8266 Pins</h1>
        <table border="1"> <tr><th>Pin</th><th>Value</th></tr> %s </table>
    </body>
</html>
"""

import socket
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]

s = socket.socket()
s.bind(addr)
s.listen(1)

print('listening on', addr)

while True:
    cl, addr = s.accept()
    print('client connected from', addr)
    cl_file = cl.makefile('rwb', 0)
    while True:
        line = cl_file.readline()
        if not line or line == b'\r\n':
            break
    rows = ['<tr><td>%s</td><td>%d</td></tr>' % (str(p), p.value()) for p in pins]
    response = html % '\n'.join(rows)
    cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
    cl.send(response)
    cl.close()