macOS跨进程通信: Unix Domain Socket 创建实例

这篇具有很好参考价值的文章主要介绍了macOS跨进程通信: Unix Domain Socket 创建实例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

macOS跨进程通信: Unix Domain Socket 创建实例

一: 简介

Socket 是 网络传输的抽象概念。
一般我们常用的有Tcp SocketUDP Scoket, 和类Unix 系统(包括Mac)独有的 Unix Domain Socket(UDX)。

  • Tcp Socket 能够跨电脑进行通信,即使是在同一个电脑下的多进程间通信,也会通过网卡进行数据传输,如果本地网卡的环回网络被禁用, 则会导致通信失败。
  • Unix Domain Socket,使用的是Liunx 系统中万物皆文件的概念,和有名管道的操作差不多,都是在文本创建一个特有的文件,用来在两个进程间通信,两个进程分别写入和读取文件流中的数据,达到传输的目的。 和Tcp Socket不一样的是不用借助网卡通信,限制比较小,传输的效率高。

这里主要针对 Unix Domain Socket进行研究.


在终端使用 ls -ll /tmp/
可以看到红圈中我们demo创建的Unix Domain Socket 文件。
Unix Domain Socket 会在 在第一列将会显示类型 s
这里还有其他类型的文件。其中p表示命名管道文件,d表示目录文件,l表示符号连接文件,-表示普通文件,s表示socket文件,c表示字符设备文件,b表示块设备文件。
macOS跨进程通信: Unix Domain Socket 创建实例,macos,unix,服务器,socket

二:主要函数

1. int socket (int domain, int type, int protocol) 创建socket 对象

  • domain 选择 AF_UNIX, 代表 unix domain socket
  • type. 选择SOCK_STREAM, socket 流
  • protocol 填0, 由系统选择

2. int bind(int sockfd, const struct sockaddr* myaddr, socklen_t addrlen)

将socket 绑定到对应 ip 和 端口上

  • sockfd 前面返回的描述符
  • myaddr 包含 通信 对象 路径的struct, 这里创建的是 /tmp/jimbo_udx_server.sock
  • addrlen前一个stuct的长度

3. int listen(int sockfd, int backlog)

调用后,本地socket 文件的状态变更

  • sockfd 前面返回的描述符
  • backlog 此socket 接收的客户端的数量

4. int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen)

阻塞式等待客户端接入,客户端接入后返回。
传入serversockfd,返回接入后的sockfd.
后面两个参数代表接口客户端的地址及struct长度

5. int read(int sockfd, void *buf, int len, unsigned int flags)

接收客户端发来的数据

6. int write(int sockfd, const void *msg, int len, int flags)

服务器 往 客户端/服务器发送数据

7. int close(int sockfd) 或 Windows的 7. int closesocket(int sockfd)

关闭连接

三:demo代码

如下图,创建了两个进程,分别为服务器App, 客户端App.
UI 上点击发送按钮。 收到消息后可以在 控制台查看 输出。
macOS跨进程通信: Unix Domain Socket 创建实例,macos,unix,服务器,socket

1. 服务器端主要逻辑

  • 主要创建了socket一个 AF_UNIXSOCK_STREAM 组合的socket

  • remove(...) 删除以前的sock 文件

  • bind 将文件路径和 socket 对象绑定在一起

  • listen() 开始监听

  • 启动子线程,在线程内 阻塞等待客户端连接(accept),和接收客户端消息(read)

  • 启动客户端进程。 客户端内进行连接到这个服务器

  • 点击ui上的发送按钮,往客户端发送消息

    主要代码: ViewController.mm 文件代码

//
//  ViewController.m
//  Sockct_UDX_MainApp
//
//  Created by jimbo on 2024/1/5.
//

#import "ViewController.h"
#include <sys/socket.h>

const char * s_sock_path = "/tmp/jimbo_udx_server.sock";


@interface ViewController ()
@property (weak) IBOutlet NSTextField *textLabel;

