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