C Don';我不理解为什么来自APUE的代码片段会取消连接到客户端unix域套接字的文件的链接
本书定义了3个定制功能:C Don';我不理解为什么来自APUE的代码片段会取消连接到客户端unix域套接字的文件的链接,c,unix-socket,unlink,C,Unix Socket,Unlink,本书定义了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 ch
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\u 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);
}
#包括“apue.h”
#包括
#包括
#包括
#包括
#define STALE 30/*客户端名称不能早于此(秒)*/
/*
*等待客户端连接到达,然后接受它。
*我们还从路径名获取客户端的用户ID
*在召唤我们之前,它必须绑定。
*如果一切正常,则返回新fd,
为什么服务器代码取消链接(名称)
附加到客户端套接字的文件
更准确地说,服务器正在删除连接到客户端套接字的文件路径。或者更通俗地说,客户端套接字的名称
回想一下,unlink()
不会删除某些进程中当前打开的命名对象;客户机的套接字可能在客户机中仍处于打开状态,因此unlink(name)
不会删除套接字。相反,它确保套接字在运行进程不再使用时被删除
它立即要做的是释放名称,以便可以使用不同的套接字重用该名称
那为什么要这样做呢?主要是为了使文件系统不会充满僵尸套接字名称。这无助于当前客户机重用该名称(例如,连接到其他服务),因为客户机在尝试使用该名称之前会取消该名称的链接。但是,僵尸名称可能是一个问题,因为未来的客户端进程具有不同的uid,而uid恰好被分配了相同的pid。未来的进程可能没有足够的权限取消名称链接,在这种情况下,它将无法使用此IPC机制(至少对于此库)
好的,那么为什么服务器会取消它的链接呢?服务器为stat
调用使用文件路径,而客户端无法知道何时会发生这种情况。由于尽快取消名称链接基本上是一个好主意,因此在这种情况下,服务器最好取消名称链接;它知道什么时候不再需要这个名字了
当然,给出的代码并不完美。有一些执行路径会导致某些名称未被取消链接(例如,如果服务器进程在错误时间崩溃)。但这些应该是罕见的。经验表明,客户端崩溃的频率远远高于服务器。我刚刚意识到unix套接字域可以命名,也可以不命名。服务器unix域套接字需要命名,因为客户端需要一种方法来知道您在哪里。Internet套接字通过端口号来实现这一点。看
然后我可以理解serv\u accept
中uname(name)
的行为
首先,检查客户端连接功能cli\u conn
code:
#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#define CLI_PATH "/var/tmp/"
#define CLI_PERM S_IRWXU /* rwx for user only */
/*
* Create a client endpoint and connect to a server.
* Returns fd if all OK, <0 on error.
*/
int
cli_conn(const char *name)
{
int fd, len, err, rval;
struct sockaddr_un un, sun;
int do_unlink = 0;
if (strlen(name) >= sizeof(un.sun_path)) {
errno = ENAMETOOLONG;
return(-1);
}
/* create a UNIX domain stream socket */
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return(-1);
/* fill socket address structure with our address */
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
sprintf(un.sun_path, "%s%05ld", CLI_PATH, (long)getpid());
printf("file is %s\n", un.sun_path);
len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
unlink(un.sun_path); /* in case it already exists */
if (bind(fd, (struct sockaddr *)&un, len) < 0) {
rval = -2;
goto errout;
}
if (chmod(un.sun_path, CLI_PERM) < 0) {
rval = -3;
do_unlink = 1;
goto errout;
}
/* fill socket address structure with server's address */
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
if (connect(fd, (struct sockaddr *)&sun, len) < 0) {
rval = -4;
do_unlink = 1;
goto errout;
}
return(fd);
errout:
err = errno;
close(fd);
if (do_unlink)
unlink(un.sun_path);
errno = err;
return(rval);
}
#包括“apue.h”
#包括
#包括
#包括
#定义CLI\u路径“/var/tmp/”
#仅为用户定义CLI_PERM S_IRWXU/*rwx*/
/*
*创建客户端端点并连接到服务器。
*如果一切正常,则返回fd,=sizeof(un.sun\u路径)){
errno=搪瓷;
返回(-1);
}
/*创建UNIX域流套接字*/
if((fd=socket(AF\u UNIX,SOCK\u STREAM,0))<0)
返回(-1);
/*用我们的地址填充套接字地址结构*/
memset(&un,0,sizeof(un));
un.sun_family=AF_UNIX;
sprintf(un.sun_路径,“%s%05ld”,CLI_路径,(长)getpid();
printf(“文件是%s\n”,un.sun\u路径);
len=偏移量(结构sockaddr\u un,sun\u path)+strlen(un.sun\u path);
取消链接(un.sun_路径);/*以防它已经存在*/
if(绑定(fd,(结构sockaddr*)&un,len)<0){
rval=-2;
后藤埃罗特;
}
如果(chmod(un.sun\u路径,CLI\u PERM)<0){
rval=-3;
do_unlink=1;
后藤埃罗特;
}
/*用服务器地址填充套接字地址结构*/
memset(&sun,0,sizeof(sun));
sun.sun_family=AF_UNIX;
strcpy(sun.sun\u路径,名称);
len=偏移量(结构sockaddr\u un,太阳路径)+strlen(名称);
if(连接(fd,(结构sockaddr*)和sun,len)<0){
rval=-4;
do_unlink=1;
后藤埃罗特;
}
返回(fd);
错误:
err=errno;
关闭(fd);
如果(不要取消链接)
取消链接(联合国新路);
errno=err;
返回(rval);
}
书中写道:
我们调用套接字来创建UNIX域套接字的客户端。我们
然后用特定于客户端的名称填写sockaddr\u un
结构。
我们不允许系统为我们选择默认地址,因为服务器将无法区分一个客户端和另一个客户端(如果我们
不要显式地将名称绑定到UNIX域套接字(内核)
代表我们隐式地将地址绑定到它,并且不会创建任何文件
在文件系统中表示套接字)。相反,我们绑定
自有地址-开发客户时通常不采取的步骤
使用套接字的程序
因此,我认为这一切都是为了使服务器能够重新格式化一组类似的客户端
例如:
使用客户端附加文件路径/tmp/cat
->客户端连接到服务器->服务器检查文件路径并知道它是“cat”客户端->服务器释放该文件路径的占用->可以创建另一个“cat”客户端
通过这种方式,服务器可以例如区分具有不同底层文件路径/tmp/cat
和/tmp/dog
的“猫”客户端和“狗”客户端