不理解为什么APUE的代码片段会取消链接到客户端unix域套接字的文件的链接

问题描述 投票:1回答:1

本书定义了3个自定义功能:

int serv_listen(const char *name);
//Returns: file descriptor to listen on if OK, negative value on error

int serv_accept(int listenfd, uid_t *uidptr);
//Returns: new file descriptor if OK, negative value on error

int cli_conn(const char *name);
//Returns: file descriptor if OK, negative value on error

serv_accept功能(图17.9)被服务器用来等待客户端的连接请求到达。到达后,系统会自动创建一个新的UNIX域套接字,将其连接到客户端的套接字,然后将新套接字返回到服务器。此外,客户端的有效用户ID存储在要存储的内存中。uidptr点。


serv_accept功能代码和描述:

#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <errno.h>

#define STALE   30  /* client's name can't be older than this (sec) */

/*
 * Wait for a client connection to arrive, and accept it.
 * We also obtain the client's user ID from the pathname
 * that it must bind before calling us.
 * Returns new fd if all OK, <0 on error
 */
int
serv_accept(int listenfd, uid_t *uidptr)
{
    int                 clifd, err, rval;
    socklen_t           len;
    time_t              staletime;
    struct sockaddr_un  un;
    struct stat         statbuf;
    char                *name;

    /* allocate enough space for longest name plus terminating null */
    if ((name = malloc(sizeof(un.sun_path + 1))) == NULL)
        return(-1);
    len = sizeof(un);
    if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0) {
        free(name);
        return(-2);     /* often errno=EINTR, if signal caught */
    }

    /* obtain the client's uid from its calling address */
    len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */
    memcpy(name, un.sun_path, len);
    name[len] = 0;          /* null terminate */
    if (stat(name, &statbuf) < 0) {
        rval = -3;
        goto errout;
    }

#ifdef  S_ISSOCK    /* not defined for SVR4 */
    if (S_ISSOCK(statbuf.st_mode) == 0) {
        rval = -4;      /* not a socket */
        goto errout;
    }
#endif

    if ((statbuf.st_mode & (S_IRWXG | S_IRWXO)) ||
        (statbuf.st_mode & S_IRWXU) != S_IRWXU) {
          rval = -5;    /* is not rwx------ */
          goto errout;
    }

    staletime = time(NULL) - STALE;
    if (statbuf.st_atime < staletime ||
        statbuf.st_ctime < staletime ||
        statbuf.st_mtime < staletime) {
          rval = -6;    /* i-node is too old */
          goto errout;
    }

    if (uidptr != NULL)
        *uidptr = statbuf.st_uid;   /* return uid of caller */
    unlink(name);       /* we're done with pathname now */
    free(name);
    return(clifd);

errout:
    err = errno;
    close(clifd);
    free(name);
    errno = err;
    return(rval);
}

...然后,我们调用stat以验证路径名确实是套接字,并且该权限仅允许用户读取,用户写入和用户执行。我们还验证了与套接字的时间不超过30秒。

如果所有这些检查都可以,我们假设客户的身份(其有效的用户ID)是套接字的所有者。

为什么服务器代码unlink(name)附加到客户端套接字的文件?

其他2个功能代码通过链接提供:

https://wandbox.org/permlink/jq5BajJYLgoh4yO6

c unix-socket unlink
1个回答
1
投票

为什么服务器代码unlink(name)附加到客户端套接字的文件?

更准确地说,服务器正在删除附加到客户端套接字的filepath。或更通俗地说,是客户端的套接字名称。

回想一下unlink()不会删除某些进程中当前打开的命名对象;客户端的套接字可能仍在客户端中打开,因此unlink(name)尚未删除该套接字。相反,它确保套接字在运行中的进程不再使用时将被删除。

它立即执行的操作是释放名称,以便可以使用其他套接字重用该名称。

那为什么呢?通常是为了使文件系统不会充满僵尸套接字名称。这无助于当前客户端重用该名称(例如,连接到其他服务),因为无论如何,客户端都会在尝试使用该名称之前取消链接。但是对于具有不同uid(恰好被分配了相同pid)的另一个将来的客户端进程,僵尸名称可能是一个问题。将来的过程可能没有足够的权限来取消名称的链接,在这种情况下,它最终将无法使用此IPC机制(至少对于此库而言)。

好吧,为什么它不链接由服务器?服务器使用stat调用的文件路径,而客户端无法知道何时发生。由于基本上最好尽快取消链接名称,因此在这种情况下,服务器最好取消链接名称。它知道何时不再需要该名称。

当然,所提供的代码并不完美。有执行路径将导致某些名称未取消链接(例如,如果服务器进程在错误的时间崩溃)。但是这些应该很少见。经验表明,客户端崩溃多于服务器。

© www.soinside.com 2019 - 2024. All rights reserved.