Sockets 已连接的Unix套接字

Sockets 已连接的Unix套接字,sockets,datagram,unix-socket,Sockets,Datagram,Unix Socket,我按照中的代码创建了一对通过Unix套接字发送和接收数据报的程序 这有什么尴尬之处:在创建第一个套接字(即“服务器”)的那一边,我不能使用调用send、recv、read或write,因为没有设置目的地(这些调用失败时出现“需要目的地地址”错误) 我尝试通过添加一个对recvfrom的初始调用并使用从那里返回的地址来解决这个问题,但它从来没有正确的值(至少在OSX上)。使用sendto也不起作用,因为我们不知道客户端地址 我让它工作的方式大致如下: 启动服务器程序,该程序: 调用socket和b

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

这有什么尴尬之处:在创建第一个套接字(即“服务器”)的那一边,我不能使用调用
send
recv
read
write
,因为没有设置目的地(这些调用失败时出现“需要目的地地址”错误)

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

我让它工作的方式大致如下:

  • 启动服务器程序,该程序:
  • 调用
    socket
    bind
    来创建服务器套接字
  • 它在这里等着
  • 启动客户端程序,该程序:
  • 调用
    socket
    bind
    来创建客户端套接字
  • 它知道服务器套接字的路径并调用
    connect
  • 这一侧现在已正确设置
  • 服务器程序:
  • 通过stdin接受客户端套接字的路径
  • 将路径复制到
    结构sockaddr\u un
    ,并使用该路径调用
    连接
    (如链接答案中所示)
  • 这真是太尴尬了!如果我用
    SOCK\u STREAM
    sockets做这件事,我可以使用
    listen
    accept
    ;流程更直接,服务器不需要知道客户端的套接字路径

    有没有更优雅的方法连接这些套接字?

    SOCK_DGRAM(UDP)套接字是“无连接”的,因此您无法“连接”这两个套接字。它们只向指定的目标地址发送数据包,客户端只需捕获数据包。因此,您首先要决定是使用SOCK_DGRAM(UDP)还是SOCK_流(TCP)

    如果您使用的是UDP套接字,则客户端套接字不需要
    连接
    ,您只需在创建和绑定后
    发送到
    目标地址(本例中为服务器)


    因此,如果您需要专用连接,最好使用TCP套接字。或者如果您在internet上使用此连接,则UDP最接近的功能是。

    解决此问题的一种方法:

    您的邮件可能有共同的标题。 将发件人的地址信息添加到标头

    然后,您的服务器可以使用
    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\u un
    不同,您可能应该使用更小、更便携的格式,可以转换为
    struct sockaddr\u un
    ,您还应该
    将客户端套接字绑定到一个地址。如果客户端套接字已绑定(即有自己的名称),则不需要带外机制将客户端地址与服务器进行通信。操作系统会将其与每个数据报一起发送

    客户机的示例代码(用python编写,因为它快速且易于原型化——应该易于转换为等效的C):

    此外,您可以在客户端执行
    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)
    
    编辑 嗯……我现在明白了,你说你已经
    绑定了客户端套接字,然后
    连接到服务器端。但这意味着你只需要让服务器最初使用
    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))
    

    听起来你想要流套接字。为什么要使用数据报套接字?如果你使用流套接字,它将非常类似于TCP(
    AF\u INET
    )除了绑定地址。同意-尽管由于推送的数据不是面向流的,我需要通过添加长度前缀进行封装。我不希望这样做,因为它需要修改两个程序上的所有读/写调用。当前程序假定1 write=1 read。//“您无法”连接两个套接字”->这是不正确的-您可以这样做,一旦它们连接起来,就有几个优点,例如能够使用
    读取
    写入
    发送
    接收
    。此外,在UDP的情况下,它使一些实现知道相关的ICMP消息/“您只需将
    sendto
    发送到目标地址”->这是正确的,但无需首先从“服务器”调用
    connect
    "另一方面,它不知道客户端的地址。服务器需要以某种方式发现此信息。@arrtchiu在无连接类型协议(如UDP)上调用connect只会将其默认的
    sendto
    IP值设置为指定的值,因此您不必每次使用目标地址调用
    sendto
    。这并不意味着n他们像TCP一样在两个套接字之间建立安全连接。
    connect
    需要目标IP。如果服务器不知道
    #!/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))