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.rhkb.cn/news/244281.html

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

相关文章

xshell可以远程登录服务器但是vscode一直显示让输入密码的解决方案

vscode报错 但是xshell可以登录 原因&#xff1a;可能因为我上一次没有恰当的退出远程链接导致的&#xff0c;我每次退出远程都是直接强制关闭VScode。 解决方法&#xff1a;打开VScode的 view &#xff08;查看&#xff09; palette&#xff08;命令面板&#xff09;然后输…

【数据结构】 顺序栈的基本操作 (C语言版)

目录 一、顺序栈 1、顺序栈的定义&#xff1a; 2、顺序栈的优缺点 二、顺序栈的基本操作算法&#xff08;C语言&#xff09; 1、宏定义 2、创建结构体 3、顺序栈的初始化 4、顺序栈的入栈 5、顺序栈的出栈 6、取栈顶元素 7、栈的遍历输出 8、顺序栈的判空 9、顺…

【算法专题】动态规划之路径问题

动态规划2.0 动态规划 - - - 路径问题1. 不同路径2. 不同路径Ⅱ3. 珠宝的最高价值4. 下降路径最小和5. 最小路径和6. 地下城游戏 动态规划 - - - 路径问题 1. 不同路径 题目链接 -> Leetcode -62.不同路径 Leetcode -62.不同路径 题目&#xff1a;一个机器人位于一个 m …

(2)(2.4) CRSF/ELRS Telemetry

文章目录 前言 1 ArduPilot 参数编辑器 前言 &#xff01;Note ELRS&#xff08;ExpressLRS&#xff09;遥控系统使用穿越火线协议&#xff0c;连接方式类似。不过&#xff0c;它不像穿越火线那样提供双向遥测。 TBS CRSF 接收机与 ArduPilot 的接口中包含遥测和遥控信息。…

【大数据精讲】全量同步与CDC增量同步方案对比

目录 背景 名词解释 问题与挑战 FlinkCDC DataX 工作原理 调度流程 五、DataX 3.0六大核心优势 性能优化 背景 名词解释 CDC CDC又称变更数据捕获&#xff08;Change Data Capture&#xff09;&#xff0c;开启cdc的源表在插入INSERT、更新UPDATE和删除DELETE活动时…

c++ 包管理工具vcpkg

微软包管理工具 一、下载 git clone https://github.com/microsoft/vcpkg二、初始化 ./vcpkg/bootstrap-vcpkg.sh三、查看帮助文档 ./vcpkg/vcpkg help四、安装包 vcpkg/vcpkg install fmt五、查看安装包 vcpkg/vcpkg list输出 包实际安装路径 ./vcpkg/packages/fmt_x…

docker - compose 部署 Tomcat

目录 下面用 docker-compose 方法部署 Tomcat 1、准备工作 2、部署容器 启动容器 查看新启动的容器 3、总结 下面用 docker-compose 方法部署 Tomcat 1、准备工作 先在主机创建工作文件夹&#xff0c;为了放置 Tomcat 的配置文件等。创建文件夹的方法&#xff0c;自己搞…

JVM篇----第三篇

系列文章目录 文章目录 系列文章目录前言一、解释 Java 堆空间及 GC?二、JVM 内存区域三、程序计数器(线程私有)前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一…

「Qt Widget中文示例指南」如何实现一个日历?(三)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文中的CalendarWi…

API接口安全总结

接口分类 HTTP接口 RPC接口&#xff08;客户端和服务器端的连接 例如游戏登陆&#xff09;非web协议&#xff0c;PRC 远程过程调用 Remote Procedure Call&#xff0c;其就是一个节点请求另外一个节点提供的服务。当两个物理分离的子系统需要建立逻辑上的关联时&#xff0c;R…

Centos7 两种方式安装 MySQL5.7 步骤 yum 、本地 tar 文件

