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表示块设备文件。
在这里插入图片描述

二:主要函数

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 上点击发送按钮。 收到消息后可以在 控制台查看 输出。
在这里插入图片描述

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);//接受新链接, 并得到新的idself.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 文件代码

//
//  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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/244242.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据的存储

目录 1 -> 数据类型的介绍 1.1 -> 类型的基本归类 2 -> 整型在内存中的存储 2.1 -> 原码、反码、补码 2.2 -> 大小端介绍 3 -> 浮点型在内存中的存储 3.1 -> 浮点数存储规则 1 -> 数据类型的介绍 基本内置类型有&#xff1a; char /…

小程序技术实践:快速开发适配鸿蒙的App

今年&#xff0c;在中国&#xff0c;被各大媒体和开发者称为“鸿蒙元年”。 在2023年底就有业内人士透露&#xff0c;华为明年将推出不兼容安卓的鸿蒙版本&#xff0c;未来IOS、鸿蒙、安卓将成为三个各自独立的系统。 果不其然&#xff0c;执行力超强的华为&#xff0c;与202…

黑马程序员JavaWeb开发|Maven高级

一、分模块设计与开发 分模块设计&#xff1a; 将项目按照功能拆分成若干个子模块&#xff0c;方便项目的管理维护、扩展&#xff0c;也方便模块间的相互调用&#xff0c;资源共享。 注意&#xff1a;分模块开发需要先对模块功能进行设计&#xff0c;再进行编码。不会先将工…

C++设计模式之迭代器模式

【声明】本题目来源于卡码网&#xff08;https://kamacoder.com/&#xff09; 【提示&#xff1a;如果不想看文字介绍&#xff0c;可以直接跳转到C编码部分】 【设计模式大纲】 【简介】 --什么是迭代器模式&#xff08;第19种设计模式&#xff09; 迭代器模式是⼀种行为设计模…

消息中间件之Kafka(二)

1.Kafka线上常见问题 1.1 为什么要对topic下数据进行分区存储? 1.commit log文件会受到所在机器的文件系统大小的限制&#xff0c;分区之后可以将不同的分区放在不同的机器上&#xff0c; 相当于对数据做了分布式存储&#xff0c;理论上一个topic可以处理任意数量的数据2.提…

Hbas简介:数据模型和概念、物理视图

文章目录 说明零 BigTable一 Hbase简介二 HBase 访问接口简介三 行式&列式存储四 HBase 数据模型4.1 HBase 列族数据模型4.2 数据模型的相关概念4.3 数据坐标 五 概念&物理视图 说明 本文参考自林子雨老师的大数据技术原理与应用(第三版)教材内容&#xff0c;仅供学习…

MySQL---多表分组查询综合练习

创建dept表 CREATE TABLE dept ( deptno INT(2) NOT NULL COMMENT 部门编号, dname VARCHAR (15) COMMENT 部门名称, loc VARCHAR (20) COMMENT 地理位置 ); 添加dept表主键 mysql> alter table dept add primary key(deptno); Query OK, 0 rows affected (0.02 s…

Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))

目录 AOP概念AOP底层原理AOP(JDK动态代理)使用 JDK 动态代理&#xff0c;使用 Proxy 类里面的方法创建代理对象**编写** **JDK** 动态代理代码 AOP(术语)AOP操作&#xff08;准备工作&#xff09;**AOP** **操作&#xff08;**AspectJ注解)**AOP** **操作&#xff08;**AspectJ…

GitHub无法完成推送 的设置选项

GitHub无法完成推送 的设置选项 系统设置 VS中控制台设置【指令】 控制台调出方法 以下为VS控制台指令 git config --global --unset http.proxy git config --global --unset https.proxygit config --global http.proxy 127.0.0.1:7890 git config --global https.proxy …

百万级监控指标的秒级采集与处理

随着云原生概念的深入普及和应用落地&#xff0c;企业应用架构由单体架构演进为微服务架构&#xff0c;应用将状态剥离到中间件层&#xff0c;通过无状态化实现更灵活的容器化部署和水平伸缩。然而&#xff0c;引入微服务框架、Kubernetes、分布式中间件等组件&#xff0c;使得…

