【问题标题】:Make ReadFile() return as soon as something has been read读取内容后立即返回 ReadFile()
【发布时间】:2019-06-03 08:14:18
【问题描述】:

我想在 Linux 和 Windows 上以一致的方式从串行端口读取数据。我注意到read()ReadFile() 的行为略有不同。考虑以下代码:

// on Linux
int r = read(fd, buf, 256);

// on Windows
ReadFile(handle, buf, 256, &r, NULL);

这两个函数都将永远阻塞,直到数据到达。到目前为止,一切都很好。但是,有一点区别:read() 将在至少 1 个字节到达后立即返回,而 ReadFile() 在所有 256 个字节到达之前不会返回。

因此,我想问一下:有没有办法让ReadFile() 在Linux 上表现得像read(),即即使请求了256 个字节,只要至少有1 个字节就返回?

【问题讨论】:

  • 你为什么不能在你的windows代码中也使用read()
  • 将第三个参数改为1,循环读取,直到有256个字节。
  • 另外,IIRC,您可以使用COMMTIMEOUTS 将所有超时值设置为0
  • 您需要调用SetCommTimeouts - 如果应用程序将 ReadIntervalTimeout 和 ReadTotalTimeoutMultiplier 设置为 MAXDWORD 并将 ReadTotalTimeoutConstant 设置为大于零且小于 MAXDWORD 的值,则在 ReadFile 函数时会发生以下情况之一被调用:如果输入缓冲区中有任何字节,ReadFile 立即返回缓冲区中的字节。如果输入缓冲区中没有字节,ReadFile 会等到一个字节到达,然后立即返回。
  • @Andreas 在这种情况下,请尝试在循环中调用ReadFile()。如果读取任何字节,则中断循环。否则,如果读取超时,请再次调用ReadFile() 以获取新的超时间隔。

标签: c winapi serial-port


【解决方案1】:

IMO 最好的方法是实现循环缓冲区,使用您喜欢的函数从缓冲区中的流中读取 1 个字节,然后调用接收回调函数 - 或者只是轮询循环缓冲区以查看数据是否存在。

它解决了你所有的问题——当数据到达时你会被通知(或者你可以轮询存在的数据)并且不会覆盖缓冲区(当然你需要读取数据,否则如果缓冲区已满您将忽略新数据)

这里有一个非常简单的实现

unsigned char buff[128];

struct 
{
    unsigned head:8;
    unsigned tail:8;
}control = {.head = 0, .tail = 0};

int isFull(void)
{
    return (control.head + 1) == control.tail;
}

int isEmpty()
{
    return control.head == control.tail;    
}

int push(int ch)
{
    if(!isFull())
    {
        buff[control.head++] = ch;
        return 0;
    }
    return -1;
}

int pop(void)
{
    if(!isEmpty())
    {
        return buff[control.tail++];
    }
    return -1;
}

以及简单的用法

void (*rcvcallback)(void);
initReceive(void (*callback)(void))
{
    rcvcallback = callback;
}

void receive(void)
{
    int r = 0;

    #ifdef LINUX
    read(fd, &r, 1);
    #endif

    #ifdef WINDOWS
    size_t size;
    ReadFile(handle, &r, 1, &size, NULL);
    #endif

    push(r);
    rcvcallback();
}

#ifdef WINDOWS
#define EL "\n\r"
#endif
#ifdef LINUX
#define EL "\n"
#endif


void myRCVcallback(void)
{
    printf("Byte received: %d"EL, pop());
}


int main()
{
    printf("Starting ......"EL);

    while(1)
    {
        receive();
    }

    return 0;
}

【讨论】:

  • DV?很有趣。
  • 我没有对你投反对票,但我更喜欢 RbMm 使用 SetCommTimeouts() 的解决方案,尽管这样做的缺点是必须使用 MAXDWORD-1 的超时,但这应该是可以接受的,因为它对应于超时 50 天左右:)
猜你喜欢
  • 2021-02-18
  • 1970-01-01
  • 2016-11-09
  • 2011-12-19
  • 2014-11-28
  • 2019-04-25
  • 2019-07-05
  • 2013-08-26
  • 1970-01-01
相关资源
最近更新 更多