我已经按照this answer中的代码创建了一对程序,它们通过Unix套接字发送和接收数据报。
这很尴尬:在创建第一个套接字的那一侧(即“服务器”),我不能使用对send
,recv
,read
或{ {1}},因为未设置目标(这些呼叫失败,并显示“需要目标地址”错误。
我尝试通过向write
添加初始调用并使用从那里返回的地址来解决此问题,但是它从来没有正确的值(至少在OSX上)。由于我们不知道客户地址,因此使用recvfrom
也不起作用。
我的工作方式大致遵循以下过程:
sendto
和socket
创建服务器套接字。bind
和socket
创建客户端套接字。bind
。connect
并使用它来调用struct sockaddr_un
(如链接的答案中一样)。这很尴尬!如果我使用connect
套接字进行此操作,则可以使用SOCK_STREAM
和listen
;无需服务器知道客户端的套接字路径,流就更直接了。
是否有一种更优雅的方式来连接这些插座?
答案 0 :(得分:0)
SOCK_DGRAM(UDP)套接字是“无连接”的,因此您不能“连接”这两个套接字。它们仅将数据包发送到指定的目标地址,而客户端仅捕获它。因此,您首先要决定要使用SOCK_DGRAM(UDP)还是SOCK_STREAM(TCP)。
如果您使用的是UDP套接字,则客户端套接字不需要connect
,只需在创建和绑定后sendto
即可访问目标地址(在本例中为Server)。
因此,如果需要专用的连接,最好使用TCP套接字。或者,如果您是通过互联网使用的,则可以找到的最接近UDP的是Hole punching。
答案 1 :(得分:0)
解决问题的一种方法:
您的消息可能具有相同的标头。 在标题中添加发件人的地址信息。
然后您的服务器可以使用sendto
来响应正确的客户端。
伪示例:
void handle_my_message(const my_message_t *msg)
{
struct sockaddr_un client_address = msg->header.sender;
my_message_response_t response_msg;
... handle the message and fill the response...
// Send response message
sendto(fd, &response_msg, sizeof(response_msg), 0,
(struct sockaddr*)&client_address, sizeof(client_address));
}
通过这种方式,您的服务器程序无需保留连接记录。
您可能应该使用更小更便携的格式,而不是标题中的struct sockaddr_un
,可以将其转换为struct sockaddr_un
。
答案 2 :(得分:0)
您还应该bind
将客户端套接字插入地址。如果客户端套接字已绑定(即具有其自己的名称),则不需要带外机制将客户端的地址传达给服务器。操作系统将其与每个数据报一起发送。
客户端的示例代码(在python中,因为它快速且易于创建原型-应该易于转换为等效的C):
#!/usr/bin/env python3
import os
import socket
server_addr = "/tmp/ux_server"
client_addr = "/tmp/ux_client"
if os.path.exists(client_addr):
os.remove(client_addr)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sock.bind(client_addr)
for n in range(5):
data = "Hello " + str(n)
data = data.encode()
print("Sent '{}' to {}".format(data, server_addr))
sock.sendto(data, server_addr)
data, addr = sock.recvfrom(16000)
print("Got '{}' back from {}".format(data, addr))
此外,您可以在客户端执行connect
。由于它是一个数据报套接字,因此实际上并没有在两者之间建立连接,但是它确实修复了服务器端点的地址,从而使您无需在每次发送时都提供服务器地址(即您可以使用简单的send
而不是sendto
)。
为完整起见,这是与上述内容相对应的回显服务器:
#!/usr/bin/env python3
import os
import socket
server_addr = "/tmp/ux_server"
if os.path.exists(server_addr):
# Bind will fail if endpoint exists
os.remove(server_addr)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sock.bind(server_addr)
while True:
data, addr = sock.recvfrom(16000)
print("Got '{}' from {}".format(data, addr))
sock.sendto(data, addr)
bind
正在客户端套接字,然后connect
正在服务器端。但这意味着您只需要让服务器最初使用recvfrom
一次即可获得客户的地址。操作系统将随同发送地址,而您无需使用带外机制。
连接套接字的不利之处在于,如果客户端发生故障,除非服务器尝试发送,否则服务器将不会知道,但是客户端将无法重新连接,因为服务器的套接字已连接。这就是为什么数据报服务器通常对所有消息都使用recvfrom
和sendto
。
更新后的服务器,其初始recvfrom
后跟connect
:
#!/usr/bin/env python3
import os
import socket
server_addr = "/tmp/ux_server"
if os.path.exists(server_addr):
# Bind will fail if endpoint exists
os.remove(server_addr)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sock.bind(server_addr)
client_addr = None
while True:
if client_addr:
data = sock.recv(16000)
else:
data, client_addr = sock.recvfrom(16000)
sock.connect(client_addr)
print("Got '{}' from {}".format(data, client_addr))
sock.send(data)
具有连接的套接字的更新的客户端。
#!/usr/bin/env python3
import os
import socket
server_addr = "/tmp/ux_server"
client_addr = "/tmp/ux_client"
if os.path.exists(client_addr):
os.remove(client_addr)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sock.bind(client_addr)
sock.connect(server_addr)
for n in range(5):
data = ("Hello " + str(n)).encode()
print("Sent '{}'".format(data))
sock.send(data)
data = sock.recv(16000)
print("Got '{}' back".format(data))