【问题标题】:Multiple read call of a socket fd with O_NONBLOCK set fails设置了 O_NONBLOCK 的套接字 fd 的多次读取调用失败
【发布时间】:2014-09-19 23:11:52
【问题描述】:

我正在使用select()NONBLOCKING 连接fd 来接受连接并处理输入和输出。我在处理超过缓冲区大小的大数据传输时遇到了一些问题。所以例如这里:

readCount = 1;
while(readCount > 0)
{
    // clear t_input to read new data from the fd
    memset(t_input, 0, CLIENT_INPUT_BUFF_LEN);

    // read data from the client connectionFd and since t_input is 1024 only
    // read that much
    readCount = read(iter->connectionFd, t_input, 1024);
    printf("<<< %d - %s >>> \n", errno, strerror(errno));

    if(readCount == -1)
    {
        if( errno == EAGAIN) 
            break;
    }

    iter->AppendInputData(t_input, readCount);
}

这不适用于大数据。因此,当我传输小于1024 的数据时,第一个read 调用成功完成,数据被复制到AppendInputData 调用中。由于它处于循环中,第二个read 调用返回-1,并将errno 设置为EAGAIN,然后打破循环——这一切都适用于这种情况。

但是,在大于1024 数据的情况下,第二次读取调用再次失败,errno 设置为EAGAIN。奇怪的是,在调试模式下,我看不到这种行为,第二个或第三个 read 调用返回正常,并且收集了所有数据。有人可以解释可能发生的事情吗?

【问题讨论】:

  • 你的 select() 调用在哪里?获得 EAGAIN/EWOULDBLOCK 是正常的和预期的,这意味着您的非阻塞 read() 调用将被阻塞。当 select() 说可以时,您应该返回执行 select() 并阅读更多内容。

标签: c linux sockets select file-descriptor


【解决方案1】:

尝试类似的方法:

do
{
    // read data from the client connectionFd and since t_input is 1024 only read that much
    readCount = read(iter->connectionFd, t_input, 1024);

    if (readCount == -1)
    {
        if (errno == EAGAIN) 
        {
            fd_set fd;
            FD_ZERO(&fd);
            FD_SET(iter->connectionFd, &fd);

            timeval tv;
            tv.tv_sec = 5;
            tv.tv_usec = 0;

            readCount = select(iter->connectionFd+1, &fd, NULL, NULL, &tv);
            if (readCount == 1)
                continue;

            if (readCount == 0)
            {
                printf("<<< timeout >>> \n");
                break;
            }
        }

        printf("<<< %d - %s >>> \n", errno, strerror(errno));
        break;
    }

    if (readCount == 0)
    {
        printf("<<< disconnect >>> \n");
        break;
    }

    iter->AppendInputData(t_input, readCount);
}
while (true);

【讨论】:

  • 文件标志设置为NONBLOCKING,所以我不知道提供超时会有所帮助。
  • @ArmenB。我建议您查看 select() 的实际作用。
  • @ArmenB.: 正是因为NONBLOCKING,我才添加了超时逻辑。传输速度不同。当read() 报告EAGAIN 时,仅表示在那一刻 没有可用的单个字节。如果网络暂时中断,您可以多给循环几秒钟来检查是否有新字节到达,这样您就不必过早地中断循环。如果您在 5 秒内甚至没有收到 2 个字节,则客户端很可能已停止发送数据。当然,您可以使用对您的代码有意义的任何超时。
【解决方案2】:

一切都是时机。您不能假设所有数据都会连续到达并在您调用 read() 时可用,或者当 EA​​GAIN 发生时它就永远结束了。习惯上调用 select() 来告诉您什么时候可以读取数据。

您在调试模式下会得到不同的行为,因为您正在更改时间。

【讨论】:

  • 那么我怎么知道客户端何时完成了所有数据的发送。我正在使用一个基于事件的系统,即使客户端接收到所有数据,我也想提高它。浏览器如何知道 Web 服务器何时发送完页面的所有内容?
  • TCP 是一个字节流,它没有消息边界的概念。您必须在通信协议中实现该逻辑。客户端必须: 1) 首先发送一个数字/标头,说明正在发送多少数据字节,然后发送实际数据;或者 2) 发送数据后发送一个唯一的终止符,该终止符没有出现在数据中。您可以先读取大小值并继续读取,直到收到那么多字节,或者继续读取直到遇到终止符。无论哪种方法适合您的协议。
  • HTTP 定义了一组关于如何确定消息结束的规则。见RFC 2616 Section 4。首先发送消息标头,以空行终止,然后标头描述其余数据的格式。支持多种格式,每种格式都有不同的方式来检测数据结束。
  • @ArmenB。您不能将 EAGAIN 用作消息边界。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-14
  • 1970-01-01
相关资源
最近更新 更多