Boost套接字关闭时出现坏的文件描述符

5

我正在使用Boost 1.45 ASIO处理一个在Windows和Mac上运行的应用程序中的一些套接字连接。在Windows上,以下代码不会导致任何错误,并且我的套接字可以干净地关闭。但是在Mac上,无论是shutdown还是close函数(如果我将其注释掉)都会给我“坏文件描述符”错误。在调用此代码之前,套接字正常工作。但是一旦我调用shutdown或close,就会出现错误。有什么想法可能是怎么回事?

if(socket.is_open())
{
    socket.shutdown(socket.both);
    socket.close();
}

如果您能澄清此代码片段的意图,那将有所帮助。通常情况下,~socket() dtor会关闭底层本地描述符类型。您明确关闭它是否有原因? - Sam Miller
我原本以为你需要关闭套接字——这个错误似乎并没有引起任何问题(除了错误本身),所以如果我不需要显式地关闭套接字,那么我会很高兴地将其删除并继续我的生活。因此,为了澄清,我是否不需要调用shutdown或close? - Matt McMinn
1
我建议不要忽略这个错误。在 close 上出现 "Bad file descriptor" 通常意味着文件描述符已经被关闭,这是一个潜在的严重错误,因为描述符会被重新使用。例如,如果您的程序错误地两次关闭(比如)描述符5,而您(或另一个线程!)恰好在其中创建了一个新的描述符,则新的描述符也将是5,并且错误的 close 将关闭它。因此,虽然我不知道您问题的答案,但我强烈建议找出根本原因,而不是通过析构函数忽略它或“把它藏起来”。 - Nemo
3个回答

7
close 出现 "Bad file descriptor" 错误通常意味着该描述符已经被关闭。这通常是因为程序中某个完全不相关的部分存在双重关闭 bug。
这种 bug 是具有感染性的。如果您的程序关闭了同一个描述符两次,并且在此期间它被重新分配,那么第二个 close 将会在无关的对象身上关闭其描述符。然后当该对象关闭其描述符时,实际上可能会关闭另一个对象的描述符......以此类推,直到排队中的最后一个对象得到 "bad file descriptor" 错误。
这是 (a) 描述符是全局状态和 (b) Unix 要求任何对 open/socket 等的调用都要分配最低编号的未使用描述符 的副作用。
我所知道的唯一调试方法是使用像 strace (Linux) 或 dtrace (Mac) 这样的工具来监视所有文件描述符的创建和销毁。(好吧,也许不是唯一的方法。我曾经写过一个复杂的 LD_PRELOAD hack 来拦截每次对 openclose 的调用,以确定哪个线程在双重关闭其描述符,因为第二个 close 正在摧毁另一个线程正在使用的描述符...)祝您好运。

2
if(socket.is_open())
{
    socket.shutdown(socket.both);
    socket.close();
}

除非你有充分的理由这样做,否则我建议让socket::~socket()析构函数关闭底层本地文件描述符。如果你担心泄漏描述符,请使用像valgrind这样的工具来分析你的程序。

这并不是真正忽略错误。asio库抽象了描述符的本地底层类型,并且不需要显式关闭以防止资源泄漏。如果程序中存在双重关闭描述符的错误,那么问题很可能出在其他地方。 - Sam Miller
错误可能在其他地方,但是错误发生在这里... 我非常确定 Boost 的开发者不会从他们的析构函数中抛出任何异常。因此,你的建议是忽略问题而不是解决问题。(依赖析构函数来关闭文件并不能检测错误,因此在一般情况下我认为这种写法是不好的。在这种情况下,无论问题是什么,这种写法肯定是在忽略一个真正的问题。) - Nemo
我有点勉强地接受这个答案 - 我同意Nemo的看法,可能有其他地方出现了错误,但是在详细检查了我的程序后,我找不到它 - 这是我唯一的关闭,所以如果我把它拿出来让析构函数处理,我就不会进行任何显式的关闭。对于我的程序来说,忽略它是合理的,因为此时程序无论如何都要关闭,但是为了记录,走这条路之前请检查是否有额外的关闭,正如Nemo所提到的那样。 - Matt McMinn

1

我遇到了同样的问题:在Windows上一切正常,在Linux上,根据套接字状态会抛出异常(如果我没记错的话)。

除了Sam的答案之外,另一个选择是使用虚拟错误代码来静默忽略异常(如果确实发生)。请参阅asio文档中的closeshutdown重载。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接