进程间通信(IPC)的方法:UNIX域套接字

2023-09-17 11:24:00

      UNIX域套接字(UNIX domain socket)为我们提供了一种在进程之间建立通信通道的便捷方法,具有许多有用的内置功能。它支持面向流(TCP)和面向数据报(UDP)协议作为TCP/IP互联网套接字。我们还可以在阻塞和非阻塞模式之间进行选择。
      首先需要创建套接字并在套接字函数中指定AF_UNIX作为域套接字。创建套接字后,必须使用绑定函数将套接字绑定到唯一的文件路径。与AF_INET域中的Internet套接字绑定到唯一的IP地址和端口号不同,UNIX域套接字绑定到文件路径。文件系统中创建的此文件,当程序关闭且不再需要该文件时,你必须手动将其删除。
      UNIX域套接字与server/client网络套接字通信没有太大不同,但它旨在供本地文件系统使用。server/client网络套接字介绍参考:https://blog.csdn.net/fengbingchun/article/details/107848160

      UNIX域套接字总结
      (1).同步;
      (2).极高的吞吐量;存储设备速度限制;
      (3).双向通信;
      (4).以线性方式读写;
      (5).自动内存管理。

      注:以上内容主要来自网络整理。

      测试代码如下:

#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <iostream>

int main()
{
    // reference: https://biendltb.github.io/tech/inter-process-communication-ipc-in-cpp/
    const char* server_sock_path = "/tmp/unix_sock.server";
    const char* client_sock_path = "/tmp/unix_sock.client";

    pid_t pid = fork(); // create two processes of client and server
    if (pid < 0) {
        fprintf(stderr, "fail to fork\n");
        return -1;
    }

    if (pid != 0) { // server process(parent process)
        auto server_sock = socket(AF_UNIX, SOCK_STREAM, 0); // open the server socket with the SOCK_STREAM type
        if (server_sock == -1) {
            fprintf(stderr, "SERVER: fail to socket: %s\n", strerror(errno));
            exit(1);
        }

        // bind to an address on file system
        // similar to other IPC methods, domain socket needs to bind to a file system, so that client know the address of the server to connect to
        struct sockaddr_un server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sun_family = AF_UNIX;
        strcpy(server_addr.sun_path, server_sock_path);
        
        unlink(server_sock_path); // unlink the file before bind, unless it can't bind: error info: Address already in use
        auto rc = bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
        if (rc == -1) {
            fprintf(stderr, "SERVER: fail to bind: %s\n", strerror(errno));
            exit(1);
        }

        // listen and accept client connection
        // set the server in the "listen" mode and maximum pending connected clients in queue
        rc = listen(server_sock, 10);
        if (rc == -1) {
            fprintf(stderr, "SERVER: fail to listen: %s\n", strerror(errno));
            exit(1);
        }

        fprintf(stdout, "SERVER: Socket listening...\n");
        struct sockaddr_un client_addr;
        auto len = sizeof(client_addr);
        int client_fd = accept(server_sock, (struct sockaddr *)&client_addr, (socklen_t*)&len);
        if (client_fd == -1) {
            fprintf(stderr, "SERVER: fail to accept: %s\n", strerror(errno));
            exit(1);
        }
        fprintf(stdout, "SERVER: Connected to client at: %s\n", client_addr.sun_path);
        fprintf(stdout, "SERVER: Wating for message...\n");

        const int buf_len = 256;
        char buf[buf_len];
        memset(buf, 0, buf_len);
        int byte_recv = recv(client_fd, buf, buf_len, 0);
        if (byte_recv == -1) {
            fprintf(stderr, "SERVER: fail to recv: %s\n", strerror(errno));
            exit(1);
        }
        else
            fprintf(stdout, "SERVER: Server received message: %s.\n", buf);

        fprintf(stdout, "SERVER: Respond to the client...\n");
        memset(buf, 0, buf_len);
        strcpy(buf, "hello from server");
        rc = send(client_fd, buf, buf_len, 0);
        if (rc == -1) {
            fprintf(stderr, "SERVER: fail to send:%s\n", strerror(errno));
            exit(1);
        }
        fprintf(stdout, "SERVER: Done!\n");

        close(server_sock);
        close(client_fd);
        remove(server_sock_path); // remove access to a file named

        int status;
        auto pid2 = wait(&status); // system call suspends execution of the calling thread until one of its children terminates
        fprintf(stdout, "process ID of the terminated child: %d\n", pid2);
        if (WIFEXITED(status)) { // returns true if the child terminated normally
            fprintf(stdout, "child process ended with: exit(%d)\n", WEXITSTATUS(status));
        }
        if (WIFSIGNALED(status)) { // returns true if the child process was terminated by a signal
            fprintf(stderr, "child process ended with: kill -%d\n", WTERMSIG(status));
        }
    }

    if (pid == 0) { // client process(child process)
        int client_sock = socket(AF_UNIX, SOCK_STREAM, 0);
        if (client_sock == -1) {
            fprintf(stderr, "CLIENT: fail to socket: %s\n", strerror(errno));
            exit(1);
        }

        // bind client to an address on file system
        // Note: this binding could be skip if we want only send data to server without receiving
        struct sockaddr_un client_addr;
        memset(&client_addr, 0, sizeof(client_addr));
        client_addr.sun_family = AF_UNIX;
        strcpy(client_addr.sun_path, client_sock_path);

        unlink (client_sock_path);
        auto rc = bind(client_sock, (struct sockaddr *)&client_addr, sizeof(client_addr));
        if (rc == -1) {
            fprintf(stderr, "CLIENT: fail to bind: %s\n", strerror(errno));
            exit(1);
        }

        // Set server address and connect to it
        struct sockaddr_un server_addr;
        server_addr.sun_family = AF_UNIX;
        strcpy(server_addr.sun_path, server_sock_path);
        rc = connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
        if (rc == -1) {
            fprintf(stderr, "CLIENT: fail to connect: %s\n", strerror(errno));
            exit(1);
        }
        fprintf(stdout, "CLIENT: Connected to server.\n");

        // Send message to server
        const int buf_len = 256;
        char buf[buf_len];
        memset(buf, 0, buf_len);
        strcpy(buf, "hello from client");
        rc = send(client_sock, buf, buf_len, 0);
        if (rc == -1) {
            fprintf(stderr, "CLIENT: fail to send: %s\n", strerror(errno));
            exit(1);
        }
        fprintf(stdout, "CLIENT: Sent a message to server.\n");

        fprintf(stdout, "CLIENT: Wait for respond from server...\n");
        memset(buf, 0, buf_len);
        rc = recv(client_sock, buf, buf_len, 0);
        if (rc == -1) {
            fprintf(stderr, "CLIENT: fail to recv: %s\n", strerror(errno));
            exit(1);
        }
        else
            fprintf(stdout, "CLIENT: Message received: %s\n", buf);

        fprintf(stdout, "CLIENT: Done!\n");

        close(client_sock);
        remove(client_sock_path);
        exit(0);
    }

    fprintf(stdout, "====== test finish ======\n");
    return 0;
}

      编译脚本build.sh如下:

