'bind()' unix域套接字客户端进程有什么用途吗?

ev7lccsx  于 2022-11-04  发布在  Unix
关注(0)|答案(4)|浏览(233)

当使用AF_UNIX(unix域套接字)时,是否有任何应用程序可以在一个从不调用listen()的进程中调用bind()
在我的系统编程讲座和实验中,我们被要求在一个unix域套接字客户端进程上调用bind()。是否有任何文档记录的、未文档记录的、或实际的应用程序可以在一个 * 仅限客户端*unix域套接字进程上调用bind?根据我的理解,bind()创建了一个特殊的套接字文件,这是一个服务器进程将承担的责任。这是正确的吗?
下面是一个示例代码,基于课堂上讨论的概念:

服务器. c


# include <stdio.h>

# include <unistd.h>

# include <sys/types.h>

# include <sys/socket.h>

int main() {
    int s0, s1;
    struct sockaddr sa0 = {AF_UNIX, "a"};

    s0 = socket(AF_UNIX, SOCK_STREAM, 0);

    bind(s0, &sa0, sizeof(sa0) + sizeof("a"));
    listen(s0, 1);
    for (;;) {
        s1 = accept(s0, NULL, NULL);
        puts("connected!");
        for (;;) {
            char text[1];
            ssize_t n = read(s1, &text, sizeof(text));
            if (n < 1) {
                break;
            }
            putchar(text[0]);
        }
        close(s1);
    }
    close(s0);
    unlink("a");
    return 0;
}

客户端. c


# include <stdio.h>

# include <unistd.h>

# include <sys/types.h>

# include <sys/socket.h>

int main() {
    int s0;
    struct sockaddr sa0 = {AF_UNIX, "b"};
    struct sockaddr sa1 = {AF_UNIX, "a"};
    socklen_t sa1_len;

    s0 = socket(AF_UNIX, SOCK_STREAM, 0);
    bind(s0, &sa0, sizeof(sa0) + sizeof("b")); // What does this do??
    connect(s0, &sa1, sizeof(sa1) + sizeof("b"));
    for (;;) {
        int c = fgetc(stdin);
        if (c == EOF) {
            break;
        }
        write(s0, &c, sizeof(c));
    }
    close(s0);
    unlink("b");
    return 0;
}
8e2ybdfx

8e2ybdfx1#

只有当你需要接收一个SOCK_STREAM类型的套接字连接时才需要调用bind(),但是bind()的行为取决于SOCKET的域。有一个manual页面专门用于此。
有用信息:
地址格式
UNIX域套接字地址以以下结构表示:


# define UNIX_PATH_MAX    108

struct sockaddr_un {
    sa_family_t sun_family;               /* AF_UNIX */
    char        sun_path[UNIX_PATH_MAX];  /* pathname */
};

在此结构中区分了三种类型的地址:

  • pathname:可以使用bind(2)将UNIX域套接字绑定到以空值结尾的文件系统路径名。当getsockname(2)、getpeername(2)和accept(2)返回套接字的地址时,其长度为offsetof(struct sockaddr_un,sun_path)+ strlen(sun_path)+ 1,并且sun_path包含以空值结尾的路径名。
  • unnamed:未使用bind(2)绑定到路径名的流套接字没有名称。同样,socketpair(2)创建的两个套接字也是未命名的。当getsockname(2)、getpeername(2)和accept(2)返回未命名套接字的地址时,其长度为sizeof(sa_family_t),并且不应检查sun_path。
  • 摘要:抽象套接字地址的区别在于sun_path[0]是空字节('\0')。套接字在此名称空间中的地址由sun_path中的附加字节给定,这些字节由地址结构的指定尺子覆盖。(名称中的空字节没有特殊意义。)该名称与文件系统路径名没有任何关系。当通过getsockname(2)、getpeername(2)和accept(2)返回抽象套接字的地址时,返回的addrlen大于sizeof(sa_family_t)(即,大于2),并且套接字的名称包含在第一个(addrlen
  • sizeof(sa_family_t))个字节。抽象套接字名称空间是不可移植的Linux扩展。

绑定到一个带有文件名的套接字会在文件系统中创建一个套接字,当调用者不再需要这个套接字时,必须将其删除(使用unlink(2))。套接字可以在任何时候被解除链接,并且当对它的最后引用被关闭时,将最终从文件系统中移除。
因此:

  • bind()在客户端中不是必需的。
  • bind()在您的上下文中为您的套接字命名为"a""b"
  • bind(s0, &sa0, sizeof(sa0) + sizeof("b"));和代码中类似行是未定义的行为;它为bind()提供了错误的大小,该大小超出了&sa0的界限。正确的代码为bind(s0, &sa0, sizeof sa0);
  • bind()在此上下文中(Linux、AF_UNIX)确实创建了一个特殊的套接字文件;如果要删除它,则必须调用unlink()remove()
rjee0c15

rjee0c152#

man bind给出了以下答案:

When  a  socket  is  created  with socket(2), it exists in a name space
   (address family) but has no address assigned to it.  bind() assigns the
   address  specified  by  addr  to  the  socket  referred  to by the file
   descriptor sockfd.  addrlen  specifies  the  size,  in  bytes,  of  the
   address structure pointed to by addr.  Traditionally, this operation is
   called “assigning a name to a socket”.

   It is normally necessary to assign a local address using bind()  before
   a SOCK_STREAM socket may receive connections (see accept(2)).
lb3vh1jj

lb3vh1jj3#

在Unix域套接字上调用bind(),而不打算调用accept(),这是一种非常有用的方法,可以确保只有一个进程副本在运行。这比依赖进程名要健壮得多,因为二进制文件可以在另一个名称下复制和运行。
然而,异常终止(SIGSEGV,作为kill -9 ...的目标)的清理是一个问题,因为除非应用程序在信号处理程序中删除套接字,否则套接字不会被删除。

wfveoks0

wfveoks04#

我刚刚在unix数据报套接字上遇到了类似的问题。wpa_supplicant有一个使用unix数据报套接字的控制接口。客户端必须将其套接字的末端绑定到一个路径上,即使它是一个将连接到wpa_supplicant的服务器套接字的客户端套接字。如果不完成这一步,那么服务器将无法向客户端发送回复,尝试失败并返回ENOTCONN错误。
我做C程序员很长时间了,这是我第一次遇到这种行为。看起来wpa_supplicant本质上是试图使用数据报套接字,就好像它们是流套接字一样,我不明白为什么。

相关问题