自动命名 AF_UNIX 本地数据报套接字?

7
我正在使用Unix本地套接字(AF_UNIX地址族,而不是UDP)实现一个简单的服务。服务器绑定到公共地址,可以成功接收请求。但是当要回答时,除非客户端也绑定,否则sendto会失败(常见错误为Transport endpoint is not connected)。绑定到一些随机名称(基于文件系统或抽象)可以解决这个问题。但我想避免这种情况:我怎么能保证我选择的名称不会发生冲突呢?Unix套接字的流模式文档告诉我们,如果它们没有抽象名称,那么在connect时将为它们分配一个抽象名称。对于面向数据报的套接字是否有类似的功能呢?
4个回答

6

我参考的unix(7)手册中提到了关于自动绑定UNIX套接字的信息:

如果bind(2)调用将addrlen指定为sizeof(sa_family_t),或者对于未明确绑定到地址的套接字指定了SO_PASSCRED套接字选项,则套接字将自动绑定到抽象地址。

这就是Linux内核检查地址长度是否等于sizeof(short)的原因,因为sa_family_t是一个short。Rob的出色答案引用的另一个unix(7)手册说,客户端套接字在连接时总是自动绑定,但由于SOCK_DGRAM套接字是无连接的(尽管在其上调用connect),我认为这仅适用于SOCK_STREAM套接字。

还要注意,当提供自己的抽象命名空间套接字名称时,该套接字在此命名空间中的地址由地址结构指定的长度所覆盖的sun_path中的附加字节给出。

struct sockaddr_un me;
const char name[] = "\0myabstractsocket";
me.sun_family = AF_UNIX;
// size-1 because abstract socket names are not null terminated
memcpy(me.sun_path, name, sizeof(name) - 1);
int result = bind(fd, (void*)&me, sizeof(me.sun_family) + sizeof(name) - 1);

sendto()应该同样限制地址的长度,而不是传递sizeof(sockaddr_un)。

4
我假设您正在运行Linux;我不知道这个建议是否适用于SunOS或任何UNIX。
首先,答案是:在socket()之后,在connect()或第一个sendto()之前,尝试添加以下代码:
struct sockaddr_un me;
me.sun_family = AF_UNIX;
int result = bind(fd, (void*)&me, sizeof(short));

现在,解释一下:unix(7)手册中说:

当套接字连接并且它没有本地地址时,将自动生成抽象命名空间中的唯一地址。

可惜,手册是错的。
检查Linux源代码,我们发现只有在套接字标志中设置了SOCK_PASSCRED时,unix_dgram_connect()才会调用unix_autobind()。由于我不知道SOCK_PASSCRED是什么,并且现在是凌晨1点,我需要寻找另一个解决方案。
检查unix_bind,我注意到如果传入的大小等于"sizeof(short)",unix_bind就会调用unix_autobind。因此,上面的解决方案。

祝你好运,早上好。

罗布


非常好的分析,Rob!非常感谢!看起来手册有不同的来源:在工作中(一些旧的SuSE系统)上,它们明确将自动绑定到连接效果限制在流套接字上。在家里,我得到了和你一样的句子。从长远来看,我会记住这是绑定到空路径。 - JB.

1
有点晚的回复,但对于像我一样使用谷歌找到这篇文章的人,Rob Adam的答案帮了我大忙:只需使用set(level SO_SOCKET,参见man 7 unix)将SO_PASSCRED设置为1即可获得“真正”的答案。不需要愚蠢的绑定。
我在PHP中使用了这个方法,但它没有定义SO_PASSCRED(愚蠢的PHP)。但是,如果您自己定义它,它仍然有效。在我的电脑上,它的值为16,我认为它会相当可移植。

绑定有什么好笑的? 命名套接字就是用它完成的,强制自动命名也不是什么大问题。从文档(暗示维护)的角度来看,SO_PASSCRED可能更好。但从直观性来看,SO_PASSCRED才是可笑的。 - JB.

-1

我不确定我完全理解你的问题,但这是我刚写的一个数据报实现回显服务器。您可以看到服务器正在使用发送方的相同IP/端口响应客户端。

这是代码

首先是服务器(监听器)

from socket import *
import time
class Listener:
    def __init__(self, port):
        self.port = port
        self.buffer = 102400

    def listen(self):

        sock = socket(AF_INET, SOCK_DGRAM)
        sock.bind(('', self.port))

        while 1:
            data, addr = sock.recvfrom(self.buffer)
            print "Received: " + data
            print "sending to %s" % addr[0]
            print "sending data %s" % data
            time.sleep(0.25)
            #print addr # will tell you what IP address the request came from and port
            sock.sendto(data, (addr[0], addr[1]))
            print "sent"
        sock.close()

if __name__ == "__main__":
    l = Listener(1975)
    l.listen()

现在,客户端(发送方)从监听器接收响应

from socket import *
from time import sleep
class Sender:
    def __init__(self, server):
       self.port = 1975
       self.server = server
       self.buffer = 102400

    def sendPacket(self, packet):
        sock = socket(AF_INET, SOCK_DGRAM)
        sock.settimeout(10.75)


        sock.sendto(packet, (self.server, int(self.port)))

        while 1:
            print "waiting for response"
            data, addr = sock.recvfrom(self.buffer)
            sock.close()
            return data



if __name__ == "__main__":
        s = Sender("127.0.0.1")
        response = s.sendPacket("Hello, world!")
        print response

问题是关于Unix套接字,而不是Internet套接字(即AF_UNIX地址族,而不是AF_INET)。感谢您的回答,我会编辑问题并明确说明。 - JB.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接