答案 0 :(得分:37)
使用unix数据报套接字是一个技巧。与流套接字(tcp或unix域)不同,数据报套接字需要为服务器和客户端定义端点。当在流套接字中建立连接时,操作系统将隐式创建客户端的端点。无论这是对应于短暂的TCP / UDP端口,还是对应于unix域的临时inode,都会为您创建客户端的端点。这就是为什么你通常不需要为客户端的流套接字发出bind()调用。
您正在查看的原因"地址已在使用中"是因为您告诉客户端绑定到与服务器相同的地址。 bind()
是关于断言外部身份。两个插座通常不能有相同的名称。
使用数据报套接字,特别是unix域数据报套接字,客户端必须bind()
到自己的端点,然后connect()
到服务器端口端点。这是您的客户端代码,略有修改,还有其他一些好东西:
char * server_filename = "/tmp/socket-server";
char * client_filename = "/tmp/socket-client";
struct sockaddr_un server_addr;
struct sockaddr_un client_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, server_filename, 104); // XXX: should be limited to about 104 characters, system dependent
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sun_family = AF_UNIX;
strncpy(client_addr.sun_path, client_filename, 104);
// get socket
int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
// bind client to client_filename
bind(sockfd, (struct sockaddr *) &client_addr, sizeof(client_addr));
// connect client to server_filename
connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
...
char buf[1024];
int bytes = read(sockfd, buf, sizeof(buf));
...
close(sockfd);
此时应完全设置套接字。我认为理论上你可以使用read()
/ write()
,但通常我会将send()
/ recv()
用于数据报套接字。
通常,您希望在每次调用后检查错误,然后发出perror()
。当出现问题时,它会极大地帮助你。通常,使用这样的模式:
if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
perror("socket failed");
}
这适用于任何C系统调用。
最好的参考是史蒂文&#34; Unix网络编程&#34;。在第3版,第15.4节,第415-419页显示了一些例子并列出了许多警告。
顺便提一下,参考
我想这是因为没有接收进程正在侦听这个本地套接字,对吗?
我认为您对服务器中write()
的ENOTCONN错误表示反对。 UDP套接字通常不会抱怨,因为它无法知道客户端进程是否正在侦听。但是,unix域数据报套接字是不同的。事实上,如果客户端的接收缓冲区已满,write()
将实际阻塞,而不是丢弃数据包。这使得unix域数据报套接字远远优于用于IPC的UDP,因为UDP在加载时肯定会丢弃数据包,即使在localhost上也是如此。另一方面,这意味着你必须小心快速写作和慢读者。
答案 1 :(得分:7)
答案 2 :(得分:2)
如果问题是关于广播(据我所知),那么根据unix(4) - UNIX-domain protocol family,广播它不适用于UNIX域套接字:
Unix Ns -domain协议系列不支持 广播寻址或任何形式的“通配符”匹配 收到的消息。所有地址都是绝对的或 其他Unix Ns -domain套接字的相对路径名。
可能是多播可能是一个选项,但我觉得它不适用于POSIX,尽管Linux supports UNIX Domain Socket multicast。
答案 3 :(得分:0)
它会因为而发生 服务器或客户端在取消链接/删除bind()文件关联之前死亡。 使用此绑定路径的任何客户端/服务器,尝试再次运行服务器。
解决方案: 如果要再次绑定,只需检查该文件是否已关联,然后取消该文件的链接。 如何步骤: 首先通过访问检查该文件的访问权限(2); 如果是,则取消链接(2)。 在bind()调用之前放置这段代码,位置是独立的。
if(!access(filename.c_str()))
unlink(filename.c_str());
更多参考读取unix(7)
答案 4 :(得分:-1)
答案 5 :(得分:-2)
答案 6 :(得分:-6)
您可以使用以下代码解决绑定错误:
int use = yesno;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&use, sizeof(int));
使用UDP协议时,如果要使用connect()
或write()
,则必须调用send()
,否则应使用sendto()
。
为了达到您的要求,以下伪代码可能会有所帮助:
sockfd = socket(AF_INET, SOCK_DGRAM, 0)
set RESUSEADDR with setsockopt
bind()
while (1) {
recvfrom()
sendto()
}