C:等待阻塞套接字上的 n 个字符超时



我需要在 Linux 上的串行端口或套接字上等待 n 个字节的数据(计数已知)。 目前,我使用带有轮询的循环,测量时间并减少超时:

static int int_read_poll(int fd, uint8_t *buffer, size_t count, int timeout)
{
struct pollfd pfd;
int rc;
pfd.fd = fd;
pfd.events = POLLIN;
rc = poll(&pfd, 1, timeout);
if (rc < 0) {
perror("poll");
return 0;
}
if (rc > 0) {
if (pfd.revents & POLLIN) {
rc = read(fd, buffer, count);
return rc;
}
}
return 0;
}
static int int_read_waitfor(int fd, uint8_t *buffer, size_t count, int timeout)
{
int rc;
struct timespec start, end;
int delta_ms;
int recv = 0;
do {
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
rc = int_read_poll(fd, buffer + recv, count - recv, timeout);
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
delta_ms = (end.tv_nsec - start.tv_nsec) / 1000000;
if (!rc || (rc < 0)) return 0;
recv += rc;
timeout -= delta_ms;
if (timeout <= 0)
return 0;
} while (recv != count);
return recv;
}

在串行端口上,轮询返回每个字节并导致多次迭代。

有没有更优雅的方法来解决这个问题?

我知道,根据波特率,该代码部分的超时可能不会减少。计算纳秒可能是一种更好的方法。

一个简单的解决方案可能是使用alarm(如果您的超时以秒为单位)或setitimerITIMER_REAL计时器一起使用。然后,只需在信号发生时返回read呼叫并显示错误(使用errno == EINTR)

感谢大家的宝贵提示!

经过一些测试,我最终决定不使用信号,因为一旦我将函数移植到库中或将它们作为源代码发布,它们可能会干扰应用程序。

我最终找到了一个使用轮询和termios(只有四个系统调用)的简洁解决方案:

static int int_read_waitfor(int fd, uint8_t *buffer, size_t count, int timeout)
{
struct termios tm;
struct pollfd pfd;
int rc;
tcgetattr(fd, &tm);
tm.c_cc[VTIME] = 1; // inter-character timeout 100ms, valid after first char recvd
tm.c_cc[VMIN] = count; // block until n characters are read
tcsetattr(fd, TCSANOW, &tm);
pfd.fd = fd;
pfd.events = POLLIN;
rc = poll(&pfd, 1, timeout);
if (rc > 0) {
rc = read(fd, buffer, count);
if (rc == -1) {
perror("read");
return 0;
}
return rc;
}
return 0;
}

与通常基于数据包的网络套接字不同,串行端口(注意:在非规范模式下)是基于字符的。预计每个到达的字符都会迭代带有轮询的循环,尤其是在低波特率下。

我我的应用程序我通过串行线向设备发送一个comand并等待答案。 如果未收到答案,将发生超时,也许我们会重试。

术语选项"VMIN"很方便,因为我可以指定我喜欢的字符数量。正常读取会阻塞,直到 n 个字符到达。

如果没有答案,该命令将永远阻止。

术语选项"VTIME"与 VMIN> 0 结合使用,以分秒 (1 = 100ms) 为单位指定字符间超时。这很方便,但超时只会在收到第一个字符后开始。否则,字符间超时将毫无意义。

因此,如果我只使用termios选项,则读取将从属串行设备的块已死。

为了规避这个问题,我在阅读前使用民意调查。 一旦第一个字符到达(轮询返回 rc=1),我就开始阅读。"VTIME"也处于活动状态,并将强制执行 100 毫秒的字符间时间(尽可能低的设置)。

作为奖励,优化了超时处理:

假设超时为 400 毫秒

  • 如果从设备已死,轮询将在 400 毫秒后返回
  • 如果从站工作并在50ms(第一个字符)内回复,则轮询返回并开始读取。如果从站发送的数据太少,VTIME将在50ms + 100ms后启动并停止接收。我们不必等待整个 400 毫秒才能到达最后一个(缺失的)字节。

相关内容

  • 没有找到相关文章

最新更新