@property (nonatomic, assign) int sfd;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    /**
     int socket(int domain, int type, int protocol)
     AF_UNIX VS AF_INET(ipv4 tcp)
     SOCK_STREAM VS SOCK_DGRAM
     当protocol为0时,会自动选择type类型对应的默认协议。
     */
    int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    self.sfd = sfd;
    
    if (sfd == -1) {
        perror("socket create failed!");
        return;
    }
    // 删除所有与路径名一致的既有文件,这样才能将 socket 绑定到这个路径名上
      if (remove(s_sock_path) == -1 && errno != ENOENT){
          perror("remove failed");
          return;
      }
    
    struct sockaddr_un addr  = {0};
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, s_sock_path);
    
    //bind 的时候会在文件路径创建响应的s_sock_path文件. (UI app 需要关闭沙盒才能有权限访问对应的路径)
    //当使用ls –ll列出时,UNIX domain socket 在第一列将会显示类型 s
    //扩展一下,这个位置还可以有其他几种选项:p、d、l、s、c、b和-:
    //其中p表示命名管道文件,d表示目录文件,l表示符号连接文件,-表示普通文件,s表示socket文件,c表示字符设备文件,b表示块设备文件。
    int ret = bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un));
    if (ret == -1) {
        perror("bind faild!");
        return;
    }
    
    ret = listen(sfd, 5);
    if (ret == -1) {
        perror("listen failed!");
        return;
    }
    
    NSThread *th = [[NSThread alloc] initWithTarget:self selector:@selector(subThreadWorker) object:nil];
    [th setName:@"udx thread"];
    [th start];
    NSLog(@"---start");
    
    
    //启动子进程 app client
    
       //启动子进程
    NSURL *subAppURL = [[NSBundle mainBundle] URLForResource:@"Sockct_UDX_SubApp" withExtension:@"app"];
    [[NSWorkspace sharedWorkspace] openURL:subAppURL configuration:[NSWorkspaceOpenConfiguration configuration] completionHandler:nil];
}


- (void)subThreadWorker {
    NSLog(@"---subThreadWorker");
    
    ssize_t numRed = 0;
    static const int buffer_size = 100;
    char buffer[buffer_size];
    
    while (self.sfd != -1) {
        printf("服务器等待客户端%i连接...\n", self.sfd);
        //接受新链接, 并得到新的id
        self.sfd = accept(self.sfd, NULL, NULL);
        printf("收到客户端连接。 sfd:%i\n", self.sfd);
        
        if (self.sfd == -1) {
            perror("这是一个无效的连接!");
            break;
        }
        
        while ((numRed = read(self.sfd, buffer, buffer_size)) > 0) {
            printf("服务器收到客户端发的数据: %s\n", buffer);
        }
        if (numRed == -1) {
            perror("numRed == -1!");
            break;
        }
        if (close(self.sfd) == -1) {
            perror("close faild!");
            break;
        }
        printf("for over!\n");
    }
    
    printf("sub thread over!\n");
    
//    exit(0);
}

- (IBAction)sendMsgToClient:(id)sender {
    
    const  char *backBuffer = [self.textLabel.stringValue UTF8String];
    ssize_t sendLen =  write(self.sfd, backBuffer, strlen(backBuffer)+1);
    if (sendLen < 0) {
        printf("error:%i\n", errno);
        perror("服务器发送给客户端失败!reason:");
    } else {
        printf("服务器发送给客户端成功!len:%zi\n", sendLen);
    }
}


@end

2. 客户端主要逻辑

  • 主要创建了socket一个 AF_UNIXSOCK_STREAM 组合的socket
  • connect(...) 使用带服务器创建的sock 路径/tmp/jimbo_udx_server.sock 的结构体,和 socket 对象进行连接。 这样双方通信就建立了
  • read(...)在子线程 阻塞式 等待服务器的消息.
  • write(..) UI 按钮点击后,往服务器发消息

主要代码: ViewController.mm 文件代码文章来源地址https://www.toymoban.com/news/detail-820520.html

//
//  ViewController.m
//  Sockct_UDX_SubApp
//
//  Created by jimbo on 2024/1/5.
//

