c - 不明白为什么来自 APUE 的代码片段会取消链接到客户端 unix 域套接字的文件
问题描述
本书定义了 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 秒。如果所有这些检查都OK,我们假设客户端的身份(它的有效用户ID)是套接字的所有者。
为什么服务器编码unlink(name)
附加到客户端套接字的文件?
其他 2 个功能代码通过链接提供:
解决方案
为什么服务器编码
unlink(name)
附加到客户端套接字的文件?
更准确地说,服务器正在删除附加到客户端套接字的文件路径。或者更通俗地说,是客户端的套接字名称。
回想一下,unlink()
不会删除当前在某些进程中打开的命名对象;客户端的套接字可能仍在客户端中打开,因此unlink(name)
尚未删除套接字。相反,它确保套接字不再被正在运行的进程使用时将被删除。
它立即做的是释放名称,以便名称可以与不同的套接字重用。
那么为什么要这样做呢?主要是为了使文件系统不会被僵尸套接字名称填满。它无助于当前客户端重用名称(例如连接到不同的服务),因为客户端无论如何都会在尝试使用名称之前取消链接名称。但是对于具有不同 uid 且恰好被分配了相同 pid 的不同未来客户端进程来说,僵尸名称可能是一个问题。未来的进程可能没有足够的权限来取消链接名称,在这种情况下,它将最终无法使用此 IPC 机制(至少与此库一起使用)。
好的,那为什么它被服务器取消链接?服务器使用文件路径进行stat
调用,客户端无法知道何时发生。由于尽快取消链接名称基本上是一个好主意,因此在这种情况下最好让服务器取消链接名称;它知道何时不再需要该名称。
当然,呈现的代码并不完美。有一些执行路径会导致某些名称没有被取消链接(例如,如果服务器进程在错误的时间崩溃)。但这些应该很少见。经验表明,客户端比服务器更频繁地崩溃。
推荐阅读
- powershell - 如何从 JavaFX 执行 PowerShell 命令
- asp.net-mvc - 如何在 axios.get(apiendpoint) 中传递 url?
- javascript - Gatsby 部署到移动设备上的 github 页面从不加载图像和钩子不起作用
- javascript - VanillaJS - 为什么这个 JS 类的导入/导出不起作用?
- sql-server - 通过 ODBC 从 MS SQL Server 导入到 MS Access 丢失了所有索引
- vuejs2 - 如何在 v-html 中使用 vuetify 的 v-img
- python - 更新布局中的 Pyqt 小部件
- javafx - 阻止盒子移动
- excel - Excel公式查找值在按组动态范围内出现的次数
- python - tensorlayer Seq2seq模型中的model.train()函数有什么作用