时间:2010-07-24 09:55:10

标签: c sockets datagram

7 个答案:

答案 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

另见:Introducing multicast Unix sockets

答案 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()
}