C++ - 函数 select 显示读取后标准输入中有数据

C++ - function select shows there are data in stdin after they were read

我的聊天客户端中有以下代码:

fd_set fds;
struct timeval t;
t.tv_sec = 0;
t.tv_usec = 100;
int ret;

string message;

while(run)
{
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds);

    if((ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &t)) < 0)
    {
        exit(ERR_SELECT);
    }
    else if(ret > 0 && FD_ISSET(STDIN_FILENO, &fds))
    {
        message.clear();
        getline(cin, message);

        if(strlen(message.c_str()) > 0)
        {
            send_message(&message, client_socket);
        }
    }
}

当我在程序 运行 中输入内容时,它工作正常。它读取 stdin 中的消息,发送它,然后循环等待下一个输入。但是当我像这样开始将输入传递给程序时:

echo "message" | ./chat_client

或者这样:

printf "message\n" | ./chat_client

它读取并发送 "message" 但随后在循环的下一次迭代中它的行为就像标准输入中有内容但 getline 只读取空字符串。然后当我写东西时,getline 不读它。

有人知道是什么原因造成的吗?

感谢任何建议

getline() returns 如果它到达文件末尾,这发生在所有来自管道的内容都已被读取时。您需要明确测试 cin.eof() returns 是否为真。

至于 select() 它是设计使然,如果在文件末尾,它会将文件显示为 "readable"。这样程序可以检测到连接已关闭。 Linux manual for select() 指出:

A file descriptor is considered ready if it is possible to perform a corresponding I/O operation (e.g., read(2) without blocking...

和文件描述符上的 read() 在 EOF returns 一个没有阻塞的零。


我不确定通过 istream 选择 fd 读取是否会遇到问题,但这对我有用 (Linux/gcc):

#include <iostream>
#include <sys/select.h>
#include <unistd.h>

int main()
{
  std::string message;
  while(1) {
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds);
    int ret = select(1, &fds, NULL, NULL, NULL);
    if (ret == -1) break;
    getline(std::cin, message);
    if (std::cin.eof()) {
      std::cout << "eof." << std::endl;
      break;
    }
    std::cout << "read: " << message << std::endl;
  }
  return 0; 
}