我在收集了更多信息后编辑了这个问题。我试图通过串口使用IOCP进行通信。
我打开带有重叠标志的串口:
HANDLE hComm = CreateFile(strPortName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
然后我联想" hComm"使用IOCP端口。
我使用ReadFile()启动重叠请求以从串行端口读取。我像这样使用ReadFile():
bool bQueued = false;
DWORD dwRead;
BOOL bResult = ReadFile(GetCommHandle(), lpOverlapped->pbBufferData, lpOverlapped->dwBufferSize, &dwRead, (OVERLAPPED*)lpOverlapped);
if (bResult)
{
// It completed, but will still trigger the completion routine, so don't need to queue another one here.
bQueued = true;
}
else
{
DWORD dwError = GetLastError();
if (ERROR_IO_PENDING == dwError)
{
bQueued = true;
}
else
{
LogQueueReceiveError(lpOverlapped, dwError);
ResetConnection();
}
}
对ReadFile()的调用总是立即返回' true'结果是IOCP请求排队。但是,只有当指定的字节数到达串行端口时,操作才会完成。在我的代码中,使用接收缓冲区的大小调用ReadFile()作为要读取的字节数(这是使用套接字时的方式)。
如果我将要读取的字节数更改为值1,则只要数据到达端口,操作就会完成。同样,如果我将要读取的字节数更改为值8,则当第八个字节到达端口等时操作完成。
如果我不知道预期的字节数,那么如何在没有读取单个字节的情况下将IOCP用于串行端口,而这似乎效率非常低?
答案 0 :(得分:1)
您可以使用SetCommTimeouts
更改此行为。
很久以前(2000年左右?)我遇到了类似的问题,所以我认为目标操作系统是Windows NT 4。
我的实现最终在IOCP上有一堆排队的单字节读取。虽然您可以使用SetCommTimeouts
进行设置以允许部分读取,但此时您最终会为计时器支付一个时间片,以便为您填充部分填充的缓冲区。对于应用程序,这引入了不可接受的延迟(10毫秒或16毫秒,具体取决于SMP与非SMP)。
从那时起世界可能已经发生了变化,或者你可以接受延迟的时间片。无论哪种方式,尝试SetCommTimeouts
都可能有用。
答案 1 :(得分:0)
Win32 IOCP,串口无法正常工作
这当然不是真的。 IOCP 在此完美地工作 - 当且仅当挂起的io请求将被完成或取消时 - 数据包将排队到 IOCP 。所以以通常的方式使用 IOCP 。您的问题不在 IOCP 中,而在于读取时的串行驱动程序行为。
我发现......它在很多字节之后就完成了 到达。
这真的是documented行为:
Serial.sys继续传输字节,直到请求的数量为止 传输字节或发生超时事件。
或SERIAL_TIMEOUTS
中的更详细信息(等于COMMTIMEOUTS
):
读取或写入请求成功完成时 传输指定的字节数或请求读取或 写操作超时。请求返回
STATUS_SUCCESS
状态代码,表示指定的字节数 转移。超过此最大值的读取请求完成时 发生超时,并返回STATUS_TIMEOUT
状态代码。该 I / O状态块的Information
字段表示数量 在超时发生之前成功读取的字节数。
所以你有两个选择:
SetCommTimeouts
或直接发送设置一些超时
IOCTL_SERIAL_SET_TIMEOUTS
控制代码(这是相同的)可能是下一个最佳用途:
如果 ReadIntervalTimeout 和 ReadTotalTimeoutMultiplier 都是 设置为 MAXULONG , ReadTotalTimeoutConstant 设置为 值大于 0 且小于 MAXULONG ,读取请求 表现如下:
- 如果串口的输入缓冲区中有任何字节,则读取请求立即完成缓冲区中的字节
并返回 STATUS_SUCCESS 状态代码。- 如果输入缓冲区中没有字节,则串口会等待一个字节到达,然后立即完成读取请求 使用一个字节的数据并返回 STATUS_SUCCESS 状态
代码。- 如果在 ReadTotalTimeoutConstant 指定的时间内没有字节到达,则读取请求超时,将I / O状态块的 Information 字段设置为零,并返回 STATUS_TIMEOUT 状态代码。
还需要记住,win32层几乎总是丢失状态,即> 0(所以STATUS_TIMEOUT
也是如此)。只有在使用FileIOCompletionRoutine
时才能直接(在BindIoCompletionCallback
回调中 - dwErrorCode 这里真的是NTSTATUS
代码)。如果您使用CreateThreadpoolIo
- 您在IoCompletionCallback
超时时已经IoResult == 0
(但会(NTSTATUS)Overlapped->Internal==STATUS_TIMEOUT
)。如果您使用自己的IOCP
和GetQueuedCompletionStatus
- 又丢失了STATUS_TIMEOUT
- 它只会返回TRUE
以获取包含此代码的已完成数据包,而不会设置上一个错误。但是在这种情况下仍然是(NTSTATUS)lpOverlapped->Internal == STATUS_TIMEOUT
(不要混淆带STATUS_TIMEOUT
代码的出队数据包和GetQueuedCompletionStatus
没有使完成数据包出队的情况因为等待超时(在这种情况下api返回false)并且上次错误设置为WAIT_TIMEOUT
,等于STATUS_TIMEOUT
))