在AF_UNIX SOCK_DGRAM中使用'抽象套接字'

我正在尝试修改原始的Michael Kerrisk的 AF_UNIX SOCK_DGRAM示例客户机/服务器程序。 请点击以下两个链接( 客户端 , 服务器 )获取更多信息。 示例代码最初发表在他的书“Linux编程接口”(第57章)中 。 我的目标是根据Kerrisk的代码示例,用“抽象套接字”定义replace“文件套接字”定义。

不幸的是,我无法build立我的版本的客户端和服务器之间的通信。 当然, Kerrisk的版本可以搭配出色的颜色

对我来说显而易见的是,我正在做一些根本性的错误……但是……我无法弄清楚它是什么。

===================服务器============================== =

这里是我的版本Kerrisk的'服务器'的代码:

int main(int argc, char *argv[]) { struct sockaddr_un svaddr, claddr; int sfd, j; ssize_t numBytes; socklen_t len; char buf[BUF_SIZE]; char *abstract_server; sfd = socket(AF_UNIX, SOCK_DGRAM, 0); /* Create server socket */ if (sfd == -1) errExit("socket"); abstract_server = "viper_server"; /* Construct well-known address and bind server socket to it */ if (remove(abstract_server) == -1 && errno != ENOENT) errExit("remove-%s", abstract_server); memset(&svaddr, 0, sizeof(struct sockaddr_un)); svaddr.sun_family = AF_UNIX; strncpy(&svaddr.sun_path[1], abstract_server, strlen(abstract_server)); if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(sa_family_t) + strlen(abstract_server) + 1) == -1) errExit("bind"); /* Receive messages, convert to uppercase, and return to client */ for (;;) { len = sizeof(struct sockaddr_un); numBytes = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len); if (numBytes == -1) errExit("recvfrom"); printf("Server received %ld bytes from %s\n", (long) numBytes, claddr.sun_path); for (j = 0; j < numBytes; j++) buf[j] = toupper((unsigned char) buf[j]); if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len) != numBytes) fatal("sendto"); } } 

=========================客户======================== ====

这是我的版本Kerrisk的'客户'的代码:

 int main(int argc, char *argv[]) { struct sockaddr_un svaddr, claddr; int sfd, j; size_t msgLen; ssize_t numBytes; char resp[BUF_SIZE]; char *abstract_client; char *abstract_server; if (argc < 2 || strcmp(argv[1], "--help") == 0) usageErr("%s msg...\n", argv[0]); /* Create client socket; bind to unique pathname (based on PID) */ sfd = socket(AF_UNIX, SOCK_DGRAM, 0); if (sfd == -1) errExit("socket"); abstract_client = "viper_client"; abstract_server = "viper_server"; memset(&claddr, 0, sizeof(struct sockaddr_un)); claddr.sun_family = AF_UNIX; strncpy(&claddr.sun_path[1], abstract_client, strlen(abstract_client)); if (bind(sfd, (struct sockaddr *) &claddr, sizeof(sa_family_t) + strlen(abstract_client) + 1) == -1) errExit("bind"); /* Construct address of server */ memset(&svaddr, 0, sizeof(struct sockaddr_un)); svaddr.sun_family = AF_UNIX; strncpy(&svaddr.sun_path[1], abstract_server, strlen(abstract_server)); /* Send messages to server; echo responses on stdout */ for (j = 1; j < argc; j++) { msgLen = strlen(argv[j]); /* May be longer than BUF_SIZE */ /* code FIX */ if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr, (sizeof(sa_family_t) + strlen(abstract_server) + 1) ) != msgLen) fatal("sendto"); /* original - non working code - replaced with the code FIX above if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) != msgLen) { fatal("sendto"); } */ numBytes = recvfrom(sfd, resp, BUF_SIZE, 0, NULL, NULL); /* Or equivalently: numBytes = recv(sfd, resp, BUF_SIZE, 0); or: numBytes = read(sfd, resp, BUF_SIZE); */ if (numBytes == -1) errExit("recvfrom"); printf("Response %d: %.*s\n", j, (int) numBytes, resp); } remove(claddr.sun_path); /* Remove client socket pathname */ exit(EXIT_SUCCESS); } 

