You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

W5100S-EVB-Pico的MicroPython TCP Socket通信ENOTCONN报错求助

问题:W5100S-EVB-Pico作为TCP服务器时出现OSError: [Errno 107] ENOTCONN错误

我用带以太网的Pi Pico(W5100S-EVB-Pico)搭建TCP服务器,运行指定版本MicroPython,计划用Python 3.7写GUI客户端发送命令实现硬件控制,但客户端连接后服务器持续报错OSError: [Errno 107] ENOTCONN。临时给客户端加time.sleep(1)能消除错误,但这不是合理方案,想搞清楚:是否需要服务器发确认后客户端再关闭连接?还有哪些可靠的TCP通信实现方式?


相关代码与现象

服务器端(W5100-EVB-Pico)代码

from machine import Pin, SPI
import network
import usocket as socket

# Only needed for static IP setup:
ip_address = '192.168.1.20'
subnet = '255.255.255.0'
gateway = '192.168.1.1'
dns = '8.8.8.8'

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket_port = 8080

# Init ethernet
def init_ethernet():
    spi = SPI(0, 2_000_000, mosi=Pin(19), miso=Pin(16), sck=Pin(18))
    nic = network.WIZNET5K(spi, Pin(17), Pin(20))
    # Static IP:
    # nic.ifconfig((ip_address, subnet, gateway, dns))
    # DHCP:
    nic.active(True)

    while not nic.isconnected():
        pass
    
    ip_address = nic.ifconfig()[0]
    subnet     = nic.ifconfig()[1]
    gateway    = nic.ifconfig()[2]
    dns        = nic.ifconfig()[3]

    print('Connected:')
    print('IP        ', ip_address)
    print('Subnet    ', subnet)
    print('Gateway   ', gateway)
    print('DNS       ', dns)
    
    listen()
    
    
def listen():
    server_socket.bind((ip_address, socket_port))
    server_socket.listen(5)
    
    print(f'Listening on {ip_address} port {socket_port}')
    
    while True:
        print('>>>This should print once and it does')
        print('>>>Waiting for connection')
        client, address = server_socket.accept()
        print(f'Client connected from: {address}')
        client.close()


if __name__ == "__main__":
    init_ethernet()

服务器运行输出:

netif changed  192.168.1.20
Connected:
IP         192.168.1.20
Subnet     255.255.255.0
Gateway    192.168.1.1
DNS        192.168.1.150
Listening on 192.168.1.20 port 8080
>>>This should print once and it does
>>>Waiting for connection

客户端(Python 3.7)代码

import socket

local_IP = socket.gethostbyname(socket.gethostname())
port = 8080

server_ip = '192.168.1.20'
server_port = 8080
server_address = (server_ip, server_port)


def test_socket():

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect(server_address)
        message = 'Hello from client'
        s.sendall(bytes(message, encoding="utf-8"))


if __name__ == '__main__':

    test_socket()

客户端运行后服务器报错:

...
>>>This should print once and it does
>>>Waiting for connection
Traceback (most recent call last):
  File "<stdin>", line 55, in <module>
  File "<stdin>", line 37, in init_ethernet
  File "<stdin>", line 49, in listen
OSError: [Errno 107] ENOTCONN

尝试过的修改

  • EDIT_01:客户端加time.sleep(1)后关闭连接,错误消失,怀疑是客户端关闭连接过早
  • EDIT_02:给服务器加数据接收逻辑,客户端发长消息,但仍需依赖time.sleep(1),不是合理方案

错误原因分析

ENOTCONN错误本质是服务器在客户端已断开连接后,仍尝试对无效的客户端套接字进行操作。你的代码里:

  1. 客户端发送数据后立刻关闭连接,TCP四次握手还没完成,服务器就执行client.close(),WIZNET5K驱动的状态同步跟不上,触发错误
  2. 服务器没有读取客户端发送的数据,TCP缓冲区未处理的情况下直接关闭套接字,也会引发这类时序相关的异常

解决方案

1. 服务器先读取数据再关闭连接

修改服务器的listen()函数,先读取客户端数据,确保TCP交互完成后再关闭套接字,避免缓冲区未处理的问题:

def listen():
    server_socket.bind((ip_address, socket_port))
    server_socket.listen(5)
    
    print(f'Listening on {ip_address} port {socket_port}')
    
    while True:
        print('>>>Waiting for connection')
        client, address = server_socket.accept()
        print(f'Client connected from: {address}')
        # 先读取客户端发送的数据
        try:
            data = client.recv(1024)  # 单次最多读1024字节
            if data:
                print(f'Received: {data.decode("utf-8")}')
        except Exception as e:
            print(f'Receive error: {e}')
        finally:
            client.close()

2. 实现请求-响应模式(推荐)

让客户端发送命令后等待服务器的确认响应,再关闭连接,从根本上避免时序问题,这也是可靠TCP通信的基础模式:

修改后客户端代码

import socket

server_ip = '192.168.1.20'
server_port = 8080
server_address = (server_ip, server_port)

def test_socket():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect(server_address)
        message = 'Hello from client'
        s.sendall(bytes(message, encoding="utf-8"))
        # 等待服务器的确认消息
        response = s.recv(1024)
        print(f'Server response: {response.decode("utf-8")}')

if __name__ == '__main__':
    test_socket()

对应修改后的服务器代码

def listen():
    server_socket.bind((ip_address, socket_port))
    server_socket.listen(5)
    
    print(f'Listening on {ip_address} port {socket_port}')
    
    while True:
        print('>>>Waiting for connection')
        client, address = server_socket.accept()
        print(f'Client connected from: {address}')
        try:
            data = client.recv(1024)
            if data:
                print(f'Received: {data.decode("utf-8")}')
                # 发送确认响应给客户端
                client.sendall(b'Command received successfully')
        except Exception as e:
            print(f'Error: {e}')
        finally:
            client.close()

可靠TCP通信的实现要点

  • 请求-响应模式:每次客户端发命令后,等待服务器返回确认/结果,确保交互完成再断开,这是最基础的可靠方式
  • 异常捕获:给套接字操作(recv、send、close)加try-except块,处理连接中断、超时等异常,防止程序崩溃
  • 设置超时:客户端和服务器都可以给套接字设超时(如s.settimeout(5)),避免因网络卡顿无限等待
  • 处理粘包/分包:如果发大尺寸数据,要定义数据包格式(比如前缀加数据长度),避免TCP粘包导致解析错误
  • 长连接优化:如果需要频繁发命令,保持客户端和服务器的长连接,避免每次都重新建立连接,提升效率和稳定性

内容的提问来源于stack exchange,提问作者MrExplore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 03:40:32