#! /bin/bash

if [ -d build ]; then
    echo "build directory already exists, it does not need to be created again"
else
    mkdir -p build
fi

cd build
cmake ..
make

rc=$?
if [[ ${rc} != 0 ]];then
    echo "#### ERROR: please check ####"
    exit ${rc}
fi

echo "==== build finish ===="

      CMakeLists.txt内容如下:

cmake_minimum_required(VERSION 3.22)
project(samples_multi_process)

set(CMAKE_BUILD_TYPE Release) # only works under linux
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O2 -std=c++17")

file(GLOB samples ${PROJECT_SOURCE_DIR}/test_*.cpp)
#message(STATUS "samples: ${samples}")

foreach(sample ${samples})
    string(REGEX MATCH "[^/]+$" name ${sample})
    string(REPLACE ".cpp" "" exec_name ${name})
    #message(STATUS "exec name: ${exec_name}")

    add_executable(${exec_name} ${sample})
    target_link_libraries(${exec_name} rt)
endforeach()

      执行结果如下所示:

      GitHubhttps://github.com/fengbingchun/Linux_Code_Test

更多推荐

地牢大师问题(bfs提高训练 + 免去边界处理的特殊方法)

地牢大师问题文章目录地牢大师问题前言题目描述题目分析输入处理移动方式【和二维的对比】边界判断问题的解决代码总结前言在之前的博客里面,我们介绍了bfs基础算法的模版和应用,这里我们再挑战一下自己,尝试一个更高水平的题目,加深一下对bfs算法的理解。如果对bfs更多知识感兴趣的话,可以点个关注,后续会继续更新有关知识点的。

