micropython-modbus-develop 调试 tcp_client_example.py 只能读一帧问题

Viewed 152

板子是创乐博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, 性能不是一般强,几千上万并发访问毫无压力

1 Answers

你好,之后会来修复accept的问题。