为什么 unix-socket 文件在其创建者退出后仍然存在?我可以重复使用吗?

Hi-*_*gel 8 c++ unix sockets linux

我第一次使用 unix 套接字,我偶然发现了 unix 套接字文件没有被自动删除的问题。看来,在创建套接字后,我不能只是退出程序,而应该删除套接字文件。

但是为什么套接字文件在创建它的程序退出后没有被破坏?看起来应该有办法以某种方式将应用程序连接到这个剩余的文件,或者为什么它仍然存在?

下面是一个代码来说明(它创建一个unix套接字然后退出)

#include <cstdio>
#include <sys/socket.h>
#include <sys/un.h>

int CreateSocket(const char* filename) {
    if (strlen(filename) > sizeof(sockaddr_un::sun_path)) {
        puts("Too long filename!");
        return -1;
    }
    int fdSock = socket(AF_UNIX, SOCK_DGRAM, 0);
    if(fdSock == -1){
        perror("socket");
        return -1;
    }
    sockaddr_un server;
    server.sun_family = AF_UNIX;
    strcpy(server.sun_path, filename);
    if(bind(fdSock, (sockaddr*)&server, sizeof(sockaddr_un)) == -1){
        perror("socket");
        return -1;
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Den*_*din 6

摘自W. Richard Stevens所著的《The Sockets Networking API: UNIX\xc2\xae Network Programming Volume 1, Third Edition》一书的简短引用;比尔·芬纳;安德鲁·M·鲁道夫

\n\n

第15章。示例:Unix Domain Socket 的绑定。

\n\n
\n

首先删除路径名。我们绑定到套接字的路径名是命令行参数。但如果文件系统中已存在该路径名,则绑定将失败。因此,我们调用 unlink 来删除路径名(如果它已经存在)。如果不存在,unlink 将返回一个错误,我们会忽略该错误。

\n
\n\n

其实,你可以通过该路径来检测服务器是否存在。当服务器终止时,它必须删除一个文件,以使客户端了解没有服务器可以读取传入连接。例如,Docker是这样工作的:如果守护进程没有生成,那么docker.sock系统中就没有文件。

\n

  • 我认为您不能使用它来检查服务器是否存在,因为如果服务器崩溃,或者只是由于某种原因无法删除文件,则该文件仍然存在,而服务器不存在。无论如何,我最初想知道的是系统不删除该文件的原因。事实上,由于没有人解决这一点,看起来只是 unix 套接字 API 或其实现中的一个错误*(取决于 API 是否强制要求)* (2认同)

小智 1

我无法完全理解你的问题。我认为你不明白这个socket()机制。我会解释。这里的socket类似于指针或者文件描述符。有两个不同的程序:服务器和客户端。他们不依赖。服务器打开一个套接字并等待连接。客户端打开自己的套接字,连接到远程地址。服务器处理连接。这两个程序之间发生数据交换,然后(如果需要)连接终止。以下是最简单的 TCP/IP 客户端和服务器程序示例,它们展示了该socket()机制的工作原理。

服务器代码:

void * server (void)
{
    int listenfd, connfd;
    struct sockaddr_in servaddr;
    socklen_t lenkeepalive;
    struct net_pack recvline;
    int i,n;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(PORT);

    bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

    listen(listenfd, LISTENQ);

    int keepalive =1;
    lenkeepalive = sizeof(keepalive);
    if(setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, lenkeepalive) < 0)
    {
        perror("server: setsockopt()");
        shutdown(listenfd,SHUT_RDWR);
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    pthread_create (NULL, NULL, (void *)server_send, NULL);

    for(;;)
    {

        if(connfd = accept(listenfd, (SA *) NULL, NULL)>0){
            bzero(&recvline, sizeof(recvline));
            //try get msg from connected client
            while ( (recv(connfd, &recvline, MAXLINE, 0)) > 0)
            {
                printf("server: recvline.msg - %s\n",recvline.msg);
                bzero(&recvline, sizeof(recvline));
                /*
                    ...
                */
                // send answ

                encode(&recvline,"hello2",H); //make a msg

                if(send(connfd, &recvline, (strlen(recvline.msg)+4), MSG_NOSIGNAL); < 0)
                {
                    printf("server_send: send error %d (%s)\n",errno, strerror(errno));
                }

                //discard client connection
                shutdown(connfd,SHUT_RDWR);
                close(connfd);
            }

        }

    }

    // some error occurs    
    perror("server: accept()");
    shutdown(listenfd,SHUT_RDWR);
    close(listenfd);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

客户端代码

void * client (void)
{
    socklen_t           lenkeepalive;
    int                 sockfd, keepalive, n;
    struct net_pack recvline;       //pack for msg 
    struct sockaddr_in  servaddr;

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {printf("client: socket error %d ( %s )\n ",errno, strerror(errno));}

    keepalive = 1;
    lenkeepalive = sizeof(keepalive);
    if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, lenkeepalive) < 0)
    {
        perror("client: setsockopt()");
        shutdown(sockfd,SHUT_RDWR);
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(PORT);

    if (inet_pton(AF_INET, servip, &servaddr.sin_addr) <= 0)
        printf("client: inet_pton error for %s\r\n", servip);


    if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    {
        printf("client: connect to %s error %d ( %s )\r\n",servip, errno, strerror(errno));
    }
    else
    {
        // send a msg

        encode(&recvline,"hello",H); //make a msg

        n = send(sockfd, &recvline, (strlen(recvline.msg)+4), MSG_NOSIGNAL);
        if(n < 0)
        {printf("client: i cannot send, error %d (%s)\n",errno, strerror(errno));}

        bzero(&recvline, sizeof(recvline));

        //recv data from server
        while( (n = recv(sockfd, &recvline, MAXLINE, 0)) >= 0)
            {
                printf("client: recvline.msg - %s\n",recvline.msg);
                bzero(&recvline, sizeof(recvline));
            }
    }

    // some error occurs
    perror("client: recv()");
    shutdown(sockfd,SHUT_RDWR);
    close(sockfd);

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

在您的情况下,您应该使用 sockaddr_un 而不是 sockaddr_in,以及一些必要的标志,如AF_UNIXSOCK_DGRAM等。