当前位置: 首页 > 知识库问答 >
问题:

Epoll零recv()和负(EAGAIN)send()

赵渊
2023-03-14

我最后几天都在和epoll做斗争,现在我在一个不知道的地方;)

网络上有很多信息,很明显在系统里,伙计,但我可能服用过量,有点困惑。

在我的服务器应用程序(到nginx的后端)中,我正在以ET模式等待来自客户端的数据:

    std::array<char, BatchSize> batch;
    ssize_t total_count = 0, count = 0;
    do {
        count = recv(_handle, batch.begin(), batch.size(), MSG_DONTWAIT);

        if (0 == count && 0 == total_count) {
            /// @??? Do I need to wait zero just on first iteration?
            close();
            return total_count;
        } else if (count < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                /// @??? Will be back with next EPOLLIN?!
                break ;
            }
            _last_error = errno;
            /// @brief just log the error               
            return 0;
        }

        if (count > 0) {
            total_count += count;
            /// DATA!
            if (count < batch.size()) {
                /// @??? Received less than requested - no sense to repeat recv, otherwise I need one more turn?! 
                return total_count;
            }
        }           
    } while (count > 0);

据我所知,send()还可能返回-1和EAGAIN,这意味着我应该在EPOLLOUT上订阅,并等待内核缓冲区空闲到可以从我的ME接收一些数据时。这样对吗?但如果客户不等那么久呢?或者,我可以调用阻塞发送(无论如何,我是在一个不同的线程上发送),并保证我发送到内核的所有内容都将真正发送到对等体,因为setsockopt(SO_LINGER)?最后一个猜测是我要求确认的:我被允许同时读写,但是n>1次并发写是一场数据竞赛,我必须处理的一切都是互斥体。

感谢每一个至少读到最后的人:)

共有1个答案

诸葛卜霸
2023-03-14

问题:在EPOLLRDHUP处理过程中,当recv()返回零和shutdown(SHUT_WR)时,我是否必须关闭socket以便读取?

不,没有什么特别的理由去执行那些有点复杂的动作序列。

recv()接收到0返回值后,您知道在网络层连接至少是半关闭的。您将不会从它收到任何进一步的东西,我也不会期望EPoll在边缘触发模式下操作,进一步宣传它准备好阅读,但这本身并不需要任何特定的操作。如果写入端保持打开(从本地角度来看),那么您可以继续在其上write()send(),尽管您将没有确认接收到所发送内容的机制。

            /// @??? Do I need to wait zero just on first iteration?

无论是否已经接收到任何数据,都应该对返回值0执行操作。不一定是相同的操作,但无论哪种方式,您都应该安排一种或另一种方法,使它脱离EPoll兴趣集,很可能通过关闭它。

                /// @??? Will be back with next EPOLLIN?!

如果recv()出现eAgaineWouldBlock故障,那么EPoll很可能在将来的调用中发出读取就绪的信号。但不一定是下一个。

                /// @??? Received less than requested - no sense to repeat recv, otherwise I need one more turn?! 

收到少于你所要求的是一个你应该随时准备的可能性。这并不一定意味着另一个recv()不会返回任何数据,如果您在EPoll中使用边缘触发模式,那么假设相反的情况是危险的。在这种情况下,您应该在非阻塞模式下或使用msg_dontwait继续recv(),直到使用eAgaineWouldBlock调用失败。

据我所知,send()还可能返回-1和EAGAIN,这意味着我应该在EPOLLOUT上订阅,并等待内核缓冲区空闲到可以从我的ME接收一些数据时。这样对吗?

send()当然会因eAgaineWouldBlock而失败。它也可以成功,但发送的字节比您请求的少,您应该为此做好准备。无论哪种方式,通过订阅文件描述符上的EPOLLOUT事件来响应都是合理的,以便稍后恢复发送。

但如果客户不等那么久呢?

不过,至少在本地方面,我看不出so_linger与此有什么关系。内核将尽一切努力将您已经通过send()调用调度的数据发送到远程对等体,即使您在数据仍被缓冲时close()套接字也是如此,而不考虑so_linger的值。该选项的目的是在连接关闭后接收(并丢弃)与连接相关联的散乱数据,以便它们不会意外地传递到另一个套接字。

但是,这些都不能保证数据成功地传递到远程对等体。什么也不能保证。

最后一个猜测是我要求确认的:我被允许同时读写,但是n>1次并发写是一场数据竞赛,我必须处理的一切都是互斥体。

 类似资料:
  • 我正在实现一个UNIX域套接字进程间通信代码,在试图从套接字读取时,我随机地碰到了这个错误--“errno 11:资源暂时不可用”。我使用MSG_PEEK从套接字读取字节数,并为接收缓冲区分配字节数,并读取实际数据。 套接字是一个阻塞套接字,我没有任何非阻塞的代码(总之,接受/读/写)。在阻塞套接字读取中,有什么可能导致这种情况的指针吗?在MSG_PEEK的手册页中,当socket标记为非阻塞时,

  • 其实所有的I/O都是轮询的方法,只不过实现的层面不同罢了. 这个问题可能有点深入了,但相信能回答出这个问题是对I/O多路复用有很好的了解了.其中tornado使用的就是epoll的. selec,poll和epoll区别总结 基本上select有3个缺点: 连接数受限 查找配对速度慢 数据由内核拷贝到用户态 poll改善了第一个缺点 epoll改了三个缺点. 关于epoll的: http://ww

  • 描述 (Description) 此函数在SOCKET上接收尝试读取LENGTH字节的消息,将读取的数据放入变量SCALAR.FLAGS参数采用与函数所基于的recvfrom()系统函数相同的值。 与套接字通信时,这提供了一种比sysread函数或基于行的运算符更可靠的读取固定长度数据的方法。 语法 (Syntax) 以下是此函数的简单语法 - recv SOCKET, SCALAR, LEN,

  • recv(经socket接收数据) 相关函数 recvfrom,recvmsg,send,sendto,socket 表头文件 #include<sys/types.h> #include<sys/socket.h> 定义函数 int recv(int s,void *buf,int len,unsigned int flags); 函数说明 recv()用来接收远端主机经指定的socket传来的

  • recv 经socket接收数据 相关函数 recvfrom,recvmsg,send,sendto,socket 表头文件 #include<sys/types.h> #include<sys/socket.h> 定义函数 int recv(int s, void *buf, int len, unsigned int flags); 函数说明 recv()用来接收远端主机经指定的socke

  • osx和Win的文档中写道:“对于TCP套接字,返回值0表示对等方关闭了连接的一半。” 2)阻塞套接字的问题:在阻塞套接字上,我希望SO_RCVTIMEO和SO_SNDTIMEO有一个默认值,比如30秒(用getsockopt检查)。但是为什么它们被设置为0?