如何使用 unix 套接字 (AF_UNIX、SOCK_DGRAM) 模拟 PUB/SUB?
How to emulate PUB/SUB with unix sockets (AF_UNIX, SOCK_DGRAM)?
我正在努力让这种简单的交流发挥作用。
我用 zmq 不到五分钟就搞定了。
用 UNIX 套接字做这件事很痛苦(显然是因为我缺乏信心)。
这是服务器:
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "streamsocket.h"
char *socket_path = "/tmp/stream";
int socket_fd=0;
struct sockaddr_un addr;
int main(){
socket_setup();
while(1){
socket_sendstr("a");
sleep(1);
}
}
void socket_setup(){
int rc;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, socket_path);
if ( (socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
rc=bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr));
if(rc<0){
perror("bind error");
exit(-2);
}
}
int socket_sendstr(char* buffer) {
int len=strlen(buffer);
// corrected after suggestion in answer below (rc->len)
// int rc=write(socket_fd, buffer, rc);
int rc=write(socket_fd, buffer, len);
if (rc != len) {
if (rc > 0) fprintf(stderr,"partial write");
else {
perror("write error");
//exit(-1);
}
}
}
这是客户:
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
char * server_filename = "/tmp/stream";
char * client_filename = "/tmp/stream-client";
struct sockaddr_un server_addr;
struct sockaddr_un client_addr;
int rc;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, server_filename, 104);
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
rc = bind(sockfd, (struct sockaddr *) &client_addr, sizeof(client_addr));
if(rc==-1) perror("bind error");
// connect client to server_filename
rc = connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
if(rc==-1) perror("connect error");
char buf[1024];
int bytes=0;
while(bytes = read(sockfd, buf, sizeof(buf))){
printf("%s\n",buf);
}
close(sockfd);
}
我做错了什么?
目前客户端没有打印任何东西。
EDIT1:更正服务器中的错误"rc" write( , ,rc) to write ( , ,len)
EDIT2:由于客户端也无法正常工作:
socat UNIX 客户端:/tmp/stream -
所以我认为问题可能出在服务器上。
int len=strlen(buffer);
int rc=write(socket_fd, buffer, rc);
不写期望得到长度作为第三个参数;
您误解了数据报套接字的工作方式,这不是 Unix 套接字的本地问题——您在使用 UDP 时也会遇到同样的问题。
数据报套接字是完全无连接的。数据报套接字上的 connect()
实际上并不建立任何连接,它只是为套接字上发送的数据包设置默认目的地。这只是为了方便,以便在您始终发送到同一地址的情况下可以使用 send
而不是 sendto
。
如果客户端首先向服务器发送数据包,您可以让服务器回复特定客户端,在这种情况下,您可以使用 sendto
回复您从 recvfrom
。但是,如果您真的想在客户端和服务器之间建立连接,则需要使用流套接字或 seqpacket 套接字。在这种情况下,您还需要确保在服务器中执行正确的 listen
/accept
序列。
事实上,如果您的服务器在尝试发送时没有打印错误,我会感到惊讶。应该是 write error: Transport endpoint is not connected
.
我正在努力让这种简单的交流发挥作用。
我用 zmq 不到五分钟就搞定了。 用 UNIX 套接字做这件事很痛苦(显然是因为我缺乏信心)。
这是服务器:
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "streamsocket.h"
char *socket_path = "/tmp/stream";
int socket_fd=0;
struct sockaddr_un addr;
int main(){
socket_setup();
while(1){
socket_sendstr("a");
sleep(1);
}
}
void socket_setup(){
int rc;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, socket_path);
if ( (socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
rc=bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr));
if(rc<0){
perror("bind error");
exit(-2);
}
}
int socket_sendstr(char* buffer) {
int len=strlen(buffer);
// corrected after suggestion in answer below (rc->len)
// int rc=write(socket_fd, buffer, rc);
int rc=write(socket_fd, buffer, len);
if (rc != len) {
if (rc > 0) fprintf(stderr,"partial write");
else {
perror("write error");
//exit(-1);
}
}
}
这是客户:
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
char * server_filename = "/tmp/stream";
char * client_filename = "/tmp/stream-client";
struct sockaddr_un server_addr;
struct sockaddr_un client_addr;
int rc;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, server_filename, 104);
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
rc = bind(sockfd, (struct sockaddr *) &client_addr, sizeof(client_addr));
if(rc==-1) perror("bind error");
// connect client to server_filename
rc = connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
if(rc==-1) perror("connect error");
char buf[1024];
int bytes=0;
while(bytes = read(sockfd, buf, sizeof(buf))){
printf("%s\n",buf);
}
close(sockfd);
}
我做错了什么?
目前客户端没有打印任何东西。
EDIT1:更正服务器中的错误"rc" write( , ,rc) to write ( , ,len)
EDIT2:由于客户端也无法正常工作: socat UNIX 客户端:/tmp/stream -
所以我认为问题可能出在服务器上。
int len=strlen(buffer);
int rc=write(socket_fd, buffer, rc);
不写期望得到长度作为第三个参数;
您误解了数据报套接字的工作方式,这不是 Unix 套接字的本地问题——您在使用 UDP 时也会遇到同样的问题。
数据报套接字是完全无连接的。数据报套接字上的 connect()
实际上并不建立任何连接,它只是为套接字上发送的数据包设置默认目的地。这只是为了方便,以便在您始终发送到同一地址的情况下可以使用 send
而不是 sendto
。
如果客户端首先向服务器发送数据包,您可以让服务器回复特定客户端,在这种情况下,您可以使用 sendto
回复您从 recvfrom
。但是,如果您真的想在客户端和服务器之间建立连接,则需要使用流套接字或 seqpacket 套接字。在这种情况下,您还需要确保在服务器中执行正确的 listen
/accept
序列。
事实上,如果您的服务器在尝试发送时没有打印错误,我会感到惊讶。应该是 write error: Transport endpoint is not connected
.