如何从Windows命名管道中读取超时?

时间:2018-10-30 17:36:21

标签: c windows winapi named-pipes

我正在尝试将在Unix中非常简单的内容移植到Windows:什么都没发生时从超时的命名管道(fifo)中读取。 我正在尝试以一种简单的方式(使用PeekNamedPipe来做到这一点,而不使用重叠的IO。这可能吗?

下面是一个完整的示例。暂时不起作用:未收到写入的字符串,而读者可以得到

  

ERROR_NO_DATA 232(0xE8)管道正在关闭

第一次读取后,作者经过尝试后得到了

  

ERROR_SEM_TIMEOUT 121(0x79)信号灯超时时间已到期

测试编写器(管道客户端):

#include "Windows.h"
#include <iostream>
#include <stdio.h>
#include <errno.h>

int main()
{
    HANDLE hpipe;
    DWORD written;
    const char *pname = "\\\\.\\pipe\\isp-control";
    char msg[] = "play asdf.wav";

    printf("waiting for pipe %s\n", pname);
    bool stat = WaitNamedPipeA(pname, 4000);
    printf("  --> %d, error %d\n", stat, GetLastError());

    if (stat)
    {
        hpipe = CreateFileA(pname, GENERIC_WRITE | GENERIC_READ,
            0, NULL, OPEN_EXISTING, 0, NULL);

        if (hpipe != INVALID_HANDLE_VALUE)
        {
            bool stat = WriteFile(hpipe, msg, strlen(msg) + 1, &written, NULL);
            printf("  wrote %d bytes of %d: '%s'\n", written, strlen(msg) + 1, msg);
            printf("  --> %d, error %d\n", stat, GetLastError());

            CloseHandle(hpipe);
        }
        else
        {
            printf("error %d opening pipe (handle %d)\n", GetLastError(), (int) hpipe);
            return 1;
        }
    }
    return 0;
}

测试读取器(管道服务器和客户端):

#include <stdio.h>
#include <iostream>
#include <string>
#include "pipeio.hpp"

int main (int argc, char *argv[])
{
  std::string pipename = "\\\\.\\pipe\\isp-control";    // command input filename
  ISP::WindowsPipeIO io(pipename, 2);

  while (true)
  {
    // wait for pipe input in command string or timeout
    std::string cmdstr = io.wait();
    printf("got response: '%s'\n", cmdstr.c_str());
  }
}

和pipeio.hpp类,在第一个#ifdef中,我离开了Unix版本以显示我要实现的目标:

#ifndef _PIPEIO_HPP_
#define _PIPEIO_HPP_

namespace ISP {

/// interface for wrapper around named pipe
class PipeIO
{
public:
  //virtual ~PipeIO ();
  virtual std::string wait () = 0;
};

#ifndef WIN32  /////////// Unix version
#include <errno.h>

/// MacOS/Linux wrapper around named pipe
class UnixPipeIO : public PipeIO
{
public:
  UnixPipeIO (std::string name, double timeout)
  : name_(name)
  {
    // open control and status pipes
    filedes_ = open(name_.c_str(), O_RDWR | O_NONBLOCK);
    if (filedes_ < 0)
      throw std::runtime_error("can't open pipe");

    timeout_.tv_sec  = timeout; // todo: use fractional
    timeout_.tv_usec = 0;
  }

  std::string wait ()
  {
    FD_ZERO(&input_);
    FD_SET(filedes_, &input_);

    // wait for commands
    //db printf("waiting for fd %d...\n", filedes_);
    int ready = select(filedes_ + 1, &input_, NULL, NULL, &timeout_);

    if (ready > 0  &&  FD_ISSET(filedes_, &input_))
    { // ctl input has data, read and parse
      //db printf("fd %d is ready.\n", filedes_);
      int nread = read(filedes_, command_, CTL_INPUT_SIZE);
      command_[nread] = 0;  // null-terminate
      //db printf("read command (nread %d): '%s'\n", nread, nread > 0 ? command_ : "");

      //todo: continue reading when more than CTL_INPUT_SIZE chars are available
      //todo: stop at \0? continue when called again
      return std::string(command_);
    }
    else if (ready == 0)
    {
      //db printf("timeout\n");
      return "timeout";
    }
    else
    {
      fprintf(stderr, "error %d in select: %s\n", errno, strerror(errno));
      return "ioerror";
    }
  }

private:
  std::string name_;    //< name of pipe
  int filedes_;
  struct timeval timeout_;
  fd_set input_;
  static const long CTL_INPUT_SIZE = 65535;
  char command_[CTL_INPUT_SIZE];
};

#else   /////////////////////// Windows

#define NOMINMAX
#include "Windows.h"
#include <errno.h>

/// Windows wrapper around named pipe
class WindowsPipeIO : public PipeIO
{
public:
  WindowsPipeIO (std::string name, double timeout)
  : name_(name), timeout_((int) (timeout * 1000.))
  {
    printf("windowspipeio %s timeout %d ms\n", name_.c_str(), timeout_);

    // open control and status pipes
    hpipe_ = CreateNamedPipeA(name_.c_str(),
                 PIPE_ACCESS_DUPLEX,
                 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT,
                 4, // num. instances
                 1024 * 16, // output buffer size
                 CTL_INPUT_SIZE,  // input size
                 timeout_, // default timeout ms 
                 NULL);
    if (hpipe_ == INVALID_HANDLE_VALUE)
      throw std::runtime_error("can't create pipe " + name);

    // open read handle
    hread_ = CreateFileA(name_.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

    if (hread_ == INVALID_HANDLE_VALUE)
      throw std::runtime_error("can't open read handle " + name);

    printf("  windowspipeio created pipe %d, read %d\n", (int) hpipe_, (int) hread_);
  }

  ~WindowsPipeIO()
  {
    CloseHandle(hread_);

    printf("~windowspipeio disconnect %s\n", name_.c_str());
    DisconnectNamedPipe(hpipe_);
    printf("~windowspipeio close %s\n", name_.c_str());
    CloseHandle(hpipe_);
  }

  std::string wait ()
  {
    DWORD dwRead;

    // wait for commands
    if (hpipe_ != INVALID_HANDLE_VALUE  &&  hread_ != INVALID_HANDLE_VALUE)
    {
      //db
      printf("waiting for connection for pipe %s handle %d\n", name_.c_str(), (int) hpipe_);
      bool stat   = ConnectNamedPipe(hpipe_, NULL);
      int  winerr = GetLastError();
      printf("  --> status %d error %d\n", stat, winerr);

      if (winerr == ERROR_PIPE_CONNECTED)
      { // connection is good

    // instead of going through implementing "overlapped i/o", we simply peek into the pipe to see if data is available
    DWORD nready = 0;
    if (PeekNamedPipe(hpipe_, NULL, 0, NULL, &nready, NULL) == 0)
      fprintf(stderr, "error %d peeking into pipe: %s\n", GetLastError(), strerror(errno));

    printf("pipe connected and peeked at %d bytes\n", nready);

    if (nready > 0)
    { // data available
      printf("  pipe %s has available %d bytes\n", name_.c_str(), nready);

      while (ReadFile(hread_, command_, CTL_INPUT_SIZE - 1, &dwRead, NULL) != FALSE)
      {
        /* add terminating zero */
        command_[dwRead] = '\0';
        //db
        printf("  read command (%d bytes): '%s'\n", dwRead, dwRead > 0 ? command_ : "");

        //todo: continue reading when more than CTL_INPUT_SIZE chars are available
        //todo: stop at \0? continue when called again
        return std::string(command_);
      }

      printf("  hmmm, read %d, but there should have been %d bytes (error %d)\n", dwRead, nready, GetLastError());
    }
    // else: no data available: timeout

    DisconnectNamedPipe(hpipe_);
    printf("disconnected from pipe (error %d)\n", GetLastError());
      }
      // else: no client connected: timeout

      //db
      printf("timeout\n");
      Sleep(timeout_);
      return "timeout";
    }
    else
    {
      fprintf(stderr, "error %d (invalid pipe handle): %s\n", errno, strerror(errno));
      return "ioerror";
    }
  }

private:
  std::string name_;    //< name of pipe
  int timeout_; //ms
  HANDLE hpipe_;
  HANDLE hread_;
  static const long CTL_INPUT_SIZE = 65535;
  char command_[CTL_INPUT_SIZE];
};

#endif

} // end namespace ISP

#endif // _PIPEIO_HPP_

0 个答案:

没有答案