UNIX Domain Socket编程3套接字
UNIX Domain Socket programming 3 sockets
我正在尝试制作一个支持 3 个套接字的 server.c 文件,分别由 3 个客户端 类 表示:client1、client2、client3。
在我的 server.c 文件中,我目前有这段代码,是我在互联网上找到的。
如果我想让它有 3 个插座。我想使用 select() 命令查看 3 个客户端的写入活动。我的问题是如何使用它来支持 3 个套接字。
我可以将 3 个客户端绑定到服务器可以侦听的 3 个套接字吗?如果是这样,服务器如何分别监听这3个套接字?可能用数组?
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#define socket1 "sock1"
#define socket2 "sock2"
#define socket3 "sock3"
int main(int argc, char *argv[]) {
//struct sockaddr_un addr;
struct sockaddr_un addr1;
struct sockaddr_un addr2;
struct sockaddr_un addr3;
char buf[100];
int socket1;
int socket2;
int socket3;
//int fd;
int cl,rc;
if (argc > 1) socket_path=argv[1];
if ( (socket1 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr1, 0, sizeof(addr1));
addr1.sun_family = AF_UNIX;
strncpy(addr1.sun_path, socket_path, sizeof(addr1.sun_path)-1);
unlink(socket_path1);
if ( (socket2 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr2, 0, sizeof(addr2));
addr1.sun_family = AF_UNIX;
strncpy(addr2.sun_path, socket_path, sizeof(addr2.sun_path)-1);
unlink(socket_path2);
if ( (socket3 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr3, 0, sizeof(addr3));
addr3.sun_family = AF_UNIX;
strncpy(addr3.sun_path, socket_path, sizeof(addr3.sun_path)-1);
unlink(socket_path3);
if (bind(socket1, (struct sockaddr*)&addr1, sizeof(addr1)) == -1) {
perror("bind error");
exit(-1);
}
if (bind(socket2, (struct sockaddr*)&addr2, sizeof(addr2)) == -1) {
perror("bind error");
exit(-1);
}
if (bind(socket3, (struct sockaddr*)&addr3, sizeof(addr3)) == -1) {
perror("bind error");
exit(-1);
}
if (listen(socket1, 5) == -1) {
perror("listen error");
exit(-1);
}
if (listen(socket2, 5) == -1) {
perror("listen error");
exit(-1);
}
if (listen(socket3, 5) == -1) {
perror("listen error");
exit(-1);
}
while (1) {
if ( (cl = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
while ( (rc=read(cl,buf,sizeof(buf))) > 0) {
printf("read %u bytes: %.*s\n", rc, rc, buf);
}
if (rc == -1) {
perror("read");
exit(-1);
}
else if (rc == 0) {
printf("EOF\n");
close(cl);
}
}
return 0;
}
如果你想在同一个进程中使用三个监听套接字,你必须让它们唯一。在 AF_INET
family you do that by bind(2)
-ing different ports, in the AF_UNIX
系列中,您可以使用不同的路径。
还有你的台词:
char *socket_path = "[=10=]hidden";
至少有两个问题:
- 赋值右侧的字符串字面量类型为
const char[8]
,衰减为const char*
指针类型,而非char*
类型。使左侧 const char*
。另外,使用更高的警告级别编译,例如-Wall -pedantic
以获得编译器的帮助。
- 字符串开头的零字节使
strncpy(3)
不复制任何内容,因为它 copies at most n characters from the string pointed to by src, including the terminating null byte ('[=20=]')
.
创建一个将 UNIX 路径作为参数并创建、绑定和标记套接字为侦听的函数,returns 创建套接字描述符。调用它三次 - 您有三个侦听 UNIX 套接字。在活动套接字上设置 select(2)
on them for reading - that'll tell you when client connections arrive. At that point call accept(2)
以获得 连接的客户端套接字 ,它与侦听套接字本身是分开的。
好吧,因为 select 一直是我的 最爱 Unix 系统调用,我决定做点小事,依我的拙见,你在找什么。
我无耻地从这里拿走了服务器和客户端的代码:
https://troydhanson.github.io/misc/Unix_domain_sockets.html
当然我做了一些小修改,以使其符合您的需要,让我们看看:
server.c:
#include <stdio.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
char *socket_path = "/tmp/socket";
int main() {
int fd, i;
int clients[10], num_clients;
fd_set read_set;
char buf[100];
struct sockaddr_un addr;
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
unlink(socket_path);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("bind error");
exit(-1);
}
if (listen(fd, 5) == -1) {
perror("listen error");
exit(-1);
}
num_clients = 0;
while (1) {
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
for (i = 0; i < num_clients; i++) {
FD_SET(clients[i], &read_set);
}
select(fd + num_clients + 1, &read_set, NULL, NULL, NULL);
if (FD_ISSET(fd, &read_set)) {
if ( (clients[num_clients++] = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
printf("we got a connection!\n");
}
for (i = 0; i < num_clients; i++) {
if (FD_ISSET(clients[i], &read_set)) {
read(clients[i], buf, sizeof(buf));
printf("client %d says: %s\n", i, buf);
}
}
}
}
client.c:
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char *socket_path = "/tmp/socket";
int main(int argc, char *argv[]) {
struct sockaddr_un addr;
char buf[100];
int fd,rc;
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("connect error");
exit(-1);
}
while( (rc=read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
printf("writing\n");
*index(buf, '\n') = 0;
if (write(fd, buf, rc) != rc) {
if (rc > 0) fprintf(stderr,"partial write");
else {
perror("write error");
exit(-1);
}
}
}
return 0;
}
好的,这很容易实现,您只需在一个终端中启动服务器,然后打开几个其他终端并启动几个客户端。
运行 它在我的电脑上得到:
exe@atreides:~/tmp$ ./server
we got a connection!
client 0 says: Hello!
we got a connection!
client 1 says: Hey man!
同时另一个终端:
exe@atreides:~/tmp$ ./client
Hey man!
writing
另一个:
exe@atreides:~/tmp$ ./client
Hello!
writing
这一切背后的魔力在于正确使用套接字和select。
首先你需要一个服务器套接字,一个接受连接的套接字。
一旦绑定到服务器套接字,让它成为 Unix 套接字或网络套接字,您就可以通过接受该套接字上的连接来为您的客户端获取套接字。每个客户端都获得一个新的套接字号。
然后,将这些套接字、服务器套接字和客户端套接字添加到 fd_set 并将其传递给 select。 Select 将同时监听所有套接字,并将接收到数据的套接字留在集合中。
现在您迭代集合以查看哪些套接字是热的,您就在那里!
还有一件事,我猜你很困惑,所有客户端都连接到 相同的 服务器套接字地址(文件)。是的,就像许多进程同时打开同一个文件一样......但这不是一个普通文件,它是一个 Unix 套接字。 :)
玩得开心,祝你好运!!!
我正在尝试制作一个支持 3 个套接字的 server.c 文件,分别由 3 个客户端 类 表示:client1、client2、client3。
在我的 server.c 文件中,我目前有这段代码,是我在互联网上找到的。
如果我想让它有 3 个插座。我想使用 select() 命令查看 3 个客户端的写入活动。我的问题是如何使用它来支持 3 个套接字。
我可以将 3 个客户端绑定到服务器可以侦听的 3 个套接字吗?如果是这样,服务器如何分别监听这3个套接字?可能用数组?
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#define socket1 "sock1"
#define socket2 "sock2"
#define socket3 "sock3"
int main(int argc, char *argv[]) {
//struct sockaddr_un addr;
struct sockaddr_un addr1;
struct sockaddr_un addr2;
struct sockaddr_un addr3;
char buf[100];
int socket1;
int socket2;
int socket3;
//int fd;
int cl,rc;
if (argc > 1) socket_path=argv[1];
if ( (socket1 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr1, 0, sizeof(addr1));
addr1.sun_family = AF_UNIX;
strncpy(addr1.sun_path, socket_path, sizeof(addr1.sun_path)-1);
unlink(socket_path1);
if ( (socket2 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr2, 0, sizeof(addr2));
addr1.sun_family = AF_UNIX;
strncpy(addr2.sun_path, socket_path, sizeof(addr2.sun_path)-1);
unlink(socket_path2);
if ( (socket3 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr3, 0, sizeof(addr3));
addr3.sun_family = AF_UNIX;
strncpy(addr3.sun_path, socket_path, sizeof(addr3.sun_path)-1);
unlink(socket_path3);
if (bind(socket1, (struct sockaddr*)&addr1, sizeof(addr1)) == -1) {
perror("bind error");
exit(-1);
}
if (bind(socket2, (struct sockaddr*)&addr2, sizeof(addr2)) == -1) {
perror("bind error");
exit(-1);
}
if (bind(socket3, (struct sockaddr*)&addr3, sizeof(addr3)) == -1) {
perror("bind error");
exit(-1);
}
if (listen(socket1, 5) == -1) {
perror("listen error");
exit(-1);
}
if (listen(socket2, 5) == -1) {
perror("listen error");
exit(-1);
}
if (listen(socket3, 5) == -1) {
perror("listen error");
exit(-1);
}
while (1) {
if ( (cl = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
while ( (rc=read(cl,buf,sizeof(buf))) > 0) {
printf("read %u bytes: %.*s\n", rc, rc, buf);
}
if (rc == -1) {
perror("read");
exit(-1);
}
else if (rc == 0) {
printf("EOF\n");
close(cl);
}
}
return 0;
}
如果你想在同一个进程中使用三个监听套接字,你必须让它们唯一。在 AF_INET
family you do that by bind(2)
-ing different ports, in the AF_UNIX
系列中,您可以使用不同的路径。
还有你的台词:
char *socket_path = "[=10=]hidden";
至少有两个问题:
- 赋值右侧的字符串字面量类型为
const char[8]
,衰减为const char*
指针类型,而非char*
类型。使左侧const char*
。另外,使用更高的警告级别编译,例如-Wall -pedantic
以获得编译器的帮助。 - 字符串开头的零字节使
strncpy(3)
不复制任何内容,因为它copies at most n characters from the string pointed to by src, including the terminating null byte ('[=20=]')
.
创建一个将 UNIX 路径作为参数并创建、绑定和标记套接字为侦听的函数,returns 创建套接字描述符。调用它三次 - 您有三个侦听 UNIX 套接字。在活动套接字上设置 select(2)
on them for reading - that'll tell you when client connections arrive. At that point call accept(2)
以获得 连接的客户端套接字 ,它与侦听套接字本身是分开的。
好吧,因为 select 一直是我的 最爱 Unix 系统调用,我决定做点小事,依我的拙见,你在找什么。
我无耻地从这里拿走了服务器和客户端的代码: https://troydhanson.github.io/misc/Unix_domain_sockets.html
当然我做了一些小修改,以使其符合您的需要,让我们看看:
server.c:
#include <stdio.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
char *socket_path = "/tmp/socket";
int main() {
int fd, i;
int clients[10], num_clients;
fd_set read_set;
char buf[100];
struct sockaddr_un addr;
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
unlink(socket_path);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("bind error");
exit(-1);
}
if (listen(fd, 5) == -1) {
perror("listen error");
exit(-1);
}
num_clients = 0;
while (1) {
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
for (i = 0; i < num_clients; i++) {
FD_SET(clients[i], &read_set);
}
select(fd + num_clients + 1, &read_set, NULL, NULL, NULL);
if (FD_ISSET(fd, &read_set)) {
if ( (clients[num_clients++] = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
printf("we got a connection!\n");
}
for (i = 0; i < num_clients; i++) {
if (FD_ISSET(clients[i], &read_set)) {
read(clients[i], buf, sizeof(buf));
printf("client %d says: %s\n", i, buf);
}
}
}
}
client.c:
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char *socket_path = "/tmp/socket";
int main(int argc, char *argv[]) {
struct sockaddr_un addr;
char buf[100];
int fd,rc;
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("connect error");
exit(-1);
}
while( (rc=read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
printf("writing\n");
*index(buf, '\n') = 0;
if (write(fd, buf, rc) != rc) {
if (rc > 0) fprintf(stderr,"partial write");
else {
perror("write error");
exit(-1);
}
}
}
return 0;
}
好的,这很容易实现,您只需在一个终端中启动服务器,然后打开几个其他终端并启动几个客户端。
运行 它在我的电脑上得到:
exe@atreides:~/tmp$ ./server
we got a connection!
client 0 says: Hello!
we got a connection!
client 1 says: Hey man!
同时另一个终端:
exe@atreides:~/tmp$ ./client
Hey man!
writing
另一个:
exe@atreides:~/tmp$ ./client
Hello!
writing
这一切背后的魔力在于正确使用套接字和select。
首先你需要一个服务器套接字,一个接受连接的套接字。
一旦绑定到服务器套接字,让它成为 Unix 套接字或网络套接字,您就可以通过接受该套接字上的连接来为您的客户端获取套接字。每个客户端都获得一个新的套接字号。
然后,将这些套接字、服务器套接字和客户端套接字添加到 fd_set 并将其传递给 select。 Select 将同时监听所有套接字,并将接收到数据的套接字留在集合中。
现在您迭代集合以查看哪些套接字是热的,您就在那里!
还有一件事,我猜你很困惑,所有客户端都连接到 相同的 服务器套接字地址(文件)。是的,就像许多进程同时打开同一个文件一样......但这不是一个普通文件,它是一个 Unix 套接字。 :)
玩得开心,祝你好运!!!