VB.Net SerialPort 读取返回碎片数据集
问题
我有一个 USB 设备,可以在 Windows 上创建虚拟串行端口。我正在使用 VB.Net 从端口写入和读取。我的设备以特定大小的字节集进行响应,但我发现 SerialPort.Read(byte-array,offset,number-bytes) 不会返回完整的字节数,但它也不会超时或生成异常。重复调用会返回额外的片段(最多需要 3 次调用)。我不明白为什么这个读取方法的行为如此?它是否认为请求的字节数只是一个建议? :-)。我希望它会等待完成整个请求,除非它首先超时。
使用 pySerial 的 Python 代码不有同样的问题。
那么,我在这里做错了什么?我是否期望太多?
一些场景是:
- 我向端口写入命令并期望得到 4 个字节的响应。我首先得到 1 个字节,然后在后续调用中得到 3 个字节。
- 我写了一条命令,期望得到 21120 字节的响应。我在 3 次调用中从端口读取数据,分别得到 1、12671 和 8448 字节。
以下是我的代码的一些摘录:
Private Sub SetupVirtualSerialPort()
Dim portName As String = "COM" + (m_DeviceContext * -1).ToString
Const baud As Int32 = 9600 '7680000
Const parity As Parity = parity.None
Const databits As Int32 = 8
Const stopbits As StopBits = stopbits.One
m_SerialPort = New SerialPort(portName, baud, parity, databits, stopbits)
m_SerialPort.WriteTimeout = VSPtimeout
m_SerialPort.ReadTimeout = VSPtimeout
m_SerialPort.ReadBufferSize = 2 * RETURN_BUFFER_SIZE
m_SerialPort.WriteBufferSize = 2 * COMMAND_BUFFER_SIZE
m_SerialPort.Open()
' Register event handlers
AddHandler m_SerialPort.ErrorReceived, AddressOf m_DriverInterface.Handle_VSP_Error
End Sub
Public Function WriteReadVSPort(ByVal commandLength As Int32, ByVal returnLength As Int32) As Int32
Const RetryLimit As Int32 = 5
Dim NumberRetries As Int32 = 0
Dim Offset As Int32 = 0
Dim ExceptionOccurred As Boolean = False
Dim NumberBytes As Int32 = 0
Try ' Writing
m_SerialPort.Write(m_CommandBuffer, 0, commandLength)
Catch exc As InvalidOperationException
MessageBox.Show("InvalidOperationException", Application.ProductName)
ExceptionOccurred = True
Catch exc As TimeoutException
MessageBox.Show("TimeoutException", Application.ProductName)
ExceptionOccurred = True
End Try
If Not ExceptionOccurred Then
Try ' Reading
' Working around a problem here: reads are returning fewer
' bytes than requested, though not signalling a timeout exception.
' Therefore, we retry if we got fewer bytes than expected, up to five times.
While NumberRetries < RetryLimit And returnLength > Offset
NumberBytes = m_SerialPort.Read(m_ReturnBytes, Offset, returnLength - Offset)
Offset += NumberBytes
NumberRetries += 1
If returnLength <> NumberBytes Then
System.Diagnostics.Debug.Print("Number of bytes read (" & NumberBytes &
") not what was requested (" & returnLength & "). Accumulated " & Offset)
End If
End While
Catch exc As InvalidOperationException
MessageBox.Show("InvalidOperationException", Application.ProductName)
ExceptionOccurred = True
Catch exc As TimeoutException
MessageBox.Show("TimeoutException", Application.ProductName)
ExceptionOccurred = True
End Try
If ExceptionOccurred Then
Return 1
Else
Return 0
End If
End Function
谢谢。
The Problem
I have a USB device which creates a Virtual Serial Port on Windows. I am using VB.Net to write and read from the port. My device responds with specific sized set of bytes, but I am finding that SerialPort.Read(byte-array,offset,number-bytes) does not return the full number-bytes but it also does not timeout or generate an exception. Repeated calls return additional fragments (up to 3 calls required). I do not understand why this read method behaves the way it does? Does it think the requested number of bytes is only a suggestion? :-). I would expect that it would wait to fulfill the entire request, unless it timesout first.
Python code using pySerial does not have the same problem.
So, what am I doing wrong here? Am I expecting too much?
Some scenarios are:
- I write a command to the port and expect to get 4 bytes in response. I get 1 byte first and then 3 bytes on the subsequent call.
- I write a command and expect 21120 bytes in response. I get 1, 12671 and then 8448 bytes in 3 calls to read from the port.
Here are some excerpts from my code:
Private Sub SetupVirtualSerialPort()
Dim portName As String = "COM" + (m_DeviceContext * -1).ToString
Const baud As Int32 = 9600 '7680000
Const parity As Parity = parity.None
Const databits As Int32 = 8
Const stopbits As StopBits = stopbits.One
m_SerialPort = New SerialPort(portName, baud, parity, databits, stopbits)
m_SerialPort.WriteTimeout = VSPtimeout
m_SerialPort.ReadTimeout = VSPtimeout
m_SerialPort.ReadBufferSize = 2 * RETURN_BUFFER_SIZE
m_SerialPort.WriteBufferSize = 2 * COMMAND_BUFFER_SIZE
m_SerialPort.Open()
' Register event handlers
AddHandler m_SerialPort.ErrorReceived, AddressOf m_DriverInterface.Handle_VSP_Error
End Sub
Public Function WriteReadVSPort(ByVal commandLength As Int32, ByVal returnLength As Int32) As Int32
Const RetryLimit As Int32 = 5
Dim NumberRetries As Int32 = 0
Dim Offset As Int32 = 0
Dim ExceptionOccurred As Boolean = False
Dim NumberBytes As Int32 = 0
Try ' Writing
m_SerialPort.Write(m_CommandBuffer, 0, commandLength)
Catch exc As InvalidOperationException
MessageBox.Show("InvalidOperationException", Application.ProductName)
ExceptionOccurred = True
Catch exc As TimeoutException
MessageBox.Show("TimeoutException", Application.ProductName)
ExceptionOccurred = True
End Try
If Not ExceptionOccurred Then
Try ' Reading
' Working around a problem here: reads are returning fewer
' bytes than requested, though not signalling a timeout exception.
' Therefore, we retry if we got fewer bytes than expected, up to five times.
While NumberRetries < RetryLimit And returnLength > Offset
NumberBytes = m_SerialPort.Read(m_ReturnBytes, Offset, returnLength - Offset)
Offset += NumberBytes
NumberRetries += 1
If returnLength <> NumberBytes Then
System.Diagnostics.Debug.Print("Number of bytes read (" & NumberBytes &
") not what was requested (" & returnLength & "). Accumulated " & Offset)
End If
End While
Catch exc As InvalidOperationException
MessageBox.Show("InvalidOperationException", Application.ProductName)
ExceptionOccurred = True
Catch exc As TimeoutException
MessageBox.Show("TimeoutException", Application.ProductName)
ExceptionOccurred = True
End Try
If ExceptionOccurred Then
Return 1
Else
Return 0
End If
End Function
Thank you.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这对于处理 IO(包括流和端口)来说是完全正常的。基本上,您需要检查返回值和循环。例如:
如果您的消息不是固定长度的,您可能需要添加长度前缀(在每个消息之前)或消息分隔符(在每个消息之后)。
This is entirely normal for dealing with IO, including streams and ports. Basically, you need to check the returned value and loop. For example:
If your messages are not fixed-length you may need to add a length prefix (before each), or a message delimiter (after each).
我采纳了使用 DataReceived 事件并使代码事件驱动而不是循环的建议。我发现虚拟串行端口仍然无法在一次读取操作中处理 21120 字节消息。较短的消息长度可以正确完成。然而,当我将串口数据接收阈值设置为21119字节并将串口读缓冲区设置为我的消息大小的10倍时,我发现
1. 只有 12672 字节可用(而不是 21119)时,将触发 DataReceived 事件,并且在对完整大小执行 Read() 时返回相同的数字。
2. 由于字节数不等于我的阈值,如果我当时不进行读取,则不会触发进一步的 DataReceived 事件
3. 但是,如果(且仅当)我读取 12672 字节时,另一个 DataReceived 事件会与剩余的 8448 字节一起出现。
我不明白为什么会这样。欢迎进一步评论。
但是,我想我应该分享我当前的代码以造福他人。
一些类变量是:
事件处理程序子例程
执行写入命令/读取响应活动的函数
I incorporated the suggestion that I use the DataReceived event and make the code event driven rather than looping. I found out the Virtual Serial Port still does not work for my 21120 byte messages in one read operation. Shorter message lengths are done properly. However, when I set the serial port data received threshold to 21119 bytes and set the serial port read buffer to be 10 times as long as my message size, I found that
1. A DataReceived event will be triggered with only 12672 bytes available (not 21119) and the same number returned when a Read() is executed for the full size.
2. With the number of bytes not equaling my threshold, if I do not do a read at that time, no further DataReceived event is triggered
3. But, if (and only if) I read the 12672 bytes, another DataReceived event comes along with the remaining 8448 bytes.
I am clueless as to why this behaves as such. Further comments are welcome.
However, I thought I would share my current code for the benefit of others.
Some class variables are:
Event-handler subroutine
Function executing the write command/read response activity