连接Unix SOCK_DGRAM套接字

问题描述 投票:0回答:3

我按照this answer中的代码创建了一对通过Unix套接字发送和接收数据报的程序。

这有什么尴尬:在创建第一个套接字的那一边(即“服务器”),我不能使用sendrecvreadwrite的调用,因为没有目标集(这些调用失败,需要“目标地址” “错误。

我已经尝试通过添加对recvfrom的初始调用并使用通过那里返回的地址来解决这个问题,但它从来没有正确的值(至少在OSX上)。由于我们不知道客户端地址,因此使用sendto也无效。

我让它运作的方式大致是遵循这个过程:

  1. 启动服务器程序,其中: 调用socketbind来创建服务器套接字。 它等在这里。
  2. 启动客户端程序,其中: 调用socketbind来创建客户端套接字。 它知道服务器套接字的路径并调用connect。 此方现在已正确设置。
  3. 服务器程序: 通过stdin接受客户端套接字的路径 将路径复制到struct sockaddr_un并使用它来调用connect(如链接的答案中所示)。

这很尴尬!如果我用SOCK_STREAM套接字这样做,我可以使用listenaccept;如果服务器不需要知道客户端的套接字路径,那么流程就会更直接。

是否有更优雅的方式来连接这些插座?

sockets datagram unix-socket
3个回答
0
投票

SOCK_DGRAM(UDP)套接字是“无连接”,因此您无法“连接”两个套接字。它们只将数据包发送到指定的目标地址,客户端只是捕获它。因此,您首先要确定是否要使用SOCK_DGRAM(UDP)或SOCK_STREAM(TCP)。

如果您使用UDP套接字,客户端套接字不需要connect,您只需在创建和绑定后sendto目标地址(在本例中为Server)。

因此,如果您需要专用的连接连接,最好使用TCP套接字。或者,如果您通过互联网使用此功能,您可以找到最接近UDP的是Hole punching


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


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)。

为了完整性,这里是与上述相对应的echo服务器:

#!/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)

EDIT

Hmm... I see now that you say you're already binding the client socket, and then connecting to the server side. But that means you simply need to have the server use recvfrom once initially to obtain the client's address. The OS will send the address along and you don't need to use an out-of-band mechanism.

连接套接字的缺点是,如果客户端发生故障,服务器将不知道除非它尝试发送,否则客户端将无法重新连接,因为服务器的套接字已经连接。这就是数据报服务器通常对所有消息使用recvfromsendto的原因。

使用初始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))
© www.soinside.com 2019 - 2024. All rights reserved.