IPC(Inter-Process Communication)即进程间通信,是计算机科学中的一个重要概念,是指两个不同进程之间数据交换的过程。
Socket 编程原本是为了网络服务的,之前已介绍。后来逐渐发展成一种进程间通信的方式:Unix Domain Socket IPC。它允许在同一台主机上运行的进程之间进行高效的数据传输,无需经过网络协议栈,因此具有低延迟和高性能的特点。通过文件系统中的特殊文件(通常是一个套接字文件),进程可以通过套接字(socket)来进行通信,实现双向的数据传输。
IPC通信的原理与机制
IPC 通信的原理是通过特定的机制,使得不同进程之间能够相互传递信息、协作完成任务。这些机制主要包括共享内存、消息传递、管道以及套接字等。这些机制主要包括以下几种:
1. 共享内存:
- 原理:通过将某一块内存区域映射到多个进程的地址空间中,实现不同进程之间的数据共享。
- 优点:可以高效地传递大量数据。
- 缺点:需要解决进程间的同步和互斥问题,以保证数据的正确性。如果多个进程同时访问共享内存区域,可能会导致数据冲突或不一致。
2. 消息传递:
- 原理:通过发送和接收消息的方式进行进程间通信。发送方将消息发送到一个消息队列或者直接发送给特定的进程,接收方则从消息队列中读取消息或者通过特定的接收机制接收消息。
- 优点:可以实现异步通信和进程间的解耦。发送方和接收方可以独立地运行,不需要知道对方的存在和状态。
- 缺点:需要解决消息的传递顺序和消息的可靠性问题。如果消息丢失或顺序错乱,可能会导致通信失败或数据不一致。
3. 管道:
- 原理:一种半双工的通信方式,用于连接一个读进程和一个写进程。管道可以使得两个进程能够单向地传递数据,其中一个进程充当写端,将数据写入管道,另一个进程充当读端,从管道中读取数据。
- 优点:适用于父子进程或者具有亲缘关系的进程之间的通信。
- 缺点:不适用于无关进程间的通信。同时,管道只能进行单向通信,且通信双方的进程必须同时存在。
4. 套接字:
- 原理:一种在不同主机间进行网络通信的机制,它可以在应用层与传输层之间建立连接,并通过网络传输数据。套接字可以通过TCP或UDP协议进行通信,可以在同一台主机上的不同进程间或者不同主机间进行通信。
- 优点:具有灵活性和可扩展性。套接字可以支持多种通信协议和数据传输方式,适用于各种复杂的网络通信场景。
- 缺点:需要注意网络传输的可靠性和安全性。网络传输过程中可能会出现数据丢失、延迟等问题,同时还需要考虑网络安全和身份验证等问题。
套接字实现
使用
AF_UNIX
通信域,可声明一个使用本地文件的 socket,其他进程可绑定该文件完成进程间通信。
socket_ipc.c:
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define SOCKET_PATH "unix_domain.socket"
#define SERVER_MODE 1
#define CLIENT_MODE 2
#define BUF_LEN 1024
#define OUTTEXT ("请输入要发送的信息:")
#define OUTSIZE sizeof(OUTTEXT)
static struct sockaddr_un socket_addr;
static char *buf;
void handle_error(char *err_msg)
{
perror(err_msg);
unlink(SOCKET_PATH); // 释放
exit(-1);
}
void server_mode(int sockfd)
{
struct sockaddr_un client_addr;
int client_fd, msg_len;
/**** 服务端接收消息,收到 EOF 结束信息 ****/
// 1. 绑定
if (bind(sockfd, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
{
handle_error("bind");
}
// 2. 挂起监听
if (listen(sockfd, 128) < 0)
{
handle_error("listen");
}
// 3. 获取连接
socklen_t client_addr_len = sizeof(client_addr);
if ((client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addr_len)) < 0)
{
handle_error("accept");
}
printf("接收到客户端的连接\n");
do
{
memset(buf, 0, BUF_LEN);
msg_len = recv(client_fd, buf, BUF_LEN, 0);
if (strncmp(buf, "EOF", 3))
{
printf("接收到客户端数据:%s\n", buf);
strcpy(buf, "ojbk\n\0");
} else
{
printf("接收到结算信息,停止接收数据\n");
}
// 回复信息 or 结束信息
send(client_fd, buf, strlen(buf), 0);
} while (strncmp(buf, "EOF", 3));
unlink(SOCKET_PATH);
}
void client_mode(int sockfd)
{
int msg_len;
/**** 客户端发送消息,发送 EOF 结束信息 ****/
// 连接服务端
if (connect(sockfd, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
{
handle_error("connect");
}
do
{
write(STDOUT_FILENO, OUTTEXT, OUTSIZE);
msg_len = read(STDIN_FILENO, buf, BUF_LEN);
if (send(sockfd, buf, BUF_LEN, 0) < 0)
{
handle_error("send");
}
// 清空缓冲区
memset(buf, 0, BUF_LEN);
recv(sockfd, buf, BUF_LEN, 0);
printf("接收到服务器回复的消息:%s", buf);
} while (strncmp(buf, "EOF", 3));
if (shutdown(sockfd, SHUT_WR) < 0)
{
handle_error("shutdown");
}
}
int main(int argc, char const *argv[])
{
int fd = 0, mode = 0;
// 设计结构
// 通过main方法传参的方式 启动不同的客户端和服务端
// 缺省:如果不填写参数 or 填写的参数默认是server
if (argc == 1 || strncmp(argv[1], "server", 6) == 0)
{
mode = SERVER_MODE;
} else if (strncmp(argv[1], "client", 6) == 0)
{
mode = CLIENT_MODE;
} else
{
perror("参数错误");
exit(EXIT_FAILURE);
}
// 创建socket_ipc通讯
memset(&socket_addr, 0, sizeof(socket_addr));
buf = malloc(BUF_LEN);
socket_addr.sun_family = AF_UNIX;
strcpy(socket_addr.sun_path, SOCKET_PATH);
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
{
handle_error("socket");
}
switch (mode)
{
case SERVER_MODE:
server_mode(fd);
break;
case CLIENT_MODE:
client_mode(fd);
break;
default:
break;
}
close(fd);
free(buf);
return 0;
}
测试结果
IPC 通信的应用场景
-
操作系统:在操作系统中,每个进程都有自己独立的地址空间和资源,它们不能直接访问其他进程的内存空间。然而,在实际的应用中,进程之间需要进行数据交换、共享资源以及协作工作。为了实现这些功能,操作系统提供了IPC机制,允许进程之间进行通信和数据传输。
-
多线程编程:在多线程编程中,线程之间需要进行数据共享和同步,可以使用基于内存的IPC机制(如共享内存)来实现。
-
分布式系统:在分布式系统中,不同节点之间需要进行数据交换和通信,可以使用基于消息的IPC机制(如消息队列、套接字)来实现。
注意事项
-
同步与互斥:对于共享内存等基于内存的IPC机制,需要处理并发访问和同步的问题,避免数据的冲突和竞争。可以使用信号量、互斥锁等同步机制来保证数据的一致性和正确性。
-
消息传递的可靠性:对于消息传递等基于消息的IPC机制,需要保证消息的传递顺序和可靠性。可以使用消息队列的持久化存储、确认机制等来提高消息的可靠性。
-
安全性:在使用IPC通信时,需要注意安全性问题。例如,避免敏感数据的泄露、防止恶意进程的攻击等。可以使用加密、身份验证等安全措施来保护通信过程和数据的安全性。