bind() 错误 98 - “地址已在使用中”

Beh*_*oth 2 c++ linux server

如果我在 Linux 上启动本地服务器,我会收到“地址已在使用中”错误。

我用setsockopt(SO_REUSEADDR)但没有帮助。我怎么解决这个问题?代码:

#include <iostream>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
using namespace std;

int main() {
    sockaddr_un s_addr;
    s_addr.sun_family = AF_UNIX;
    strcpy(s_addr.sun_path, "server");

    int s_descriptor = socket(AF_UNIX, SOCK_STREAM, 0);

    const int par = 1;
    if (setsockopt(s_descriptor, SOL_SOCKET, SO_REUSEADDR, &par, sizeof(int)) < 0){
        cout << "Error setsockopt()";
        return -1;
    }

    if (bind(s_descriptor, (sockaddr *)&s_addr, sizeof(s_addr)) < 0) {
        cout << "Error with bind()\n";
        cout << strerror(errno);
        return -2;
    }

    close(s_descriptor);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Sam*_*hik 5

您正在尝试创建AF_UNIX套接字。AF_UNIX套接字创建一个文件系统管道节点来处理连接。

在这种情况下EADDRINUSE意味着文件系统节点已经存在。实际上可能没有什么东西在监听它,但它确实存在,并且bind()因此而失败。这是套接字工作方式的一个怪癖AF_UNIXSO_REUSEADDR是针对AF_INET或套接字的,对于套接字AF_INET6没有多大作用。AF_UNIX

strcpy(s_addr.sun_path, "server");
Run Code Online (Sandbox Code Playgroud)

您会惊讶地发现server当前目录中有一个特殊的管道文件。这就是罪魁祸首。解决方案很简单:

unlink("server");
Run Code Online (Sandbox Code Playgroud)

就在您之前bind(),现在您可以绑定它。

如果您一直在关注,您现在会想知道是否有任何事情会阻止服务器的多个实例启动、创建和bind()连接到此AF_UNIX套接字,并粗鲁地unlink连接其前身的侦听套接字,从而使您拥有服务器进程的多个实例,除了一个之外的所有实例他们现在完全没用了。这是正确的,没有什么可以阻止你这样做。

这就是为什么AF_UNIX服务器通常会采用其他方法来确保只有一个实例正在运行,例如单独的锁定文件。但这将是一个单独的问题......

  • 是的,退出时删除套接字文件是一种方法。现在你必须弄清楚如何处理 SIGKILL...另一种方法是通过尝试 connect() 到现有套接字来处理 bind() 失败,并查看是否有人在家,如果没有,则“取消链接”并移入.但这仍然存在竞争条件。所以,最后,你会不情愿地得出结论,你需要一个锁定文件...... (2认同)