Libevent 仅在第二个 buffer_write 之后写入套接字

Posted

技术标签:

【中文标题】Libevent 仅在第二个 buffer_write 之后写入套接字【英文标题】:Libevent writes to the socket only after second buffer_write 【发布时间】:2013-04-14 04:03:28 【问题描述】:

Libevent 很棒,到目前为止我很喜欢它。但是,在回显服务器上,写入仅在第二次写入时发送到套接字。我的写作来自另一个线程,一个泵线程,它与数据库对话并进行一些最小的数据按摩。

我通过为写入设置回调来验证这一点:

bufferevent_setcb( GetBufferEvent(), DataAvailable, DataWritten, HandleSocketError, this );

调用 bufferevent_flush(m_bufferEvent, EV_READ|EV_WRITE, BEV_NORMAL) 似乎没有任何效果。

这是设置,以防万一我把它弄坏了。为了获得一些帮助,我极大地简化了代码库中的开销。这包括套接字的初始化,我的线程初始化等。这是一个多线程的应用程序,所以那里可能存在一些问题。我从这个开始:

m_LibEventInstance = event_base_new();
evthread_use_windows_threads();
m_listener = evconnlistener_new_bind( m_LibEventInstance, 
         OnAccept, 
         this,
         LEV_OPT_CLOSE_ON_FREE | LEV_OPT_CLOSE_ON_EXEC | LEV_OPT_REUSEABLE, 
         -1,// no maximum number of backlog connections
         (struct sockaddr*)&ListenAddress, socketSize );

   if (!m_listener) 
          perror("Couldn't create listener");
          return false;
   
   evconnlistener_set_error_cb( m_listener, OnSystemError );

AFAIK,这是从示例中复制和粘贴的,所以它应该可以工作。我的 OnAccept 执行以下操作:

void  OnAccept( evconnlistener* listenerObj, evutil_socket_t newConnectionId, sockaddr* ClientAddr, int socklen, void* context )

    // We got a new connection! Set up a bufferevent for it. 
    struct event_base*  base = evconnlistener_get_base( listenerObj );
    struct bufferevent* bufferEvent = bufferevent_socket_new( base, newConnectionId, BEV_OPT_CLOSE_ON_FREE );

   bufferevent_setcb( GetBufferEvent(), DataAvailable, DataWritten, 
                                   HandleSocketError, this );

  // We have to enable it before our callbacks will be called. 
  bufferevent_enable( GetBufferEvent(), EV_READ | EV_WRITE );

  DisableNagle( m_connectionId );

现在,我只需响应传入的数据并将其存储在缓冲区中以供以后处理。这是一个多线程的应用程序,所以我稍后会处理数据,按摩它,或者返回一个响应给客户端。

void     DataAvailable( struct bufferevent* bufferEventObj, void* arg )

const U32   MaxBufferSize = 8192;
   MyObj*   This = (MyObj*) arg;
   U8          data[ MaxBufferSize ];
   size_t      numBytesreceived;

   /* Read 8k at a time and send it to all connected clients. */
   while( 1 )
   
      numBytesreceived = bufferevent_read( bufferEventObj, data, sizeof( data ) );
      if( numBytesreceived <= 0 ) // nothing to send
      
         break;
      

      if( This )
      
         This->OnDataReceived( data, numBytesreceived );
      
   

最后发生的事情是,一旦我查找数据,打包到缓冲区中,然后在线程时间片上执行以下操作:

bufferevent_write( m_bufferEvent, buffer, bufferOffset );

它永远不会第一次发送。为了让它发送,我必须发送第二个充满数据的缓冲区。

这种行为让我很生气,我为此花了很多时间。有什么想法吗?

//--------------------------------------------- ----------

我终于放弃了,改用了这个 hack……只是没有足够的信息告诉我为什么 libevent 没有写入套接字。这很好用。

int result = send( m_connectionId, (const char* )buffer, bufferOffset, 0 );

【问题讨论】:

仅供参考:libevent 邮件列表非常友好。我在他们的帮助下调试了几个问题;所以如果有的话,我也会在那里问。 【参考方案1】:

我也遇到了这个问题!我花了一天时间解决这个问题。最后,我解决了。

当你调用event_base_dispatch的线程时,它会一直休眠直到任何信号量唤醒它。所以,当它休眠时,你调用bufferevent_write,bufferevent的fd添加到事件列表中,但直到下一次它才会是epoll。所以你必须在调用bufferevent_write 之后发送信号量来唤醒调度线程。您可以使用的方式是设置一个事件绑定对套接字并将其添加到event_base。然后在需要唤醒调度线程时发送 1 个字节。

【讨论】:

以上是关于Libevent 仅在第二个 buffer_write 之后写入套接字的主要内容,如果未能解决你的问题,请参考以下文章

在第二个查询中查询两个单独的表传递结果,同时仅在第二个查询返回 null 时保留第一个查询的结果

运行时错误,但仅在第二个循环中

SQL 表更改仅在第二个程序运行后可见

仅在第二个代码块中遇到“ORA-29283:无效文件操作”(第一个块也成功执行,它也使用 UTL_FILE)

APNG 在第二个页面刷新后不同步

SQL Reporting Services报告仅在第二次单击时加载