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

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

一: 简介

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

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

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


在终端使用 netstat -nta -p tcp | grep 8766
可以查看 8766 端口的连接情况,可以看到 已经有 62106端口的客户端连接成功。
在这里插入图片描述

二:主要函数

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

  • domain 选择 AF_INET, /* internetwork: UDP, TCP, etc. */
  • type. 选择SOCK_STREAM, 代表Tcp。 如果是udp的话,需要使用 SOCK_DGRAM
  • protocol 填0, 由系统选择

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

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

  • sockfd 前面返回的描述符
  • myaddr 包含有ip和port的struct 对象
  • addrlen前一个stuct的长度

3. int listen(int sockfd, int backlog)

调用后,本地socket端口的状态变更为:LISTEN, 可以使用netstat -nta -p tcp | grep 8766在终端查看

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

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

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

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

tcp的接收客户端发来的数据

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

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

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

关闭连接

三:demo代码

如下图,创建了两个进程,分别为服务器(app), 客户端为 命令行程序(客户端的代码可以直接移植到win)
在这里插入图片描述

1. 服务器端主要逻辑

  • 主要创建了socket一个 internetwork (AF_INET)和 TCP (SOCK_STREAM)组合的socket
  • 绑定任何ip的8766端口(代表任意ip的客户端都可以连接过来)
  • listen() 开始监听
  • 启动子线程,在线程内 阻塞等待客户端连接(accept),和接收客户端消息(read)
  • 启动客户端命令行进程。(这里可以在本地其他地方或者局域网内的其他电脑启动命令行)
  • 点击ui上的发送按钮,往客户端发送消息

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

#import "ViewController.h"
#import <sys/socket.h>
#include <thread>@interface ViewController ()
@property (weak) IBOutlet NSTextField *textLabel;
@property (nonatomic, assign) int listenFd;
@property (nonatomic, assign) int currListenFd;
@endstatic void subThreadWorker(ViewController *vc);@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];int listenFd;listenFd = socket(AF_INET, SOCK_STREAM, 0);self.listenFd = listenFd;if (listenFd == -1) {perror("socket create failed!");return;}//    INADDR_ANY是ANY,是绑定地址0.0.0.0上的监听, 能收到任意一块网卡的连接;//    INADDR_LOOPBACK, 也就是绑定地址LOOPBAC, 往往是127.0.0.1, 只能收到127.0.0.1上面的连接请求/**serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);$ netstat -nta -p tcp | grep LISTENtcp4       0      0  *.8765                 *.*                    LISTEN------serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");//tcp4       0      0  127.0.0.1.8766         *.*                    LISTEN*/struct sockaddr_in serverAddr = {0};serverAddr.sin_family = AF_INET;
//    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);serverAddr.sin_addr.s_addr = INADDR_ANY; //接受任意ip
//    serverAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);serverAddr.sin_port = htons(8766); //监听端口号8766int ret = bind(listenFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));if (ret == -1) {perror("socket bind failed!");return;}//5代表正在等待完成相应的TCP三次握手过程的队列长度ret = listen(listenFd, 5);if (ret == -1) {printf("socket listen failed! error: %s(errno: %d)\n",strerror(errno),errno);perror("socket listen failed!");return;}std::thread(subThreadWorker, self).detach();printf("创建成功!\n");//启动子进程NSURL *subAppURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/Socket_TCP_ClientApp", [NSBundle mainBundle].executablePath.stringByDeletingLastPathComponent]];[[NSWorkspace sharedWorkspace] openURL:subAppURL configuration:[NSWorkspaceOpenConfiguration configuration] completionHandler:nil];
}static void subThreadWorker(ViewController *vc) {//单独线程监听服务器发回来的消息ssize_t numRed = 0;static const int buffer_size = 4096;char buff[buffer_size];int tmpListenFd = vc.listenFd;while (tmpListenFd != -1) {printf("服务器等待客户端  %i  连接...\n", vc.listenFd);tmpListenFd = accept(tmpListenFd, NULL, NULL);printf("收到客户端连接。 sfd:%i\n", vc.listenFd);if (vc.listenFd == -1) {printf("socket accept failed! error: %s(errno: %d)\n",strerror(errno),errno);perror("这是一个无效的连接!");break;}vc.currListenFd = tmpListenFd;//循环读取 client 发来的消息while ((numRed = read(tmpListenFd, buff, buffer_size)) > 0) {printf("服务器收到客户端发的数据: %s\n", buff);}if (numRed == -1) {perror("numRed == -1!");break;}if (close(tmpListenFd) == -1) {perror("close faild!");break;}tmpListenFd = vc.listenFd;printf("for over!\n");}
}//点击按钮
- (IBAction)sendMsgToClient:(id)sender {const  char *backBuffer = [self.textLabel.stringValue UTF8String];ssize_t sendLen =  write(self.currListenFd, backBuffer, strlen(backBuffer)+1);if (sendLen < 0) {printf("error:%i\n", errno);perror("服务器发送给客户端失败!reason:");} else {printf("服务器发送给客户端成功!len:%zi\n", sendLen);}
}- (void)dealloc {
}@end

