板子是创乐博K230,刷了PreRelease 下CanMV-K230_V3P0_micropython_PreRelease_nncase_v2.9.0.img.gz 固件,解决了socket.recv()阻塞问题,Modbu Tcp_client 可以读了,但是只能读一次,只响应一次,断开连接,重连又可相应一次,也能响应第二个连接
后来到处加print, 终于把 umodbus 库 modbus Tcp 从站 程序流程理清楚, 从站是先创建socket, 然后绑定端口,然后侦听client连接,当有连接接入,就recv(), 使用的都是默认的阻塞模式, 设置超时,超时后结束操作,程序是先socket.accept(),当有client 接入,先socket.settimeout(value),接着进行socket.recv(size) ,超时后进行下次socket.accept(),等待client接入,recv
之前的固件,socket.recv()始终阻塞,设置超时时间和socket.setblocking(True)无效,超时时间3秒或5秒,更新了PreRelease 固件解决了这个问题,socket.recv()可以设置setblocking模式,或设置超时,就可以不等待或等到超时就退出recv了
我现在的现象是只能读一次,但可以多次accept(),但是之后只能recv() 一次数据,并发送响应包,完成一次modbus读写操作,最后确定程序死在了socket.accept(), accept()不能设置非阻塞模式,也不能设置超时,都无效,只能接收到连接然后退出进行下一步的recv, 所以,就之能完成第一次建立连接之后的接收并回复了
我现在在socket.accept()之前先判断是否有client_soket ,如果没有就accept(),之后recv, 有就直接recv, 现在是可以连续响应了,但是缺陷是只能单连接操作,不会在响应第二连接, 也就是说modbus tcp 在502端口等待连接, 只能接入一个client 连接并支持响应读写,单是无法支持多连接了
所以希望能尽快修正socket.accept()的这个bug, 让socket.accept()支持非阻塞模式和超时
socket.setblocking(flag)
Set blocking or non-blocking mode of the socket: if flag is false, the socket is set to non-blocking, else to blocking mode.
This method is a shorthand for certain settimeout() calls:
• sock.setblocking(True) is equivalent to sock.settimeout(None)
• sock.setblocking(False) is equivalent to sock.settimeout(0)
def _accept_request(self,
accept_timeout: float,
unit_addr_list: list) -> Union[Request, None]:
"""
Accept, read and decode a socket based request
:param accept_timeout: The socket accept timeout
:type accept_timeout: float
:param unit_addr_list: The unit address list
:type unit_addr_list: list
"""
self._sock.settimeout(accept_timeout)
new_client_sock = None
if self._client_sock is None:
try:
new_client_sock, client_address = self._sock.accept()
except OSError as e:
if e.args[0] != 11: # 11 = timeout expired
raise e
if new_client_sock is not None:
if self._client_sock is not None:
self._client_sock.close()
self._client_sock = new_client_sock
# recv() timeout, setting to 0 might lead to the following error
# "Modbus request error: [Errno 11] EAGAIN"
# This is a socket timeout error
self._client_sock.settimeout(0.5)
if self._client_sock is not None:
try:
req = self._client_sock.recv(128)
if len(req) == 0:
return None
req_header_no_uid = req[:Const.MBAP_HDR_LENGTH - 1]
self._req_tid, req_pid, req_len = struct.unpack('>HHH', req_header_no_uid)
req_uid_and_pdu = req[Const.MBAP_HDR_LENGTH - 1:Const.MBAP_HDR_LENGTH + req_len - 1]
except OSError:
# MicroPython raises an OSError instead of socket.timeout
# print("Socket OSError aka TimeoutError: {}".format(e))
return None
except Exception:
# print("Modbus request error:", e)
self._client_sock.close()
self._client_sock = None
return None
if (req_pid != 0):
# print("Modbus request error: PID not 0")
self._client_sock.close()
self._client_sock = None
return None
if ((unit_addr_list is not None) and (req_uid_and_pdu[0] not in unit_addr_list)):
return None
try:
return Request(self, req_uid_and_pdu)
except ModbusException as e:
self.send_exception_response(req[0],
e.function_code,
e.exception_code)
return None
要说socket IO,Liunx 下的Epoll 是太好用了,太强大了,轻松支持几千上万并发接入,消耗资源非常少,是其他多线程,多任务,没法比的,只是只能用c 编程,在liunx下,Nginx就是因为用了它才性能强, 我在liunx下用c 开发modbus tcp ,就用epoll, 性能不是一般强,几千上万并发访问毫无压力