在Linux系统中,socketpair是一种创建双向通信管道的机制,它可以为进程之间的通信提供一种相对简单的方式。本文将介绍socketpair的用途、语法、使用限制以及一些实例应用。
1. socketpair的用途
相信不少读者都已经知道,pipe可以用来在进程之间传输数据,但是,pipe有些限制,比如只能单向传输数据,不能同时读写,只能用于父子进程之间通信等。而socketpair这种机制则是pipe的一个改进版,它支持双向通信,可以同时读写,也可以用于任意两个进程之间通信等。
2. socketpair的语法
socketpair的语法非常简单,可以在C语言、C++、Java等多种语言中使用。
在C语言中,可以使用以下函数创建一个socketpair:
int socketpair(int domain, int type, int protocol, int sv[2]);
参数说明:
- domain:协议族,通常为AF_UNIX或AF_INET。
- type:socket类型,通常为SOCK_STREAM或SOCK_DGRAM。
- protocol:协议,通常为0。
- sv:指向一个保存socket文件描述符的数组,其中index为0的socket表示读端,index为1的socket表示写端。
3. socketpair的使用限制
虽然socketpair是pipe的改进版,但是它也有一些使用限制,需要开发者注意。
- 只能用于同一个主机上的进程通信,而不能用于跨主机通信。
- 双向通信需要两个socket连接,因此socketpair只能用于两个进程之间通信。
- 在传输中需要使用自定义协议,因此程序开发中需要编写更多的代码。
4. socketpair实例
下面我们来看两个简单的socketpair实例。
4.1. 实例1:简单Echo服务器
我们先来看如何使用socketpair实现一个简单的Echo服务器,该服务器接收客户端的输入后,将其返回给客户端。
服务端代码:
#include
#include
#include
#include
#include
#include
#define PORT 9999
int main(int argc, char *argv[])
{
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len;
int t, len;
char buf[1024];
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_fd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1) {
perror("bind");
exit(1);
}
if (listen(server_fd, 5) == -1) {
perror("listen");
exit(1);
}
printf("server listening on port %d...\n", PORT);
client_len = sizeof(client_addr);
if ((client_fd = accept(server_fd,
(struct sockaddr *)&client_addr, &client_len)) == -1) {
perror("accept");
exit(1);
}
printf("Accepted connection from %s, port %d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
if (socketpair(PF_UNIX, SOCK_STREAM, 0, &t) != 0) {
perror("socketpair");
exit(1);
}
if (fork() == 0) {
close(client_fd);
while ((len = recv(t, buf, 1024, 0)) > 0) {
if (send(t, buf, len, 0) != len) {
perror("send");
exit(1);
}
}
exit(0);
}
close(t);
while ((len = recv(client_fd, buf, 1024, 0)) > 0) {
if (send(client_fd, buf, len, 0) != len) {
perror("send");
exit(1);
}
if (send(t, buf, len, 0) != len) {
perror("send");
exit(1);
}
}
close(client_fd);
return 0;
}
客户端代码:
#include
#include
#include
#include
#include
#include
#define PORT 9999
int main(int argc, char *argv[])
{
int fd;
struct sockaddr_in server_addr;
char buf[1024];
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
if (connect(fd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1) {
perror("connect");
exit(1);
}
printf("Type your message: ");
while (fgets(buf, sizeof(buf), stdin)) {
if (send(fd, buf, strlen(buf), 0) != strlen(buf)) {
perror("send");
exit(1);
}
memset(buf, 0, sizeof(buf));
int len = recv(fd, buf, 1024, 0);
if (len < 0) {
perror("recv");
exit(1);
}
printf("%s", buf);
}
return 0;
}
运行结果:
$ ./server
server listening on port 9999...
Accepted connection from 127.0.0.1, port 45660
$ ./client
Type your message: Hello world!
Hello world!
4.2. 实例2:使用socketpair实现多进程通信
我们再来看一个更复杂的例子,该例子使用socketpair实现了多个进程之间的通信,其中两个父进程分别与两个子进程通信,将消息传递下去。
这里需要先解释一下代码中的一些术语:
- ID:每个进程都有一个唯一的ID,用于标识进程。
- pipe_in:向该pipe写入消息,可以通过pipe_out来读取。
- pipe_out:从该pipe读取消息,可以通过pipe_in来写入。
代码:
#include
#include
#include
#include
#include
#include
#include
#define PARENT_A 1
#define PARENT_B 2
#define CHILD_A 3
#define CHILD_B 4
#define PARENT_A_PIPE_IN 0
#define PARENT_A_PIPE_OUT 1
#define PARENT_B_PIPE_IN 2
#define PARENT_B_PIPE_OUT 3
#define CHILD_A_PIPE_IN 4
#define CHILD_A_PIPE_OUT 5
#define CHILD_B_PIPE_IN 6
#define CHILD_B_PIPE_OUT 7
void send_message(int pid, int pipe_in, char *message) {
int len = strlen(message) + 1;
write(pipe_in, &pid, sizeof(int));
write(pipe_in, &len, sizeof(int));
write(pipe_in, message, len);
}
void read_message(int *pid, int pipe_out, char *message) {
int len;
read(pipe_out, pid, sizeof(int));
read(pipe_out, &len, sizeof(int));
read(pipe_out, message, len);
}
void parent_a(int pipe_in, int pipe_out) {
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
read_message(NULL, pipe_out, buffer);
printf("Parent_A received: %s\n", buffer);
send_message(CHILD_A, pipe_in, buffer);
}
void parent_b(int pipe_in, int pipe_out) {
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
read_message(NULL, pipe_out, buffer);
printf("Parent_B received: %s\n", buffer);
send_message(CHILD_B, pipe_in, buffer);
}
void child_a(int pipe_in, int pipe_out) {
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
read_message(NULL, pipe_out, buffer);
printf("Child_A received: %s\n", buffer);
send_message(PARENT_B, pipe_in, buffer);
}
void child_b(int pipe_in, int pipe_out) {
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
read_message(NULL, pipe_out, buffer);
printf("Child_B received: %s\n", buffer);
send_message(PARENT_A, pipe_in, buffer);
}
int main(int argc, char *argv[]) {
int pid;
int pipes[8];
char buffer[1024];
pid_t child_pid;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, &pipes[PARENT_A_PIPE_IN]) != 0) {
perror("socketpair");
exit(1);
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, &pipes[PARENT_B_PIPE_IN]) != 0) {
perror("socketpair");
exit(1);
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, &pipes[CHILD_A_PIPE_IN]) != 0) {
perror("socketpair");
exit(1);
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, &pipes[CHILD_B_PIPE_IN]) != 0) {
perror("socketpair");
exit(1);
}
child_pid = fork();
if (child_pid == 0) {
close(pipes[PARENT_A_PIPE_IN]);
close(pipes[PARENT_B_PIPE_IN]);
parent_a(pipes[CHILD_A_PIPE_OUT], pipes[CHILD_A_PIPE_IN]);
exit(0);
}
child_pid = fork();
if (child_pid == 0) {
close(pipes[PARENT_A_PIPE_IN]);
close(pipes[PARENT_B_PIPE_IN]);
parent_b(pipes[CHILD_B_PIPE_OUT], pipes[CHILD_B_PIPE_IN]);
exit(0);
}
child_pid = fork();
if (child_pid == 0) {
close(pipes[PARENT_A_PIPE_IN]);
close(pipes[PARENT_B_PIPE_IN]);
close(pipes[CHILD_B_PIPE_IN]);
close(pipes[CHILD_B_PIPE_OUT]);
child_a(pipes[CHILD_A_PIPE_OUT], pipes[CHILD_A_PIPE_IN]);
exit(0);
}
child_pid = fork();
if (child_pid == 0) {
close(pipes[PARENT_A_PIPE_IN]);
close(pipes[PARENT_B_PIPE_IN]);
close(pipes[CHILD_A_PIPE_IN]);
close(pipes[CHILD_A_PIPE_OUT]);
child_b(pipes[CHILD_B_PIPE_OUT], pipes[CHILD_B_PIPE_IN]);
exit(0);
}
close(pipes[CHILD_A_PIPE_IN]);
close(pipes[CHILD_B_PIPE_IN]);
close(pipes[CHILD_A_PIPE_OUT]);
close(pipes[CHILD_B_PIPE_OUT]);
memset(buffer, 0, sizeof(buffer));
send_message(PARENT_A, pipes[PARENT_A_PIPE_IN], "Hello, World!");
read_message(&pid, pipes[PARENT_A_PIPE_OUT], buffer);
printf("Parent received message from %d: %s\n", pid, buffer);
memset(buffer, 0, sizeof(buffer));
send_message(PARENT_B, pipes[PARENT_B_PIPE_IN], "How are you?");
read_message(&pid, pipes[PARENT_B_PIPE_OUT], buffer);
printf("Parent received message from %d: %s\n", pid, buffer);
wait(NULL);
wait(NULL);
wait(NULL);
wait(NULL);
return 0;
}
运行结果:
$ ./test
Parent_A received: Hello, World!
Child_A received: Hello, World!
Child_B received: Hello, World!
Parent received message from 3: Hello, World!
Parent_B received: How are you?
Child_A received: How are you?
Child_B received: How are you?
Parent received message from 4: How are you?