【Pico 以太网实战系列 3/3】TCP 服务器实操:MicroPython 实现监听与通信

TCP 服务器实验

在该实验中,我们将会配置 w5500 和树莓派 Pico 作为 TCP 协议的服务器,其基本工作流程如下:

1.png

  1. 监听端口​:服务器创建一个套接字,将其绑定到一个指定的 IP 地址和端口,监听客户端的连接请求;
  2. 建立连接​:当有客户端发起连接请求时,服务器接受连接,并为每个客户端建立一个新的套接字(子套接字)来处理与客户端的通信;
  3. 数据交互​:服务器和客户端通过子套接字进行数据传输,通常使用字节流进行读写操作;
  4. 关闭连接​:数据传输完成后,双方可以关闭子套接字,并继续监听其他客户端的连接请求。

以下代码可以在我们提供的资料包中 elegance-devkit v1\Demo\42 ETH_TCP_Server 文件夹找到。

示例代码如下:

# Python env   : MicroPython v1.23.0 on Wiznet W5500
# -*- coding: utf-8 -*-        
# @Time    : 2024/8/8 上午11:08   
# @Author  : 李清水            
# @File    : main.py       
# @Description : 以太网类实验,进行TCP服务器测试

# ======================================== 导入相关模块 ========================================

# 导入用于创建和管理网络连接的套接字模块
from usocket import socket
# 导入硬件相关模块
from machine import Pin,SPI
# 导入时间相关模块
import time
# 导入网络相关模块
import network

# ======================================== 全局变量 ============================================

# TCP服务器IP地址
ip = '192.168.1.20'
sn = '255.255.255.0'
gw = '192.168.1.1'
dns= '8.8.8.8'
# 用于网络连接的元组对象
netinfo=(ip, sn, gw, dns)

# 本地监听地址和端口
localip = '192.168.1.20'
localport = 8000
# 用于网络连接的元组对象
listen_info = (localip, localport)

# 全局变量,表示连接状态
conn_flag = False

# ======================================== 功能函数 ============================================

# 初始化网络接口
def w5x00_init() -> network.WIZNET5K:
    """
​    初始化 network.WIZNET5K 实例,设置静态 IP 地址并进行网络连接。

​    Args:
​        None

​    Returns:
​        network.WIZNET5K: 初始化并连接成功的 network.WIZNET5K 实例。

​    Raises:
​        None
​    """

​    ​# 声明全局变量
    global localip

    # 初始化SPI对象
    spi = SPI(0, 2_000_000, mosi=Pin(19), miso=Pin(16), sck=Pin(18))
    # 实例化network.WIZNET5K类,传入使用的片选引脚CS和复位引脚RST
    nic = network.WIZNET5K(spi, Pin(17), Pin(20))
    # 激活网络接口
    nic.active(True)
    # 设置静态IP地址:主机地址、子网掩码、网关地址和DNS服务器地址
    nic.ifconfig(netinfo)

    # 若是没有连接网络,则循环执行
    while not nic.isconnected():
        time.sleep(1)
        # 输出寄存器信息
        print(nic.regs())

    # 打印设置的IP地址
    print('ip :', nic.ifconfig()[0])
    print('sn :', nic.ifconfig()[1])
    print('gw :', nic.ifconfig()[2])
    print('dns:', nic.ifconfig()[3])

    # 返回network.WIZNET5K实例
    return nic

def server_loop() -> None:
    """
​    服务器端主循环,用于接收客户端连接并处理数据。

​    该函数会持续运行,等待客户端连接。一旦有客户端连接成功,服务器会接收客户端发送的数据,
​    并将数据回传给客户端。如果连接断开,服务器会重新进入等待连接的状态。

​    Args:
​        None

​    Returns:
​        None

​    Raises:
​        None
​    """

​    ​# 声明全局变量
    global localip
    global conn_flag

    # 无限循环以持续运行服务器
    while True:
        # 如果当前没有连接,则等待客户端连接
        if (conn_flag == False):
            # 创建一个套接字对象
            s = socket()
            # 将套接字绑定到本地IP地址和端口
            s.bind(listen_info)
            # 开始监听连接请求,最大连接队列长度为5
            s.listen(5)
            # 设置连接状态为已连接
            conn_flag = True
            # 输出测试信息
            print("TEST server Loop")
            # 打印服务器IP和监听端口信息
            print('TCP Server:{0} ,listen port:{1}\r\n'.format(localip, listen_info[1]))
            # 接受一个客户端连接,阻塞运行,直到客户端连接才会执行下一条语句
            conn, addr = s.accept()
            # 打印连接的客户端IP和端口信息
            print('Connect from {0}:{1}\r\n'.format(addr[0], addr[1]))
        else:
            # 如果已经有连接
            try:
                # 尝试接收最多2048个字节的数据
                data = conn.recv(2048)
                # 将接收到的数据解码为字符串
                data = data.decode('utf-8')
                # 打印接收到的数据
                print(data)

                # 如果接收到的数据不是'NULL'
                if data != 'NULL':
                    # 将数据编码为字节并发送回去
                    conn.send(data)
            # 如果在接收或发送数据时出现错误,会抛出异常执行以下代码块
            except:
                # 输出断开连接的信息
                print('disconnect')
                # 设置连接标志为False,表示连接已断开
                # 继续执行等待客户端连接的代码块
                conn_flag = False

# ======================================== 自定义类 ============================================

# ======================================== 初始化配置 ==========================================

# 延时3s,等待器件上电
time.sleep(3)
# 打印调试信息
print("FreakStudio : Using W5x00 Ethernet to do TCP Server Test")

# 初始化w5500模块
nic = w5x00_init()

# ========================================  主程序  ===========================================

# 运行客户端循环
server_loop()

这里,我们使用的 TCP 服务器地址为 192.168.1.20,在服务器循环函数 server_loop 中,我们进行如下操作:

2.png

  1. 如果当前没有连接,创建一个套接字对象,绑定 IP 地址和端口,开始监听连接请求
    1. 打印服务器 IP 和端口信息,等待客户端连接
    2. 接受客户端连接后打印连接信息
  2. 如果已经有连接,尝试接收和处理客户端发送的数据
    1. 如果数据非空,发送回客户端
    2. 处理接收或发送数据时的异常情况,打印断开连接信息,并将连接标志设置为 False

烧录代码,打开远程终端:

3.png

可以看到输出 TCP 服务器地址和端口号,accept() 方法的作用是阻塞运行,直到有客户端连接到服务器:

  • 当服务器在等待客户端连接时,程序会暂停在 s.accept() 这一行,确保服务器能够正确接收并处理每个客户端的连接请求
  • 一旦有客户端连接请求进来,accept() 方法会返回一个新的套接字对象 conn 和客户端地址 addr

我们打开 NetAssist 软件,选择协议类型为 TCP Client,配置远程主机地址为 198.168.1.20,远程主机端口设置与代码中一致,为 8000,其余选项均为默认,点击“连接”按钮:

4.png

5.png

我们可以看到终端中显示连接成功的信息:

6.png

我们通过 NetAssist 软件发送数据,可以看到对应数据又回传到了 TCP 客户端:

7.png

mmexport1781287238259.gif

posted @ 2026-09-02 14:47  FreakStudio  阅读(10)  评论(0)    收藏  举报