![](/img/trans.png)
[英]How to emulate PUB/SUB with unix sockets (AF_UNIX, SOCK_DGRAM)?
[英]Using 'abstract sockets' with AF_UNIX SOCK_DGRAM
我正在嘗試修改原始的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
伊戈爾
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);
}
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抽象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是您的“綁定”或“發送到”的最后一個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。
您的服務器綁定到的地址與客戶端在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 /似乎我有與其他答案相同的復制和粘貼錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.