有一个诀窍使用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系统调用。
最好的参考是Steven的“Unix网络编程”。在第3版中,第15.4节第415-419页显示了一些示例并列出了许多注意事项。
顺便说一句,在参考
我想这是因为没有接收进程正在听这地方插座,是否正确?
我认为你是正确的从服务器中的write() ENOTCONN错误。 UDP套接字通常不会抱怨,因为它无法知道客户端进程是否正在侦听。但是,unix域数据报套接字是不同的。实际上,如果客户端的接收缓冲区已满而不是丢弃数据包,write()实际上会阻塞。这使得unix域数据报套接字远远优于IPC的UDP,因为在加载时UDP甚至会在本地主机上确实会丢弃数据包。另一方面,这意味着你必须小心快速的作家和慢读者。