套接字:为啥阻塞 read() 会因 ENOTCONN 而失败?

Posted

技术标签:

【中文标题】套接字:为啥阻塞 read() 会因 ENOTCONN 而失败?【英文标题】:Sockets: Why does a blocking read() fail with ENOTCONN?套接字:为什么阻塞 read() 会因 ENOTCONN 而失败? 【发布时间】:2011-05-16 13:41:06 【问题描述】:

我正在尝试从阻塞套接字读取,但我想知道 read() 返回 -1,我认为这意味着当前没有要读取的数据 - 我希望它会阻塞,直到它可以读取的数量字节。

我还尝试确保套接字处于阻塞模式并使用以下方法设置高超时:

int setBlockingIO(int fd)

       int flags = fcntl(fd, F_GETFL);
       fcntl(fd, F_SETFL, flags & (~O_NONBLOCK));
       int nTimeout = 30000; // 30 seconds
       setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&nTimeout, sizeof(int));

但这并没有改变任何东西。

我的问题:

我该怎么办read()really block? 是否有一些我可能遇到的陷阱? (我的程序有错误?)

我知道有关于这个主题的another question,但我找不到我的问题的答案。

更新

在不设置超时的情况下,read() 也立即(主观地)返回 -1

更新 2

errno 是 107 (ENOTCONN, Transport endpoint is not connected). 但同时客户端并没有关闭连接(由write()之后的很长的sleep()保证)

【问题讨论】:

man read 并阅读 errno 以确定发生的错误,不一定按此顺序。 【参考方案1】:

你期待什么?你说你有一个非阻塞套接字,所以它当然不会阻塞。 read 上的非阻塞套接字的行为是,如果数据可供读取,则立即返回一些数据(可能比请求的数量短),并在 errno 设置为 EAGAIN 或 @ 时返回 -1 987654324@ 如果没有可用数据。

如果您不想要非阻塞行为,为什么将套接字设置为非阻塞?

编辑: Grr,你改变了你的问题。 ENOTCONN 的原因是您试图从未连接的套接字中读取。除非通过acceptsocketpair 获得套接字,否则您必须在其上调用connect 将其连接到远程地址,然后read 才能工作。

【讨论】:

【参考方案2】:

这意味着要么发生了超时,要么可能是信号中断了读取。您可以在errno.h 中使用errno 的结果来查看错误是什么,如果您希望错误采用人类可读的格式,您可以使用strerror()perror() 来自string.h 或@ 987654327@

更新:根据 POSIX 规范,您应该将 struct timeval(在 sys/time.h 中定义)设置为所需的秒数和微秒数,然后超时发生到 setsockopt当指定SO_RCVTIMEO 标志而不是将int 转换为const char* 时。因此,即使您的客户端现在可能行为不端并导致不同的错误,如果您向函数发送错误的参数类型,您仍然可能会遇到更进一步的问题。

【讨论】:

谢谢。原因是连接已经被客户端关闭(因为意外的客户端行为,见***.com/questions/6018974/…) 修复客户端后,同样的错误又回来了。我已经更新了问题。 好的,我已经为我的答案添加了更新,希望对您有所帮助。 如果上面更新的答案没有帮助,如果您在没有O_NOBLOCK 设置或任何超时的情况下在套接字上使用默认配置会发生什么? read() 是否仍会立即返回? 是的,行为是一样的。【参考方案3】:

原因在于errno。可能的情况:

[ECONNRESET] d 参数指的是一个套接字,而远程 套接字端被强制关闭。 [EAGAIN] 文件被标记为非阻塞 I/O,并且没有数据 可以阅读了。

基本上,您可以使用以下方法进行非阻塞阻塞:

do

    read(...);
 while(errno == EAGAIN);

【讨论】:

【参考方案4】:

超时时返回-1。

不要设置超时,它会永远阻塞(好吧,至少在套接字仍然连接时,无论如何)。

【讨论】:

超时设置为 30 秒,但似乎立即返回( 【参考方案5】:

我已经解决了这个问题。 谢谢你们所有的cmets,他们帮了很大的忙。

问题在于使用了错误的文件描述符

我将服务器套接字的文件描述符传递给read(),而不是accept()返回的客户端套接字的文件描述符。

【讨论】:

很高兴你发现了问题:-) 那么你可以接受告诉你检查errno的答案之一,或者任何帮助你解决问题的方法。【参考方案6】:

read() 可能会阻塞,因此如果没有可用数据,它将返回-1 并将errno 设置为EAGAINEWOULDBLOCK

如果您想使用read(),您可以使用select()poll() 等待套接字上的数据可用。

【讨论】:

这是错误的。在阻塞模式下, read() 不会返回 EAGAIN (除非达到(可选)超时)。您正在描述非阻塞读取。

以上是关于套接字:为啥阻塞 read() 会因 ENOTCONN 而失败?的主要内容,如果未能解决你的问题,请参考以下文章

如何在非阻塞套接字上处理 OpenSSL SSL_ERROR_WANT_READ / WANT_WRITE

网络I/O模型--04非阻塞模式(解除accept() read()方法阻塞)的基础上加入多线程技术

为啥套接字不设置为非阻塞模式?

为啥非阻塞套接字在connect() 或accept() 之前是可写的?

非阻塞套接字仍然可以在 OpenSSL 中阻塞吗?

为什么IO多路复用需要采用非阻塞式IO