使用 Socket 建立 IPC 通信

笔记 · 2024-10-16 · 128 人浏览
使用 Socket 建立 IPC 通信

  IPC(Inter-Process Communication)即进程间通信,是计算机科学中的一个重要概念,是指两个不同进程之间数据交换的过程。

  Socket 编程原本是为了网络服务的,之前已介绍。后来逐渐发展成一种进程间通信的方式:Unix Domain Socket IPC。它允许在同一台主机上运行的进程之间进行高效的数据传输,无需经过网络协议栈,因此具有低延迟和高性能的特点。通过文件系统中的特殊文件(通常是一个套接字文件),进程可以通过套接字(socket)来进行通信,实现双向的数据传输。

  Socket 编程是网络通信中的一项基础而强大的技术,它允许两台计算机(或同一台计算机上的两个进程)之间进行双向通信。Socket 可以看作是网络通信中的一个端点,通过它可以发送或接收数据。在 TCP/IP 协议族中, Socke...
2024-09-01
3
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 通信的应用场景

  1. 操作系统:在操作系统中,每个进程都有自己独立的地址空间和资源,它们不能直接访问其他进程的内存空间。然而,在实际的应用中,进程之间需要进行数据交换、共享资源以及协作工作。为了实现这些功能,操作系统提供了IPC机制,允许进程之间进行通信和数据传输。

  2. 多线程编程:在多线程编程中,线程之间需要进行数据共享和同步,可以使用基于内存的IPC机制(如共享内存)来实现。

  3. 分布式系统:在分布式系统中,不同节点之间需要进行数据交换和通信,可以使用基于消息的IPC机制(如消息队列、套接字)来实现。

注意事项

  1. 同步与互斥:对于共享内存等基于内存的IPC机制,需要处理并发访问和同步的问题,避免数据的冲突和竞争。可以使用信号量、互斥锁等同步机制来保证数据的一致性和正确性。

  2. 消息传递的可靠性:对于消息传递等基于消息的IPC机制,需要保证消息的传递顺序和可靠性。可以使用消息队列的持久化存储、确认机制等来提高消息的可靠性。

  3. 安全性:在使用IPC通信时,需要注意安全性问题。例如,避免敏感数据的泄露、防止恶意进程的攻击等。可以使用加密、身份验证等安全措施来保护通信过程和数据的安全性。

C Socket
Theme Jasmine by Kent Liao

本网站由 又拍云 提供CDN加速/云存储服务

鄂ICP备2023005457号    鄂公网安备 42011302000815号

欢迎来自 * · * 的用户