#import "ViewController.h"
#include <sys/socket.h>

const char * s_sock_path = "/tmp/jimbo_udx_server.sock";


@interface ViewController ()
@property (weak) IBOutlet NSTextField *textLabel;

@property (nonatomic, assign) int sfd;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    self.sfd = sfd;
    
    if (sfd == -1) {
        perror("socket create failed!");
        return;
    }
    
    /*
    // client 也可以同事绑定一个路径。自己又当客户端又当服务器
    
    const char * s_sock_path_client = "/tmp/jimbo_udx_client.sock";
     
    // 删除所有与路径名一致的既有文件,这样才能将 socket 绑定到这个路径名上
    if (remove(s_sock_path_client) == -1 && errno != ENOENT){
        perror("remove jimbo_udx_client.sock failed");
        return;
    }
    
    struct sockaddr_un addr_client  = {0};
    addr_client.sun_family = AF_UNIX;
    strcpy(addr_client.sun_path, s_sock_path_client);
    
    //bind 的时候会在文件路径创建响应的s_sock_path文件. (UI app 需要关闭沙盒才能有权限访问对应的路径)
    //当使用ls –l列出时,UNIX domain socket 在第一列将会显示类型 s
    //扩展一下,这个位置还可以有其他几种选项:p、d、l、s、c、b和-:
    //其中p表示命名管道文件,d表示目录文件,l表示符号连接文件,-表示普通文件,s表示socket文件,c表示字符设备文件,b表示块设备文件。
    int ret = bind(sfd, (struct sockaddr *)&addr_client, sizeof(struct sockaddr_un));
    if (ret == -1) {
        perror("bind  addr_client faild!");
        return;
    }
    */
    struct sockaddr_un addr = {0};
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, s_sock_path);
    
    NSLog(@"准备connect");
    int ret = connect(self.sfd, (const struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1) {
        perror("connect faild!");
        return;
    }
    NSLog(@"connect成功");
    
    
    [NSThread detachNewThreadWithBlock:^{
        //单独线程监听服务器发回来的消息
        static const int buffer_size = 100;
        char buffer[buffer_size];
        while (self.sfd != -1) {
            printf("等待服务的回调...\n");
            ssize_t len = read(self.sfd, buffer, buffer_size);
            printf("收到的服务器回馈长度:%zi\n", len);
            if (len <= 0) {
                printf("read error:%i\n", errno);
                perror("read failed");
//                assert(false); //需要判断是否服务器已经断开了的情况。
                exit(0);
            }else {
                printf("服务器返回的数据:%s\n", buffer);
            }
        }
        printf("等待服务器回调线程结束!\n");
    }];
    
}

- (IBAction)sendMegToServer:(id)sender {
    //发送消息
    const char *buf = [self.textLabel.stringValue UTF8String];
    size_t numWrite = strlen(buf) + 1;
    
    ssize_t writeSize = write(self.sfd, buf, numWrite);
    printf("numWrite: %zu writeSize:%zi\n", numWrite, writeSize);
    if (writeSize == -1) {
        perror("write failed!");
        return;
    }
}

- (void)dealloc {
    if (self.sfd > 0) {
        NSLog(@"关闭 sfd");
        close(self.sfd);
        self.sfd = -1;
    }
}

@end

