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

这篇具有很好参考价值的文章主要介绍了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端口的客户端连接成功。
macOS跨进程通信: TCP Socket 创建实例,macos,tcp/ip,网络协议

二:主要函数

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)
macOS跨进程通信: TCP Socket 创建实例,macos,tcp/ip,网络协议

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;
@end

static 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 LISTEN
     tcp4       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); //监听端口号8766
    
    
    int 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 文件代码文章来源地址https://www.toymoban.com/news/detail-820486.html

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


int 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 _WIN32
        char buf[1024];
        strerror_s(buf, 1024, errno);
#else
        char *buf = strerror(errno);
#endif // _WIN32
        printf("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 // _WIN32

    printf("client ended successfully\n");
    exit(EXIT_SUCCESS);
}

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

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

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

相关文章

  • 【Linux】socket 编程(socket套接字介绍、字节序、socket地址、IP地址转换函数、套接字函数、TCP通信实现)

    橙色 所谓套接字,就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。 一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进程通

    2024年02月09日
    浏览(53)
  • C++网络通信实例(TCP/IP协议,包括服务端与客户端通信)

    创作不易 觉得有帮助请点赞关注收藏 TCP/IP是当下网络协议栈中的主流协议 TCP属于传输层的协议  可靠传输 包括经典的三次握手等等 IP协议是网络层协议 尽全力传输但不可靠 学过计算机网络的同学们对这个应该比较熟悉 以下是使用C++进行网络通信的实例  服务端 主要使用

    2024年02月14日
    浏览(47)
  • MacOS使用USB接口与IPhone进行Socket通信

    演示效果如下:   开源地址: GitHub - rsms/peertalk: iOS and Mac Cocoa library for communicating over USB   克隆源码: 克隆后打开peertalk然后启动xcode工程  先启动MacOS服务端工程,再启动iOS客户端工程    客户端 服务端          

    2024年02月17日
    浏览(44)
  • socket实现tcp通信

    tcp的详细细节后面讲解,先来用它的一些接口实现1个简单的通信。下面来看它的一套接口 功能:socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符;应用程序可以像读写文件一样用read/write在网络上收发数据; 函数原型: 参数说明: domain:协议域又称

    2024年02月01日
    浏览(49)
  • 网络通信(Socket/TCP/UDP)

    Socket(又叫套接字)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接协议,客户端的IP地址,客户端的端口,服务器的IP地址,服务器的端口。 一个Socket是一对IP地址和端口。 Socket可以看

    2024年01月22日
    浏览(54)
  • Python Socket TCP多线程通信【四】

    一.开启多线程通信 前面说到,因为单线程原因,客户端与服务器无法做到自由对话,则需要用到多线程来处理。我们现在的服务端和客户端最多也就是发送消息和接收消息两种行为,所以我们采用双线程。 或许我们可以新建一个Client.py的客户端和Server.py的服务端,代码照搬

    2024年02月04日
    浏览(44)
  • 1、Linux中的socket与TCP通信

    1、所谓 socket(套接字),就是 对网络中不同主机上的应用进程之间进行双向通信的端点的抽象 。 2、一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络

    2023年04月08日
    浏览(40)
  • 基于python socket实现TCP/UDP通信

    两个应用程序如果需要进行通讯最基本的一个前提就是能够唯一的标示一个进程,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。能够唯一标示网络中的进程后

    2024年02月16日
    浏览(49)
  • 【网络通信】socket编程——TCP套接字

    TCP依旧使用代码来熟悉对应的套接字,很多接口都是在udp中使用过的 所以就不会单独把他们拿出来作为标题了,只会把第一次出现的接口作为标题 通过TCP的套接字 ,来把数据交付给对方的应用层,完成双方进程的通信 在 tcpServer.hpp 中,创建一个命名空间 yzq 用于封装 在命名

    2024年02月13日
    浏览(46)
  • TCP/IP(十一)TCP的连接管理(八)socket网络编程

    一  socket网络编程  socket 基本操作函数 bind、listen、connect、accept、recv、send、select、close ①  针对 TCP 应该如何 Socket 编程? ②   listen 时候参数 backlog 的意义? ③  accept 发生在三次握手的哪一步? ④   客户端调用 close 了,连接是断开的流程是什么? ⑤  没有 accept,能建立 T

    2024年02月07日
    浏览(50)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包