Win32 IOCP串口无法正常工作

时间:2017-12-11 16:51:08

标签: c++ windows serial-port iocp

我在收集了更多信息后编辑了这个问题。我试图通过串口使用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用于串行端口,而这似乎效率非常低?

2 个答案:

答案 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字段表示数量   在超时发生之前成功读取的字节数。

所以你有两个选择:

  • 始终将 nNumberOfBytesToRead 设置为1 - 在这种情况下读取请求 数据到达港口后立即完成
  • 通过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)。如果您使用自己的IOCPGetQueuedCompletionStatus - 又丢失了STATUS_TIMEOUT - 它只会返回TRUE以获取包含此代码的已完成数据包,而不会设置上一个错误。但是在这种情况下仍然是(NTSTATUS)lpOverlapped->Internal == STATUS_TIMEOUT(不要混淆带STATUS_TIMEOUT代码的出队数据包和GetQueuedCompletionStatus没有使完成数据包出队的情况因为等待超时(在这种情况下api返回false)并且上次错误设置为WAIT_TIMEOUT,等于STATUS_TIMEOUT))