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错误本质是服务器在客户端已断开连接后,仍尝试对无效的客户端套接字进行操作。你的代码里:
- 客户端发送数据后立刻关闭连接,TCP四次握手还没完成,服务器就执行
client.close(),WIZNET5K驱动的状态同步跟不上,触发错误 - 服务器没有读取客户端发送的数据,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
相关产品推荐
相关产品推荐

