AF_UNIX 本地数据报套接字的自动命名?
题
我正在使用 unix 本地套接字上的数据报(AF_UNIX 地址族,即 不是UDP)。服务器绑定到公共地址,并且它可以正常接收请求。不幸的是,当要回复时, sendto
除非客户端也被绑定,否则失败。(常见的错误是 Transport endpoint is not connected
).
绑定到一些随机名称(基于文件系统的或抽象的)是有效的。但我想避免这种情况:我有什么资格保证我选择的名字不会冲突?
unix 套接字的流模式文档告诉我们,将在以下位置为它们分配一个抽象名称: connect
如果他们还没有的话,是时候了。这样的功能可用于面向数据报的套接字吗?
解决方案
我假设你运行的是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:00,所以我需要寻找另一个解决方案。
正在检查 unix_bind, ,我注意到如果传入的大小等于“sizeof(short)”,则 unix_bind 会调用 unix_autobind。于是就有了上面的解决方案。
祝你好运,早上好。
抢
其他提示
这 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)。
回复有点晚了,但对于像我一样使用谷歌发现这一点的人来说。罗布·亚当的回答帮助我得到了“真正”的答案:只需使用 set (级别 SO_SOCKET
, , 看 man 7 unix
) 设置 SO_PASSCRED
至 1。不需要愚蠢的绑定。
我在PHP中使用了这个,但它没有 SO_PASSCRED
定义(愚蠢的PHP)。不过,如果您自己定义它,它仍然有效。在我的计算机上它的值为 16,我认为它可以很好地移植。
我不太确定我完全理解你的问题,但这是我刚刚编写的回显服务器的数据报实现。您可以看到服务器正在响应发送的同一 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