2. 客户端主要逻辑

  • 如果是windows,需要首先调用 WSAStartup(...)
  • 同样创建socket 连接,tcp 类型的。socket(AF_INET, SOCK_STREAM, 0);
  • 连接到服务器,地址为: "10.34.133.46:8766"(如果本机的话可以使用127.0.0.1), connect(...)
  • 向服务器发送一条消息, send(client_fd, buf, sizeof(buf), 0)
  • 收到一次消息,就退出程序. recv(client_fd, buf, sizeof(buf), 0)
  • 处理退出前的清理工作。

主要代码: main.cpp 文件代码

//
//  main.cpp
//  Socket_TCP_ClientApp
//
//  Created by jimbo on 2024/1/22.
//#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>#ifdef _WIN32
#include <system_error>
#include <WS2tcpip.h>
#pragma warning(disable:4996)
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#endif // _WIN32
#pragma comment(lib, "ws2_32.lib")#ifdef _WIN32
class  SocketInit
{
public:SocketInit() {//Win 必须在socket 使用前调用 WSAStartup()WSADATA data;int ret = WSAStartup(MAKEWORD(2, 1), &data);printf("WSAStartup val:%d\n", ret);}~SocketInit() {printf("~SocketInit\n");WSACleanup();}
};
const SocketInit sInit;
#endif // _WIN32int main(int argc, char* argv[]) {printf("\n\n我是Client\n\n");int client_fd;struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8766); //端口server_addr.sin_addr.s_addr = inet_addr("10.34.133.46"); //ip//创建连接client_fd = socket(AF_INET, SOCK_STREAM, 0);if (client_fd < 0) {
#ifdef _WIN32char buf[1024];strerror_s(buf, 1024, errno);
#elsechar *buf = strerror(errno);
#endif // _WIN32printf("socket create failed. %s(errno:%d)\n", buf, errno);exit(EXIT_FAILURE);}//连接到服务器if (connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("socket connect failed");exit(EXIT_FAILURE);}printf("socket connect  success!\n");char buf[1024] = "hello socket from client!";//向服务器发送一条消息if (send(client_fd, buf, sizeof(buf), 0) < 0) {perror("socket send failed");exit(EXIT_FAILURE);}printf("socket send  success!\n");//收到一次消息,就退出程序if (recv(client_fd, buf, sizeof(buf), 0) < 0) {perror("socket recv failed");exit(EXIT_FAILURE);}printf("client recv response: [%s]\n", buf);#ifdef _WIN32/* 关闭socket */closesocket(client_fd);
#else/* 关闭socket */close(client_fd);
#endif // _WIN32printf("client ended successfully\n");exit(EXIT_SUCCESS);
}

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

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

相关文章

web安全学习笔记【05】——反弹Shell、正反向连接

思维导图 #知识点&#xff1a; 1、Web常规-系统&中间件&数据库&源码等 2、Web其他-前后端&软件&Docker&分配站等 3、Web拓展-CDN&WAF&OSS&反向&负载均衡等 ----------------------------------- 1、APP架构-封装&原生态&H5&am…

解决TCP粘包问题

粘包问题已经在上一篇博客中写到具体可以看----------- 以下是利用分包解决粘包问题的步骤&#xff1a; 消息长度前缀&#xff1a; 在每个数据包前加入消息长度信息。 定义协议格式&#xff1a; 明确定义通信协议的格式&#xff0c;包括消息头和消息体。 封包&#xff1a; 将…

Java研学-代理模式

一 概述 1 分类 静态代理&#xff1a;在程序运行前就已经存在代理类的字节码文件&#xff0c;代理对象和真实对象的关系在运行前就确定了。&#xff08;代理类及对象要自行创建&#xff09;   动态代理&#xff1a;代理类是在程序运行期间由 JVM 通过反射等机制动态的生成的…

电脑数据恢复软件哪个有效好用?十大电脑数据恢复软件排行

在数字时代&#xff0c;数据就是一切。从珍贵的家庭照片和重要的工作文档到最喜欢的音乐和电影&#xff0c;我们的生活越来越多地存储在各种设备上。系统崩溃、意外删除或恶意病毒都可能使您的宝贵数据瞬间消失。这就是数据恢复工具的用武之地。 十大电脑数据恢复软件排行 这些…

Integer.valueOf方法详解

Integer.valueOf 是 Java 中 Integer 类的一个静态方法&#xff0c;它用于将给定的字符串或基本数据类型转换成一个 Integer 对象。 使用场景 从字符串转换&#xff1a;将字符串形式的数字转换为 Integer 对象。 Integer num Integer.valueOf("123");从基本数据类…

论文阅读2---多线激光lidar内参标定原理

