C++ poll()是一个边缘触发函数吗?

C++ poll()是一个边缘触发函数吗?,c++,linux,networking,C++,Linux,Networking,我负责通过TCP连接导出数据的服务器。对于服务器传输的每个数据记录,它要求客户端发送一个简短的“\n”确认消息。我有一位客户声称,他发送的确认不是从web服务器读取的。以下是我在套接字上用于I/O的代码: bool can_send = true; char tx_buff[1024]; char rx_buff[1024]; struct pollfd poll_descriptor; int rcd; poll_descriptor.fd = socket_handle; poll_des

我负责通过TCP连接导出数据的服务器。对于服务器传输的每个数据记录,它要求客户端发送一个简短的“\n”确认消息。我有一位客户声称,他发送的确认不是从web服务器读取的。以下是我在套接字上用于I/O的代码:

bool can_send = true;
char tx_buff[1024];
char rx_buff[1024];
struct pollfd poll_descriptor;
int rcd;

poll_descriptor.fd = socket_handle;
poll_descriptor.events = POLLIN | POLLOUT;
poll_descriptor.revents = 0;
while(!should_quit && is_connected)
{
   // if we know that data can be written, we need to do this before we poll the OS for
   // events.  This will prevent the 100 msec latency that would otherwise occur
   fill_write_buffer(write_buffer);
   while(can_send && !should_quit && !write_buffer.empty())
   {
      uint4 tx_len = write_buffer.copy(tx_buff, sizeof(tx_buff));
      rcd = ::send(
         socket_handle,
         tx_buff,
         tx_len,
         0);
      if(rcd == -1 && errno != EINTR)
         throw SocketException("socket write failure");
      write_buffer.pop(rcd);
      if(rcd > 0)
         on_low_level_write(tx_buff, rcd);
      if(rcd < tx_len)
         can_send = false;
   }

   // we will use poll for up to 100 msec to determine whether the socket can be read or
   // written
   if(!can_send)
      poll_descriptor.events = POLLIN | POLLOUT;
   else
      poll_descriptor.events = POLLIN;
   poll(&poll_descriptor, 1, 100);

   // check to see if an error has occurred
   if((poll_descriptor.revents & POLLERR) != 0 ||
      (poll_descriptor.revents & POLLHUP) != 0 ||
      (poll_descriptor.revents & POLLNVAL) != 0)
      throw SocketException("socket hung up or socket error");

   // check to see if anything can be written
   if((poll_descriptor.revents & POLLOUT) != 0)
      can_send = true;

   // check to see if anything can be read
   if((poll_descriptor.revents & POLLIN) != 0)
   {
      ssize_t bytes_read;
      ssize_t total_bytes_read = 0;
      int bytes_remaining = 0;
      do
      {
         bytes_read = ::recv(
            socket_handle,
            rx_buff,
            sizeof(rx_buff),
            0);
         if(bytes_read > 0)
         {
            total_bytes_read += bytes_read;
            on_low_level_read(rx_buff,bytes_read);
         }
         else if(bytes_read == -1)
            throw SocketException("read failure");
         ioctl(
            socket_handle,
            FIONREAD,
            &bytes_remaining);
      }
      while(bytes_remaining != 0);

      // recv() will return 0 if the socket has been closed
      if(total_bytes_read > 0)
         read_event::cpost(this);
      else
      {
         is_connected = false;
         closed_event::cpost(this);
      }
   }
}
bool can\u send=true;
字符tx_buff[1024];
字符rx_buff[1024];
结构pollfd poll_描述符;
国际刚果民盟;
poll_descriptor.fd=套接字_句柄;
poll_descriptor.events=POLLIN | POLLOUT;
poll_descriptor.revents=0;
当(!应退出&&已连接)
{
//如果我们知道数据是可以写入的,我们需要在轮询操作系统之前写入数据
//事件。这将防止否则将发生的100毫秒延迟
填充写入缓冲区(写入缓冲区);
while(可以发送&&!应该退出&&!写入缓冲区.empty())
{
uint4 tx_len=写入缓冲区.copy(tx_buff,sizeof(tx_buff));
rcd=::发送(
插座和把手,
博夫,
德克萨斯州,
0);
如果(rcd==-1&&errno!=EINTR)
抛出SocketException(“套接字写入失败”);
写入缓冲区pop(rcd);
如果(rcd>0)
低电平写入(tx buff,rcd);
如果(rcd0)
{
总字节读取+=字节读取;
低电平读取(rx缓冲,字节读取);
}
else if(字节\u读取==-1)
抛出SocketException(“读取失败”);
ioctl(
插座和把手,
菲昂雷德,
&字节(剩余);
}
while(剩余字节数)=0;
//如果套接字已关闭,recv()将返回0
如果(总字节数\u读取>0)
读取事件::cpost(此);
其他的
{
is_connected=false;
关闭事件::cpost(此);
}
}
}

我编写这段代码的前提是poll()是一个级别触发函数,只要有数据要从套接字读取,就会立即解除阻塞。我所读到的一切似乎都支持这一假设。是否有我可能错过的原因会导致上述代码错过读取事件?

它不是边缘触发的。它总是水平触发的。不过,我必须阅读您的代码才能回答您的实际问题。但这回答了标题中的问题。:-)


我看不出代码中有什么明确的原因可以解释为什么您会看到所看到的行为。但是,您的问题范围比您介绍的代码要大得多,我不能假装这是一个完整的问题诊断。

根据您自己对问题的评估(即,当您希望能够阅读确认时,您被阻止进行
轮询),然后您最终会得到一个超时


如果客户的机器距离您的服务器超过50毫秒,则在收到确认之前,您的连接将始终超时,因为您只需等待100毫秒。这是因为数据到达客户至少需要50毫秒,确认返回至少需要50毫秒。

它是级别触发的。轮询时,如果套接字接收缓冲区中有数据,则POLLIN会激发,如果套接字发送缓冲区中有空间(几乎总是有空间),则POLLOUT会激发。

我认为问题不在您发布的代码中。虽然我确实怀疑您使用FIONREAD ioctl,但您可以通过检测小于缓冲区大小的读取来完成同样的事情。但是,您将它与
poll
结合使用的方式并不是问题。100ms是一个非常激进的超时。例如,Nagle超时比这个长。我会在任何网络上使用至少几秒钟的超时时间。因为所有读/写代码都在while循环中,所以我希望下一次迭代能够处理数据。@jontrantVein:我已经更新了我为什么提供它的答案。如果你有更多关于你的问题可能实际存在的细节,你应该在你的问题中提出它们。