到了这里,关于macOS跨进程通信: Unix Domain Socket 创建实例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • UNIX网络编程:socket实现client/server通信

    阅读 UNIX网络编程 卷1:套接字联网API 第3版 的前4个章节,觉得有必要对书籍上的源码案例进行复现,并推敲TCP的C/S通信过程。 📌 测试环境:CentOS7.6 x64 编译server.c 和 client.c gcc server.c -g -std=gnu99 -o server 和 gcc client.c -g -std=gnu99 -o client 运行测试: 📌 server.c仅仅实现对单个客户

    2024年02月06日
    浏览(49)
  • export 是一个在 Unix 和类 Unix 系统(比如 Linux 和 macOS)中常用的 shell 命令,主要用于设置或导出环境变量。

    export 是一个在 Unix 和类 Unix 系统(比如 Linux 和 macOS)中常用的 shell 命令,主要用于设置或导出环境变量。环境变量是在操作系统中用于存储系统设置和命令行程序配置的全局值。下面提供了一些 export 命令的基本用法和示例。 基本用法 设置环境变量 : 这里, VARIABLE_NAME 是

    2024年01月19日
    浏览(47)
  • UNIX网络编程:socket & fork()多进程 实现clients/server通信

    UNIX网络编程:socket实现client/server通信 随笔简单介绍了TCP Server服务单客户端的socket通信,但是并未涉及多客户端通信。 对于网络编程肯定涉及到多客户端通信和并发编程 (指在同时有大量的客户链接到同一服务器),故本随笔补充这部分知识。 而且并发并发编程涉及到多进程

    2024年02月06日
    浏览(51)
  • UNIX网络编程:socket & pthread_create()多线程 实现clients/server通信

    UNIX网络编程:socket fork()多进程 实现clients/server通信 随笔介绍了通过fork()多进程实现了服务器与多客户端通信。但除了多进程能实现之外,多线程也是一种实现方式。 重要的是,多进程和多线程是涉及操作系统层次。随笔不仅要利用pthread_create()实现多线程编程,也要理解线

    2024年02月06日
    浏览(55)
  • macOS 创建Flutter项目

    参考在 macOS 上安装和配置 Flutter 开发环境 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 这个文档,配置好flutter的环境 编辑器可以选择vscode或者IDEA。 我这里以IDEA为例 打开 IDE 并选中 New Flutter Project 。 选择 Flutter ,验证 Flutter SDK 的路径。完成后选择 Next 。 输入项目名称

    2024年02月06日
    浏览(56)
  • (macOS)创建SSH密钥

    关于SSH创建密钥 的 方法   第一步 打开终端 输入代码 如图所示:  点回车(即设置在默认位置)之后 输入密码 第二步 输入代码 打开~/.ssh/config文件(若没有这个文件 ,先创建再打开) 第三步  在~/.ssh/config文件中输入: 并且保存编辑。 第四步 输入代码 将SSH私钥添加到

    2024年02月07日
    浏览(44)
  • 创建MacOS应用的Button

    使用Objective-C创建Button: 在Objective-C中,可以使用 NSButton 类来创建按钮。 要绑定按钮事件,可以使用 addTarget:action:forControlEvents: 方法。 以下是一个示例代码: 这个示例代码中,我们创建了一个带有标题“按钮”的按钮,并将其添加到视图中。 然后,我们使用 setTarget:action

    2024年02月16日
    浏览(27)
  • 如何创建可引导的 macOS 安装介质

    如何创建可引导的 macOS 安装介质 如何创建可引导的 macOS 安装器 | 如何制作 macOS USB 启动盘 请访问原文链接:https://sysin.org/blog/macos-createinstallmedia/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org 当前版本: - macOS Ventura 13.4 (22F66) 正式版 ISO、IPSW、PKG 下载 您可

    2024年02月09日
    浏览(45)
  • 在MacOS上实现两个网络调试助手的UDP通信测试

    因为有一个项目要中会使用本机中两个应用程序之间的UDP通信。 因此本文记录一下怎么在MacOS上实现两个网络调试助手的UDP通信测试。 我使用的网络调试助手软件是:网络调试助手 直接在APP store里面下载就行了。 因为是两个网络调试助手之间的通信,因此我们需要双开该软

    2023年04月09日
    浏览(41)
  • 如何创建可引导的 macOS Sonoma 安装介质

    2023 年 9 月 26 日(北京时间 27 日凌晨)macOS Sonoma 正式版现已发布。 如何创建可引导的 macOS Sonoma 安装介质 如何创建可引导的 macOS 安装器 | 如何制作 macOS USB 启动盘 请访问原文链接:https://sysin.org/blog/macos-createinstallmedia/,查看最新版。原创作品,转载请保留出处。 作者主页

    2024年02月08日
    浏览(68)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包