我如何启动testing:

 > ./server & > ./client aa bb cc > result: ERROR: sendto 

任何帮助| build议| 解决scheme| RTFM非常受欢迎

伊戈尔

PS由于我的声望不到10,我只能发布2个链接(请参阅Kerrisk的客户端URL,Kerrrisk的服务器URL在这篇文章的顶部)。 所以,我做了一个“剪切粘贴” Kerrisk的 “如何使用抽象套接字” C代码片段。 这本书发表在他的书第1176页,清单57-8。

 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_un addr; char *str; memset(&addr, 0, sizeof(struct sockaddr_un)); /* Clear address structure */ addr.sun_family = AF_UNIX; /* UNIX domain address */ /* addr.sun_path[0] has already been set to 0 by memset() */ str = "xyz"; /* Abstract name is "\0abc" */ strncpy(&addr.sun_path[1], str, strlen(str)); // In early printings of the book, the above two lines were instead: // // strncpy(&addr.sun_path[1], "xyz", sizeof(addr.sun_path) - 2); // /* Abstract name is "xyz" followed by null bytes */ sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd == -1) errExit("socket"); if (bind(sockfd, (struct sockaddr *) &addr, sizeof(sa_family_t) + strlen(str) + 1) == -1) errExit("bind"); // In early printings of the book, the final part of the bind() call // above was instead: // sizeof(struct sockaddr_un)) == -1) sleep(60); exit(EXIT_SUCCESS); } 

您的服务器绑定到不同于客户端在sendto()使用的地址。 这是因为您在客户机和服务器中以不同的方式计算svaddr的长度。

server: sizeof(sa_family_t) + strlen(abstract_server) + 1)

client: sizeof(struct sockaddr_un)

unix套接字地址是struct sockaddr_un.sun_path描述的路径的整个给定长度。 更改大小计算后,以匹配客户端和服务器的工作。

编辑 s / abstract_client / abstract_server /似乎我有相同的复制和粘贴错误作为其他答案。

 if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) != msgLen) 

最后一个参数是无效的,sockaddr_un地址长度应该是

 sizeof(sa_family_t) + strlen(abstract_client) + 1; 

顺便说一句,你应该打印出“errno”值来精确定位从glibc API错误返回时发生的事情。 在上述情况下,“errno”值为“111”,表示无法到达服务器地址。


好,再次阅读你的文章后,我想我明白你的意思了。

当使用no抽象unix套接字(sun_path [0]!= 0)时,服务器名称len是通过在内核中使用strlen(sun_path)来计算的。 所以最后一个参数并没有真正使用。

净/ UNIX / af_unix.c:unix_mkname

 if (sunaddr->sun_path[0]) { /* ... */ ((char *)sunaddr)[len] = 0; len = strlen(sunaddr->sun_path)+1+sizeof(short); return len; } 

但是,当您使用抽象的unix套接字(sun_path [0] == 0)时,服务器名称len是您的“绑定”或“sendto”的最后一个参数。

净/ UNIX / af_unix.c:unix_mkname:

 *hashp = unix_hash_fold(csum_partial(sunaddr, len, 0)); 

所以当你把套接字绑定到地址上的时候,

 if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(sa_family_t) + strlen(abstract_server) + 1) == -1) 

内核认为服务器名称是一个具有这个长度的数组。

当你尝试发送到一个地址

 if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr, sizeof((struct sockaddr)) != msgLen) 

内核认为服务器名称是最后一个参数的长度的数组。

因为,

 sizeof((struct sockaddr)) != sizeof(sa_family_t) + strlen(abstract_server) + 1 

内核不会认为地址是一样的,所以sendto返回errno 111。