Libevent bufferevent 套接字刷新

Posted

技术标签:

【中文标题】Libevent bufferevent 套接字刷新【英文标题】:Libevent bufferevent socket flush 【发布时间】:2013-08-17 12:41:32 【问题描述】:

我会保持简短。如何以阻塞方式将bufferevent 输出缓冲区中等待的数据直接刷新到套接字。

在进行异步写入(使用evbuffer_add)后关闭我的套接字包装类时,libevent 吐出epoll 错误,表示尝试写入无效的 fd。我需要能够将挂起的 libevent 数据刷新到套接字,有什么建议吗?

注意:具体错误是 Epoll MOD(4) on fd 9 failed。旧事件是 6;读取变化为 2 (del);写入更改为 0(无):文件描述符错误。

【问题讨论】:

【参考方案1】:

您是否尝试过禁用套接字延迟?禁用套接字延迟会导致套接字在关闭套接字之前不等待未发送的数据。

struct linger linger;
memset(&linger, 0, sizeof(struct linger));
retVal = setsockopt(sock, SOL_SOCKET, SO_LINGER, (const void*)&linger, sizeof(struct linger));

【讨论】:

问题是 libevent 实际上并没有将所有数据写入套接字,数据留在 libevents 内部缓冲区中,截至目前我关闭套接字,而数据仍在缓冲区中等待。我想将 libevent 缓冲区刷新到套接字然后关闭它。禁用套接字延迟没有帮助。 我认为bufferevent_flush 还不能与网络套接字一起使用,但是您尝试过吗? 如果bufferevent_flush() 不起作用,那么我会在创建缓冲区事件时尝试设置BEV_OPT_CLOSE_ON_FREE 标志。此外,bufferevents 是内部引用计数的,所以如果 bufferevent 在你释放它时有挂起的延迟回调,它不会被删除,直到回调完成。 只有当我从回调函数内部释放bufferevent时才会出现错误。 别担心 - 您的 BEV_OPT_CLOSE_ON_FREE 建议有效。发布答案以防万一它可能对其他人有所帮助,谢谢:)【参考方案2】:

对于任何可能遇到相同错误的人,我发现了我的问题 - 感谢 Inge Henriksen。

class ASocket


    // ...

    ~ASocket()
    
        if(m_handle.bev)
        
            bufferevent_free(m_handle.bev);
        

        if(m_handle.fd >= 0)
            ::close(m_handle.fd);
    

    // ...

删除异步套接字对象 (ASocket) 后,bufferevent 将被释放(如果存在)并且套接字将被删除 - libevent 将继续在关闭的套接字上运行。请注意,bufferevent_free,如http://www.wangafu.net/~nickm/libevent-book/Ref6_bufferevent.html#_freeing_a_bufferevent 所述,但不在 Doxygen 文档页面上,在调用 bufferevent_free 函数时不会释放 bufferevent,而是:

但是,bufferevent_free() 函数会尝试尽快释放 bufferevent。

这是这样修复的:

class ASocket


    // ...

    // If bufferevent exists, it must be created with
    // the BEV_OPT_CLOSE_ON_FREE flag.
    ~ASocket()
    
        if(m_handle.bev)
        
            bufferevent_free(m_handle.bev);
        
        else
        

            if(m_handle.fd >= 0)
                ::close(m_handle.fd);
        
    

    // ...

如果套接字有bufferevent,它会被释放,一旦完成,libevent 将关闭套接字。

【讨论】:

你应该把这个帖子放在你的问题中,只需添加一个名为“更新:”的行并添加这个帖子,然后删除这个帖子并接受我的回答。当其他人在您之前回答了您的问题时,使用该解决方案创建一个新帖子并不是 SO 的常见做法。 @IngeHenriksen 实际上是这样。自我回答完全没问题。 +1,我将您的“不是答案”标记标记为无效。 我对您表示同情并感谢您的帮助,但是标记和回答不是正确答案但将我指向正确的方向作为正确答案似乎适得其反。答案甚至没有提到 BEV_OPT_CLOSE_ON_FREE,即在 cmets 中。如果您更新答案以包含解决方案,我将很乐意接受。我同意@Doorknob。 你的回答对我来说是无价的,但“设置 BEV_OPT_CLOSE_ON_FREE”并没有尽可能简单的回答(我重申,不是在答案本身,而是在 cmets)所以我决定用代码给出一个具体的答案,以防有人可能犯同样的错误。 感谢您的解决方案,但我在 bufferevent_free 中找不到任何关于刷新挂起数据的阻塞代码。 bufferevent_free 只是清除回调,取消 ctrl 数据,然后 decref_and_unlock bufferevent。我目前正在自己​​调用 evbuffer_write 函数来尝试刷新数据。但是 evbuffer_write 中的 write() 可能仍有剩余数据。我正在开发 libevent-2.0.21-stable【参考方案3】:

我无法在回调本身内部执行此操作,但可以使用另一个回调执行此操作,请参阅https://github.com/libevent/libevent/blob/master/sample/le-proxy.c

if (partner) 
                    /* Flush all pending data */
                    readcb(bev, ctx);

                    if (evbuffer_get_length(
                                bufferevent_get_output(partner))) 
                            /* We still have to flush data from the other
                             * side, but when that's done, close the other
                             * side. */
                            bufferevent_setcb(partner,
                                NULL, close_on_finished_writecb,
                                eventcb, NULL);
                            bufferevent_disable(partner, EV_READ);
                     else 
                            /* We have nothing left to say to the other
                             * side; close it. */
                            bufferevent_free(partner);
                    
             

【讨论】:

以上是关于Libevent bufferevent 套接字刷新的主要内容,如果未能解决你的问题,请参考以下文章

Libevent bufferevent 的 evbuffer_add

Libevent源码分析--- bufferevent

Libevent源码分析--- bufferevent

Libevent源码分析--- bufferevent

libevent & bufferevent

libevent源码分析:bufferevent