我有两个应用程序通过 TCP/IP 连接进行交互;现在我需要它们也能够通过串行连接进行交互。
套接字 IO 和串行 IO 之间存在一些差异,这使得移植没有我希望的那么简单。
差异之一在于发送/写入超时的语义以及应用程序可能对成功通过连接传递的数据量所做的假设。知道这个数量后,应用程序还知道如果它选择这样做的话,以后需要传输哪些剩余数据。
Socket.send
像 socket.send(string) 这样的调用可能会产生以下结果:
- 整个字符串已被 TCP/IP 堆栈接受,并且 返回字符串的长度。
- TCP/IP 堆栈已接受字符串的一部分,并且 返回该部分的长度。应用程序可以传输 稍后再处理字符串的其余部分。
- 如果套接字配置为,则会引发 socket.timeout 异常 使用超时,发送者就会淹没数据连接。 这意味着(如果我理解正确的话)没有个字节 字符串已被 TCP/IP 堆栈接受,因此 应用程序稍后可能会尝试发送整个字符串。
- 由于某些问题而引发了 socket.error 异常 连接。
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.
这个规范给我留下了一些不确定的问题:
- 在什么情况下“write(data)”可能会返回更少的写入字节 比数据的长度?是不是只能在非阻塞的情况下才可以 模式(writeTimeout=0)?
- 如果我使用正的 writeTimeout 并且 SerialTimeoutException 是 提出来,我如何知道连接中有多少字节?
我还观察到了一些我没有预料到的serial.write行为。
测试尝试通过慢速连接发送长字符串。发送端口使用9600,8,N,1,无流控。接收端口也打开,但没有尝试从中读取数据。
- 如果 writeTimeout 为正数但不够大,则发送方预计 获取 SerialTimeoutException。
- 如果 writeTimeout 设置得足够大,发送方将获得所有写入的数据 成功(接收者不关心阅读,我们也不关心)。
- 如果 writeTimeout 设置为 None,发送方会意外收到 SerialTimeoutException 而不是阻塞直到所有数据都通过连接。我错过了什么吗?
我不知道这种行为是否典型。 以防万一,我在 Windows 7 64 位上使用两个通过 null-modem 电缆连接的 USB-to-COM 适配器来试验 PySerial;该设置似乎可以运行,因为 Tera Term 的两个实例可以通过它相互通信。
了解人们是否以中止连接和通知用户问题以外的任何方式处理串行写入超时会很有帮助。
由于我目前不知道超时发生之前写入的数据量,因此我正在考虑使用非阻塞写入并自己将类似套接字的超时语义维护在该级别之上的解决方法。我并不认为这是一个非常高效的解决方案 (:-)),但幸运的是,我的应用程序交换的消息相对不频繁且较短,因此性能应该在可接受的范围内。
[已编辑]
仔细观察非阻塞串行写入
我写了一个简单的程序来看看我是否理解非阻塞写入是如何工作的:
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 bytes in the whole message: 'Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! '
Written 0 bytes of the message: ''
Receiving back 70 bytes of the message
Received back 70 bytes of the message: 'Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! '
我很困惑:发送方认为没有发送任何数据,但接收方却收到了全部数据。我一定错过了一些非常基本的东西......
非常欢迎任何意见/建议/问题!
最佳答案
由于没有文档,让我们看一下源代码。我只看了POSIX和Win32实现,但很明显,至少在这两个平台上:
- 在任何情况下,
write(data)
返回的写入字节数可能少于数据长度、超时或其他原因;它总是返回完整的len(data)
,或者引发异常。 - 如果您使用正的
writeTimeout
并且引发SerialTimeoutException
,则根本无法知道发送了多少字节。
特别是,在 POSIX 上,到目前为止发送的字节数仅存储在局部变量中,一旦引发异常,该变量就会丢失;在 Windows 上,它只执行单个重叠的 WriteFile
操作,并引发除成功“写入所有内容”之外的任何内容的异常。
我假设您至少关心这两个平台之一。 (如果没有,您可能没有编写跨平台代码,并且可以查看您真正关心的一个平台。)因此,没有直接的解决方案可以解决您的问题。
如果您描述的解决方法是可以接受的,或者是不同的解决方法(例如一次只写入一个字节 - 这可能效率更低,但可能更简单),请执行此操作。
或者,您必须编辑您关心的 write
实现(无论您是通过 fork 包并编辑您的 fork ,还是在运行时猴子修补 Serial.write
来实现此操作) ,或者只是编写一个 serial_write
函数并在脚本中调用 serial_write(port, data)
而不是 port.write(data)
)来提供您想要的信息。
这看起来并不难。例如,在 POSIX 版本中,您只需将 len(data)-t
存储在任一 raise writeTimeoutError
行之前。您可以将其粘贴到 Serial
对象的属性中,或者将其作为额外参数传递给异常构造函数。 (当然,如果您正在尝试编写跨平台程序,并且您不太了解所有平台以编写适当的实现,那么这不太可能是一个好的答案。)
实际上,考虑到实现您想要的东西并不难,您可能需要在 pyserial 跟踪器上添加功能请求(最好带有补丁)。
关于python - PySerial 写入超时——传输了多少数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18369993/