我正在尝试修改原始 Michael Kerrisk的 AF_UNIX SOCK_DGRAM示例客户端/服务器程序。请点击以下两个链接(client,server)以获取更多信息。示例代码最初发表在他的书“ Linux编程接口,第57章”中。我的目标是基于 Kerrisk的代码示例将'文件套接字'定义替换为'抽象套接字'定义。
不幸的是,我无法在我的客户端版本和服务器之间建立通信。当然, Kerrisk的版本适用于绚丽的色彩。
对我来说很明显,我做的事情根本就是错误的......但......我无法弄清楚它是什么。
=================== SERVER ======================== =======
这是我的'服务器'的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);
}
我如何启动测试:
> ./server &
> ./client aa bb cc
> result: ERROR: sendto
任何帮助|建议|解决方案| RTFM非常受欢迎
伊戈尔
P.S。由于我的声誉小于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);
}
答案 0 :(得分:3)
if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
sizeof(struct sockaddr_un)) != msgLen)
最后一个arg无效,sockaddr_un地址长度应为
sizeof(sa_family_t) + strlen(abstract_client) + 1;
顺便说一句,你应该打印出“errno”值来精确定位从glibc API错误返回时发生的事情。在上面的例子中,“errno”值为“111”,表示无法访问服务器地址。
好的,再次阅读你的帖子之后,我想我明白了你的意思。
当使用no abstract unix套接字(sun_path [0]!= 0)时,服务器名称len通过在内核中使用strlen(sun_path)来计算。所以最后的arg并没有真正使用过。
净/ 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是你的“bind”或“sendto”的最后一个arg。
净/ 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)
内核认为服务器名称是一个具有最后一个arg长度的数组。
因为,
sizeof((struct sockaddr)) != sizeof(sa_family_t) + strlen(abstract_server) + 1
内核不会认为地址是相同的,所以sendto会返回errno 111。
答案 1 :(得分:2)
您的服务器绑定到客户端在sendto()
中使用的不同地址。这是因为您在客户端和服务器中以不同方式计算svaddr的长度。
服务器:
sizeof(sa_family_t) + strlen(abstract_server) + 1)
客户端:
sizeof(struct sockaddr_un)
unix套接字地址是struct sockaddr_un.sun_path
中描述的路径的整个给定长度。更改大小计算以匹配客户端和服务器工作。
编辑 s / abstract_client / abstract_server /似乎我和其他答案有相同的复制和粘贴错误。