PySerial写入超时 - 数据通过了多少?

时间:2013-08-22 00:52:27

标签: python sockets timeout pyserial

我有两个应用程序通过TCP / IP连接进行交互;现在我需要他们能够通过串行连接进行交互。

套接字IO和串行IO之间存在一些差异,这使得移植不如我希望的那么简单。

其中一个区别在于发送/写入超时的语义以及应用程序可能对成功传递给连接的数据量的假设。知道了这个数量,应用程序也知道它应该选择后需要传输哪些剩余数据。

Socket.send

像socket.send(string)之类的调用可能会产生以下结果:

  1. 整个字符串已被TCP / IP堆栈接受,并且 返回字符串的长度。
  2. TCP / IP堆栈接受了字符串的一部分,并且 返回该部分的长度。应用程序可以传输 以后的其余部分。
  3. 如果套接字配置为,则引发socket.timeout异常 使用超时和发件人压倒与数据的连接。 这意味着(如果我理解正确的话) no 的字节 字符串已被TCP / IP堆栈接受,因此 应用程序可能会尝试稍后发送整个字符串。
  4. 由于某些问题引发了socket.error异常 连接。
  5. PySerial.Serial.write

    PySerial API文档说明了以下关于Serial.write(string):

    的内容
    write(data)
      Parameters:   
        data – Data to send.
      Returns:  
        Number of bytes written.
      Raises 
        SerialTimeoutException:
          In case a write timeout is configured for the port and the time is exceeded.
    
      Changed in version 2.5: Write returned None in previous versions.
    

    这个规范给我留下了一些不确定的问题:

    1. 在哪种情况下可以"写(数据)"返回更少的字节写入 比数据的长度?是否只能在非阻塞中使用 mode(writeTimeout = 0)?
    2. 如果我使用正的writeTimeout并且是SerialTimeoutException 提出,我怎么知道连接中有多少字节?
    3. 我还观察到一些我没想到的serial.write行为。

      测试尝试通过慢速连接发送长字符串。发送端口使用9600,8,N,1,无流量控制。接收端口也是打开的,但没有尝试从中读取数据。

      1. 如果writeTimeout是肯定的,但预计发件人不够大 获取SerialTimeoutException。
      2. 如果writeTimeout设置得足够大,发送方应该会写入所有数据 成功(接收者不在乎,我们也不读)。
      3. 如果writeTimeout设置为None,则发件人意外获取SerialTimeoutException 而不是阻塞,直到所有数据下降连接。我错过了什么吗?
      4. 我不知道这种行为是否典型。 如果重要的话,我使用通过零调制解调器电缆连接的两个USB到COM适配器在Windows 7 64位上试验PySerial;该设置似乎是可操作的,因为Tera Term的两个实例可以相互通信。

        了解人们是否以除了中止连接和通知用户问题之外的任何方式处理串行写入超时将会很有帮助。

        由于我目前不知道在超时发生之前写入的数据量,我正在考虑使用非阻塞写入的解决方法,并在该级别上维护类似套接字的超时语义。我不认为这是一个非常有效的解决方案(:-)),但幸运的是我的应用程序交换相对不频繁和短消息,因此性能应该在可接受的范围内。

        [EDITED]

        仔细研究非阻塞串行写入

        我写了一个简单的程序,看看我是否理解非阻塞写的工作方式:

        import serial
        
        p1 = serial.Serial("COM11") # My USB-to-COM adapters appear at these high port numbers
        p2 = serial.Serial("COM12")
        
        message = "Hello! " * 10
        print "%d bytes in the whole message: %r" % (len(message), message)
        
        p1.writeTimeout = 0 # enabling non-blocking mode
        bytes_written = p1.write(message)
        print "Written %d bytes of the message: %r" % (bytes_written, message[:bytes_written])  
        
        print "Receiving back %d bytes of the message" % len(message)
        message_read_back = p2.read(len(message))
        print "Received back %d bytes of the message: %r" % (len(message_read_back), message_read_back) 
        
        p1.close()
        p2.close()
        

        我得到的输出是:

          

        整个消息中有70个字节:'您好!你好!你好!你好!你好!你好!你好!你好!你好!你好! '
          写入消息的0个字节:''
          收到70个字节的消息
          收到了70个字节的消息:'您好!你好!你好!你好!你好!你好!你好!你好!你好!你好! '

        我很困惑:发送者认为没有数据发送但是接收者得到了所有数据。我必须在这里遗漏一些非常基本的东西......

        非常欢迎任何意见/建议/问题!

1 个答案:

答案 0 :(得分:1)

由于没有记录,让我们来看看源代码。我只关注POSIXWin32实现,但至少在这两个平台上显而易见:

  1. write(data)返回的字节数少于数据长度,超时或其他时,有情况;它总是要么返回完整的len(data),要么引发异常。
  2. 如果您使用正writeTimeout并且SerialTimeoutException被引发,则根本无法确定发送了多少字节。
  3. 特别是在POSIX上,到目前为止发送的字节数仅存储在一旦引发异常就丢失的局部变量上;在Windows上,它只是做了一个重叠的WriteFile并且除了成功的“写了所有内容”之外的任何事情都引发了异常。

    我认为你至少关心这两个平台中的一个。 (如果没有,你可能不会编写跨平台代码,并且可以查看你关心的平台。)因此,没有直接的解决方案来解决你的问题。

    如果你所描述的解决方法是可接受的,或者是另一个解决方法(比如一次只写一个字节 - 这可能效率更低,但可能更简单),那就这样做。

    或者,您必须编辑您关心的write实现(无论您是通过在运行时分配包和编辑fork,monkeypatching Serial.write还是仅编写{{1}来执行此操作函数并在脚本中调用serial_write而不是serial_write(port, data)来提供所需的信息。

    这看起来不太难。例如,在POSIX版本中,您只需要在port.write(data)行之前的某处隐藏len(data)-t。您可以将其粘贴在raise writeTimeoutError对象的属性中,或将其作为额外参数传递给异常构造函数。 (当然,如果你正在尝试编写一个跨平台的程序,并且你并不能很好地了解所有平台以编写适当的实现,那么这不太可能是一个好的答案。)

    实际上,考虑到实现你想要的并不难,你可能想在pyserial跟踪器上添加一个功能请求(理想情况下是一个补丁)。