一、使用 yum 源方式安装 1、卸载系统自带 mariadb MariaDB Server 是最流行的开源 关系型数据库 之一。它由 MySQL 的原始开发者制作&#xff0c;并保证保持开源。 在 CentOS 7 中默认安装有 MariaDB 可忽略&#xff0c;安装完成之后可以直接覆盖掉 MariaDB。 查看并卸载系统…

扩散模型公式推导

这篇文章将尝试推导扩散模型 DDPM 中涉及公式&#xff0c;主要参考两个 B 站视频&#xff1a; 大白话AI狗中赤兔 本文所用 PPT 元素均来自 UP 主&#xff0c;狗中赤兔和大白兔AI&#xff0c;特此感谢。 在证明开始&#xff0c;我们需要先对扩散模型有一个整体的认知。扩散模型…

(M)unity2D敌人的创建、人物属性设置,遇敌掉血

敌人的创建 1.敌人添加与组件设置 1&#xff09;添加敌人后&#xff0c;刚体添加&#xff0c;碰撞体添加&#xff08;一个碰撞体使猪在地上走&#xff0c;不接触人&#xff0c;另一个碰撞体组件使人和猪碰在一起产生伤害&#xff09; ①刚体 ②碰撞体一 设置的只在脚下&a…

【寒假打卡】Day01

文章目录 选择编程HJ99 自守数OR86 返回小于 N 的质数个数 选择 如下代码输出的是什么&#xff08; &#xff09; char a101; int sum200; a27;suma; printf("%d\n",sum);A: 32 B: 99 C: 328 D: 72 答案&#xff1a; C 解析&#xff1a; 首先&#xff0c;char a …

VIM工程的编译 / VI的快捷键记录

文章目录 VIM工程的编译 / VI的快捷键记录概述笔记工程的编译工程的编译 - 命令行vim工程的编译 - GUI版vim备注VIM的帮助文件位置VIM官方教程vim 常用快捷键启动vi时, 指定要编辑哪个文件正常模式光标的移动退出不保存 退出保存只保存不退出另存到指定文件移动到行首移动到行尾…

Spring RabbitMQ那些事(3-消息可靠传输和订阅)

目录 一、序言二、生产者确保消息发送成功1、为什么需要Publisher Confirms2、哪些消息会被确认处理成功 三、消费者保证消息被处理四、Spring RabbitMQ支持代码示例1、 application.yml2、RabbigtMQ配置3、可靠生产者配置4、可靠消费者配置5、测试用例 一、序言 在有些业务场…

告别无法访问的Github

告别无法访问的Github 最近在使用github的时候又登不上去了&#xff0c;挂着VPN都没用 但是自己很多项目都存在github&#xff0c;登不上去那不得损失很大 所以一行必须整点儿特殊手段来访问&#xff0c;顺便分享一下 1.加速器 网上很多解决方案都是在分享各种加速器来登陆…

大型语言模型 (LLM)全解读

一、大型语言模型&#xff08;Large Language Model&#xff09;定义 大型语言模型 是一种深度学习算法&#xff0c;可以执行各种自然语言处理 (NLP) 任务。 大型语言模型底层使用多个转换器模型&#xff0c; 底层转换器是一组神经网络。 大型语言模型是使用海量数据集进行训练…

【Linux】Linux中的日志查询方法

文章目录 linux日志与日志的查询方法更多journalctl用法journalctl用法案例部分日志路径说明推荐阅读 linux日志与日志的查询方法 在Linux系统中&#xff0c;日志文件用于记录系统的各种运行信息和错误消息。常见的日志文件包括但不限于/var/log/下的各种日志&#xff0c;如me…

Armv8-M的TrustZone技术之SAU寄存器总结

每个SAU寄存器是32位宽。下表显示了SAU寄存器概要。 5.1 SAU_CTRL register SAU_CTRL寄存器的特征如下图和表所示&#xff1a; 5.2 SAU_TYPE register 5.3 SAU_RNR register 5.4 SAU_RBAR register 5.5 SAU_RLAR register 5.6 SAU区域配置 当SAU启用时&#xff0c;未由已启用…