定向减免!函数计算让 ETL 数据加工更简单

业内较为常见的高频短时 ETL 数据加工场景&#xff0c;即频率高时延短&#xff0c;一般费用大头均在函数调用次数上&#xff0c;推荐方案一般为攒批处理&#xff0c;高额的计算成本往往令用户感到头疼&#xff0c;函数计算推出定向减免方案&#xff0c;让 ETL数据加工更简单、更…

【漏洞复现】CloudPanel makefile接口远程命令执行漏洞(CVE-2023-35885)

文章目录 前言声明一、CloudPanel 简介二、漏洞描述三、影响版本四、漏洞复现五、修复建议 前言 CloudPanel 是一个基于 Web 的控制面板或管理界面&#xff0c;旨在简化云托管环境的管理。它提供了一个集中式平台&#xff0c;用于管理云基础架构的各个方面&#xff0c;包括 &a…

相关系数(皮尔逊相关系数和斯皮尔曼相关系数)

本文借鉴了数学建模清风老师的课件与思路&#xff0c;可以点击查看链接查看清风老师视频讲解&#xff1a;5.1 对数据进行描述性统计以及皮尔逊相关系数的计算方法_哔哩哔哩_bilibili 注&#xff1a;直接先看 &#xff08; 三、两个相关系数系数的比较 &#xff09; 部分&#x…

解决 BeanUtil.copyProperties 不同属性直接的复制

1、引入hutool <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version> </dependency> hutool官网 2、直接上例子 对象&#xff1a;User.java Data public class User {p…

二进制计算

二进制的引入 十进制规则:满10进1&#xff0c;由数字0到9组成。 而所谓十六进制&#xff0c;八进制&#xff0c;二进制的规则也是类似。 这里为了区分十六进制和八进制&#xff0c;十六进制前面会加上0x&#xff0c;八进制前面会加个0作为区分 而二进制的规则类似于十进制&…

ZEM20台式扫描电子显微镜在混凝土材料微观结构及性能研究中的应用

混凝土作为当今世界上使用量最大的建筑材料&#xff0c;广泛应用于桥梁、大坝、高楼等重要民用工程和设施中发挥着举足轻重的作用。但是普通混凝土存在抗压强度不高、脆性大等缺陷。此外&#xff0c;水泥生产过程还伴随着高能耗和二氧化碳排放问题。因此研发高性能、环保型混凝…

5G_射频测试_发射机测量(四)

6.2 Base station output power 用于测量载波发射功率的大小&#xff0c;功率越大小区半径越大但是杂散也会越大 载波功率&#xff08;用频谱仪测&#xff09;天线口功率&#xff08;用功率计测&#xff09;载波功率是以RBW为单位的filter测量的积分功率不同带宽的多载波测试时…

Jedis(一)与Redis的关系

一、Jedis介绍&#xff1a; 1、背景&#xff1a; Jedis是基于Java语言的Redis的客户端&#xff0c;Jedis Java Redis。Redis不仅可以使用命令来操作&#xff0c;现在基本上主流的语言都有API支持&#xff0c;比如Java、C#、C、PHP、Node.js、Go等。在官方网站里有一些Java的…

密码学的100个基本概念

密码学作为信息安全的基础&#xff0c;极为重要,本文分为上下两部分&#xff0c;总计10个章节&#xff0c;回顾了密码学的100个基本概念&#xff0c;供小伙伴们学习参考。本文将先介绍前五个章节的内容。 一、密码学历史 二、密码学基础 三、分组密码 四、序列密码 五、哈希…

【产品交互】超全面B端设计规范总结

不知不觉已经深耕在B端这个领域3年有余&#xff0c;很多人接触过B端后会觉得乏味&#xff0c;因为B端的设计在视觉上并没有C端那么有冲击力&#xff0c;更多的是结合业务逻辑&#xff0c;设计出符合业务需求的交互&#xff0c;以及界面排版的合理性&#xff0c;达到产品的可用性…