前言&#xff1a;该论文介绍多线激光lidar的标定内参的原理&#xff0c;有兴趣的&#xff0c;可研读原论文。 1、标定参数 rotCorrection&#xff1a;旋转修正角&#xff0c;每束激光的方位角偏移&#xff08;与当前旋转角度的偏移&#xff0c;正值表示激光束逆时针旋转&…

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)

几年前,公司买了台国产贴片机,里面的主程序是QT编写,运行在WINDOW XP系统上。主程序打开的界面,如图: 我来简单介绍下程序界面,各位读者不需要搞明白功能,只要知道大体的流程即可。 分析主界面: 一、左边的列表&#xff1a; 贴片生产文件,里面包括了贴片时元器件的坐标、飞达…

C#winform上位机开发学习笔记11-串口助手接收数据用波形显示功能添加

1.功能描述 接收串口数据&#xff0c;并将收到的十六进制数据用坐标系的方式将数据波形展示出来 2.代码部分 步骤1&#xff1a;定义链表&#xff0c;用于数据保存 //数据结构-线性链表private List<byte> DataList new List<byte>(); 步骤2&#xff1a;定义波…

shell脚本2

在自定义变量当中&#xff0c;不可以以数字开头 变量追加值 在变量名后要写的东西 read -p &#xff1a;交互式输入变量值&#xff0c;然后使用变量 自定义变量 export 全局变量 如果想要bash里面的所有进程都能看见变量&#xff0c;加入export 父进程 子进程…

计算机毕业设计 基于SpringBoot的民宿租赁系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

Sentinel 新版本发布,提升配置灵活性以及可观测配套

作者&#xff1a;屿山 基本介绍 Sentinel 是阿里巴巴集团开源的&#xff0c;面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;承接了阿里巴巴近 15 年的双十一大促流量的核心场景&#xff0c;例如秒杀、冷启动、消息削峰填谷、集群流量控制、实时熔断下游不可用服…

考研C语言刷题基础篇之分支循环结构基础(二)

目录 第一题分数求和 第二题&#xff1a;求10 个整数中最大值 第三题&#xff1a;在屏幕上输出9*9乘法口诀表 第四题&#xff1a;写一个代码&#xff1a;打印100~200之间的素数 第五题&#xff1a;求斐波那契数的第N个数 斐波那契数的概念&#xff1a;前两个数相加等于第三…

2023 工业 AR 关键词:纵深和开拓

2023 年&#xff0c;以虚实融合、工业元宇宙为代表的“新数字化”升级在工业制造领域达成共识。 ▲五部委联合印发元宇宙行动计划 通过发展元宇宙赋能新型工业化 而相对过去几年的行业渗透广、落地场景多样的 AR 业务拓展与合作&#xff0c;#纵深和#开拓&#xff0c;成为 2023…

80端口被占用解决思路

普及一个概念&#xff1a;80端口是 HTTP&#xff08;HyperText Transport Protocol)即超文本传输协议开放的&#xff0c;此为上网冲浪使用次数最多的协议&#xff0c;主要用于WWW&#xff08;World Wide Web&#xff09;即万维网传输信息的协议。 我们使用 http 域名访问时都会…

Vue3 ref与reactive

✨ 专栏介绍 在当今Web开发领域中&#xff0c;构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架&#xff0c;正是为了满足这些需求而诞生。它采用了MVVM架构模式&#xff0c;并通过数据驱动和组件化的方式&#xff0c;使…

HarmonyOS鸿蒙学习基础篇 - 基本语法概述

书接上文 HarmonyOS鸿蒙学习基础篇 - 运行第一个程序 Hello World 基本语法概述 打开 entry>src>main>ets>pages>index.ets 代码如下代码详细解释如下&#xff1a; Entry //Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中&#xff0c;最多可以使用…

Dify学习笔记-应用发布(四)

1、发布为公开 Web 站点 使用 Dify 创建 AI 应用的一个好处在于&#xff0c;你可以在几分钟内就发布一个可供用户使用的 Web 应用&#xff0c;该应用将根据你的 Prompt 编排工作。 如果你使用的是自部署的开源版&#xff0c;该应用将运行在你的服务器上 如果你使用的是云服务&…

2024年跨境电商上半年有哪些营销节日?

2024年伊始&#xff0c;跨境电商开启新一轮的营销竞技&#xff0c;那么首先需要客户需求&#xff0c;节假日与用户需求息息相关&#xff0c;那么接下来小编为大家整理2024上半年海外都有哪些节日和假期&#xff1f;跨境卖家如何见针对营销日历选品&#xff0c;助力卖家把握2024…

Java框架篇面试题

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d7;本文收录于java面试题系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏Rust初阶教程、go语言基…

flutter底层架构初探

本文出处&#xff1a;​​​​​​​​​​​​​Flutter 中文开发者网站 架构 embedder嵌入层 提供程序入口&#xff08;其他原生应用也采用此方式&#xff09;&#xff0c;程序由此和底层操作系统协调&#xff08;surface渲染、辅助功能和输入服务&#xff0c;管理事件循环…