我正在使用unix本地套接字上的数据报实现一个简单的服务(AF_UNIX地址系列,即不是UDP )。服务器绑定到公共地址,它接收请求就好了。不幸的是,当回答时,sendto
会失败,除非客户端也受到约束。 (常见错误为Transport endpoint is not connected
)。
绑定到某个随机名称(基于文件系统或抽象)有效。但我想避免这种情况:我是谁保证我选择的名字不会发生碰撞?
unix套接字的流模式文档告诉我们,如果它们还没有,那么将在connect
时为它们分配一个抽象名称。这种功能是否适用于面向数据报的套接字?
答案 0 :(得分:5)
我引用的unix(7)手册页有关于autobind UNIX套接字的信息:
如果bind(2)调用将addrlen指定为sizeof(sa_family_t),或者为未明确绑定到地址的套接字指定了SO_PASSCRED套接字选项,则套接字将自动响应为抽象地址。
这就是Linux内核检查地址长度等于sizeof(short)的原因,因为sa_family_t是一个简短的。 Rob的精彩回答引用的另一个unix(7)手册页说客户端套接字在连接时总是自动响应,但由于SOCK_DGRAM套接字是无连接的(尽管在它们上调用了连接),我相信这只适用于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)。
答案 1 :(得分:4)
我假设您正在运行Linux;我不知道这个建议是否适用于SunOS或任何UNIX。
首先,答案是:在socket()之后和connect()或first sendto()之前,尝试添加以下代码:
struct sockaddr_un me;
me.sun_family = AF_UNIX;
int result = bind(fd, (void*)&me, sizeof(short));
现在,解释:unix(7)手册页说明了这一点:
连接插座时 还没有本地地址a 摘要中的唯一地址 将生成名称空间 自动。
可悲的是,手册页在说谎。
检查Linux source code,我们看到如果在套接字标志中设置了SOCK_PASSCRED,unix_dgram_connect()只调用unix_autobind()。由于我不知道SOCK_PASSCRED是什么,现在凌晨1点,我需要寻找另一个解决方案。
检查unix_bind,我注意到如果传入的大小等于“sizeof(short)”,unix_bind会调用unix_autobind。因此,上面的解决方案。
祝你好运,早上好。罗布
答案 2 :(得分:1)
有点迟到的反应,但对于任何人发现这使用谷歌,因为我做了。 Rob Adam的回答帮助我得到了“真正的”答案:只需使用set(级别SO_SOCKET
,参见man 7 unix
)将SO_PASSCRED
设置为1.无需愚蠢的绑定。< / p>
我在PHP中使用过它,但它没有定义SO_PASSCRED
(愚蠢的PHP)。但是,如果您自己定义它,它仍然可以工作。在我的电脑上,它的值为16,我认为它可以很方便地工作。
答案 3 :(得分:-1)
我不太确定我完全理解你的问题,但这是我刚写的一个echo服务器的数据报实现。您可以看到服务器正在响应客户端发送的相同IP / PORT。
这是代码
首先,服务器(监听器)
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