2023/9/15 -- C++/QT

作业:1>将工程文件进行注释2>03login_box.pro:QT+=coregui#core核心库gui图形开发库greaterThan(QT_MAJOR_VERSION,4):QT+=widgets#4.0版本以上自动包含widgets库CONFIG+=c++11#支持C++11版本#Thefollowingde

如何在百度百科建立个人词条,百度词条建立的过程是怎样的

如何在百度百科建立个人词条,很多朋友对百科全书这些问题都非常感兴趣,特别是很多朋友比较关心如何在百度百科上创建自己,下面洛希爱做百科网分享百度百科词条建立的过程,让大家明白创建百科词条是怎么样的。一、准备工作百度百科词条平台不能自己创建是个不正确的说法,自己完全可以再百度百科平台编辑。主要先做好以下准备工作:收集资料:

书剑宠物疫苗接种管理软件操作教程

【软件简介】书剑宠物疫苗接种管理软件是一款宠物疫苗接种管理的工具,适合宠物诊所使用。具有动物主人建档、宠物疫苗接种登记管理、每日提醒、打印疫苗接种通知卡、自定义短信提醒模板等完善的功能。另外本软件的特色是同时具有手机网页版功能,手机扫一扫即能接到电脑数据库,然后在手机上批量打电话,发短信,提醒疫苗接种即将到期的宠物主人

支持国密浏览器的堡垒机叫什么?联系电话多少?

支持国密浏览器的堡垒机叫什么?联系电话多少?目前市面上支持国密浏览器的堡垒机不多,这里给大家推荐行云管家堡垒机,其支持国密算法、国密浏览器,满足数据安全要求。行云管家堡垒机优势1、技术领先:技术团队实力雄厚,自主研发,原厂商;2、服务优质:提供24小时不间断的技术支持和服务;3、经验丰富:拥有10万+中小企业客户;4、

Command not found 解决方法

前言:要更新code上服务器用GUI失败,$patch_delivery_gui,报错:patch_delivery_gui:commandnotfound,上次编TA也是这个问题写了个脚本:这个脚本会先检查ifconfig、firewall-cmd和vim命令是否可用,如果不可用,则尝试安装相应的软件包。然后,它会显

从Vue 2到Vue 3:深入了解路由配置的变化与升级建议

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录📘前言vue2路由配置📟一、控制台安装vue路由📟二、项目src文件夹下创建router文件夹,并在router文件夹下创建index.js文件📟三、在index.js文件夹下进行vue路由配置📟四、

【云原生 | 56】Docker三剑客之Docker Swarm高效使用

🍁博主简介:🏅云计算领域优质创作者🏅2022年CSDN新星计划python赛道第一名🏅2022年CSDN原力计划优质作者🏅阿里云ACE认证高级工程师🏅阿里云开发者社区专家博主💊交流社区:CSDN云计算交流社区欢迎您的加入!目录1.创建集群id2.配置集群节点3.配置管理节点4.查看集群节点列表5.使用集群

软考 - 计算机组成与体系笔记

数据的表示进制转化二进制转十进制(十进制以D表示)从右往左,用二进制位上的数字乘以2的n次幂的和(n从0开始+1累加)十进制转二进制(二进制以B表示)十进制数不断除以2直至到0,得到的余数按从下而上的顺序排列得到的数值二进制与八进制(八进制以O或Q表示)二进制从右往左,每三位对应的都是八进制的一位数二进制与十六进制(十

LeetCode 753. 破解保险箱【欧拉回路,DFS】困难

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及

优化系统报错提示信息,提高人机交互(三)

对于业务比较复杂的接口,可能存在方法嵌套,每个方法都可能会报错,出现异常,那么需要把异常信息返回给接口调用者,如何实现呢?(1)捕获异常进行处理,不返回controller代码@AllArgsConstructor@RestController@Slf4jpublicclassDemoController{privat

热文推荐