peertalk Usbmux 资料收集与整理

news2024/11/28 8:49:30

Usbmux - The iPhone Wiki

Usbmux

During normal operations, iTunes communicates with the iPhone using something called “usbmux” – this is a system for multiplexing several “connections” over one USB pipe. Conceptually, it provides a TCP-like system – processes on the host machine open up connections to specific, numbered ports on the mobile device. (This resemblance is more than superficial – on the mobile device, usbmuxd actually makes TCP connections to localhost using the port number you give it.)

On the Mac, this is handled by /System/Library/PrivateFrameworks/MobileDevice.framework/Resources/usbmuxd, a daemon that is started by launchd (see /System/Library/LaunchDaemons/ com.apple.usbmuxd.plist). It creates a listening UNIX Domain Socket at /var/run/usbmuxd. usbmuxd then watches for iPhone connections via USB; when it detects an iPhone running in normal mode (as opposed to recovery mode), it will connect to it and then start relaying requests that it receives via /var/run/usbmuxd – this is to say, usbmuxd is the only thing that actually speaks USB to the iPhone. This means that third-party applications which wish to talk to the iPhone must either do so through usbmuxd, or usbmuxd must be replaced

对 usbmuxd 的一点研究 · Farlanki

USBMuxd

当 iTunes 和 iPhone 连接时,它们之间会通过 USBMux 进行连接。USBMux 用于在 USB 协议上实现多路 TCP 连接。USBMuxd 能够实现 USB-TCP 协议的转换,将 USB 的端口映射到本机的 TCP 端(基于 Unix Domain Socket),将 USB 通信抽象为 TCP 通信。苹果的 iTunes , XCode 都使用了这个服务。

根据 iphonewiki.com 所说的,USBMuxd 程序存放在 Mac 系统下的 /System/Library/PrivateFrameworks/MobileDevice.framework/Resources路径中。并且由 plist 文件 /System/Library/LaunchDaemons/com.apple.usbmuxd.plist 可知,USBMuxd 是一个开机启动的服务。

由于 iOS 系统也具有 USBMuxd 服务,所以 iPhone 和 Mac 就能通过 USB 进行 TCP 通信。更进一步的是,只要实现了 USBMuxd,非 OSX 系统也能与iPhone 系统进行通信,例如 Windows 和 Linux。

github 上也存在一个名为 USBMuxd 的项目,这是一个跨平台的开源项目,实现了 usbmux 的功能,支持 MAC/Linux/Windows 平台。

usbmuxd连接流程

以下是Mac应用程序、App与 USBMuxd 的连接流程和数据收发流程。

有几个地方是需要留意的:

  • 应用程序需要创建两个 Socket , 一个用于监听 USBMuxd 广播包,以便应用程序得知 iOS 设备的连接与断连;另一个 Socket 用于在得知 iOS 设备连接后请求连接,如果 iOS 设备同意连接,后续的数据传输将通过这个 Socket 进行。
  • App 也需要创建两个 Socket,一个为欢迎 Socket,另一个为连接 Socket,这和普通的服务器端套接字流程一样。对于App,USBMuxd 服务是透明的。
  • USBMuxd 的广播包使用自己的一套协议,需要按照该协议生成数据包来和 USBMuxd 进行数据传输。
  • 当应用程序与 iOS 设备连接后,应用程序和 iOS 进行数据传输的协议由我们自己决定。




GitHub - libimobiledevice/libusbmuxd: A client library to multiplex connections from and to iOS devicesA client library to multiplex connections from and to iOS devices - GitHub - libimobiledevice/libusbmuxd: A client library to multiplex connections from and to iOS devicesicon-default.png?t=N7T8https://github.com/libimobiledevice/libusbmuxd

。。。

GitHub - libimobiledevice-win32/libimobiledevice-vs: Visual Studio solution to build all of libimobiledevice at onceVisual Studio solution to build all of libimobiledevice at once - GitHub - libimobiledevice-win32/libimobiledevice-vs: Visual Studio solution to build all of libimobiledevice at onceicon-default.png?t=N7T8https://github.com/libimobiledevice-win32/libimobiledevice-vs

。。。

使用usbmuxd服务,通过USB连接与PC端、Mac端实现通信,Peertalk的使用 - 简书一、usbmuxd 介绍 usbmuxd 是苹果的一个服务,这个服务主要用于在USB协议上实现多路TCP连接,将USB通信抽象为TCP通信。苹果的iTunes、Xcode,都...icon-default.png?t=N7T8https://www.jianshu.com/p/eba133891ec6

usbmuxd 是苹果的一个服务,这个服务主要用于在USB协议上实现多路TCP连接,将USB通信抽象为TCP通信。苹果的iTunes、Xcode,都直接或间接地用到了这个服务。

iTunes使用 usbmux 与 iphone 通信, 它提供了一个USB - TCP的转换服务, 这个服务在Mac端是由/System/Library/PrivateFrameworks/MobileDevice.framework/Resources/usbmuxd提供的, 当然, 开机自动启动。

它创建了一个Unix Domain Socket 在 /var/run/usbmuxd. usbmuxd服务程序监控iPhone在USB口上的连接, 当它监控到iPhone以用户模式连接到USB, (相对的是recovery模式), usbmuxd服务程序就会连接到这个/var/run/usbmuxd的TCP端口, 并开始成为一个USB - TCP 请求转发器

那么,如果想编写个第三方程序与iphone进行通信,实现类似iTunes的功能, 你的程序可以通过usbmuxd! 建立一个TCP连接到/var/run/usbmuxd端口, 根据协议发送对应的请求包, usbmuxd服务会将请求转发到USB的iPhone上。

peertalk,一个基于usbmuxd服务的开源代码,可以实现 iPhone 与 Mac 通信。

libimobiledevice,在可以PC端提供usbmuxd服务,实现 iPhone 与 windows 通信。

二、Peertalk 的使用:iPhone 与 Mac 通信

iOS 端

1、创建 channel,监听指定端口

    // 创建 channel
    PTChannel *channel = [PTChannel channelWithDelegate:self];
    // 监听指定端口,PTExampleProtocolIPv4PortNumber自定义端口号
    [channel listenOnPort:PTExampleProtocolIPv4PortNumber IPv4Address:INADDR_LOOPBACK callback:^(NSError *error) {
        if (error) { // 创建监听失败
        
        } else { // 创建监听成功 
        
        }
    }];

2、实现 Channel 的代理方法

@protocol PTChannelDelegate <NSObject>

@required
// 收到信息
- (void)ioFrameChannel:(PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(PTData*)payload;

@optional
// 收到消息调用,如回复NO,则忽略这条消息
- (BOOL)ioFrameChannel:(PTChannel*)channel shouldAcceptFrameOfType:(uint32_t)type tag:(uint32_t)tag payloadSize:(uint32_t)payloadSize;

// 出错回调
- (void)ioFrameChannel:(PTChannel*)channel didEndWithError:(NSError*)error;

// 连接成功回调
- (void)ioFrameChannel:(PTChannel*)channel didAcceptConnection:(PTChannel*)otherChannel fromAddress:(PTAddress*)address;

@end

3、连接成功后,会发送设备信息

Mac 端

1、监听USB设备的连接/断开

// 开始监听设备的连接与断开
- (void)startListeningForDevices {
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    
    // 监听设备连接
    [nc addObserverForName:PTUSBDeviceDidAttachNotification object:PTUSBHub.sharedHub queue:nil usingBlock:^(NSNotification *note) {
        NSNumber *deviceID = [note.userInfo objectForKey:@"DeviceID"];
        dispatch_async(notConnectedQueue_, ^{
            if (!connectingToDeviceID_ || ![deviceID isEqualToNumber:connectingToDeviceID_]) {
                // 断开现有连接
                [self disconnectFromCurrentChannel];
                connectingToDeviceID_ = deviceID;
                connectedDeviceProperties_ = [note.userInfo objectForKey:@"Properties"];
                // 重新连接
                [self enqueueConnectToUSBDevice];
            }
        });
    }];
    
    // 监听设备断开
    [nc addObserverForName:PTUSBDeviceDidDetachNotification object:PTUSBHub.sharedHub queue:nil usingBlock:^(NSNotification *note) {
        NSNumber *deviceID = [note.userInfo objectForKey:@"DeviceID"];
        if ([connectingToDeviceID_ isEqualToNumber:deviceID]) {
            connectedDeviceProperties_ = nil;
            connectingToDeviceID_ = nil;
            if (connectedChannel_) { // 关闭连接通道
                [connectedChannel_ close];
            }
        }
    }];
}

2、连接设备

// 连接设备
- (void)connectToLocalIPv4Port {
    // 创建通道,设置代理
    PTChannel *channel = [PTChannel channelWithDelegate:self];
    channel.userInfo = [NSString stringWithFormat:@"127.0.0.1:%d", PTExampleProtocolIPv4PortNumber];
    // 连接指定端口地址,与iOS端设置保持一致
    [channel connectToPort:PTExampleProtocolIPv4PortNumber IPv4Address:INADDR_LOOPBACK callback:^(NSError *error, PTAddress *address) {
        if (error) { // 连接失败
            
        } else { // 连接成功
            
        }
    }];
}

3、连接成功会,会收发送 ping、pong 心跳数据

三、libimobiledevice、Peertalk 的使用:iPhone 与 windows 通信

1、实现原理

windows端 通过 libimobiledevice 运行 usbmuxd 的多路复用守护进程,该进程的作用是建立本地端口和远程端口的转发,实现usb到tcp的转换服务

2、安装服务

windows端首先要安装苹果公司提供的相关服务,才能实现通信功能。服务名称为:AppleApplicationSupport和AppleMobileDeviceSupport

3、规定协议

首先指定 ip地址和端口,端口号建议大些,以免与苹果系统应用端口重复。如:127.0.0.1:62345。PC端可以通过端口转发实现。
定义相同结构体数据,以便数据的加密、解析。PC端可和Peertalk定义的协议保持一致。如:

// 数据头结构体
typedef struct _PTFrame {
  uint32_t version;     // 对应版本
  uint32_t type;        // 数据类型
  uint32_t tag;         // tag标记
  uint32_t payloadSize; // 数据大小
} PTFrame;

// 数据类型
enum {
  PTExampleFrameTypeDeviceInfo = 100,  // 设备信息
  PTExampleFrameTypeTextMessage = 101, // 文本数据
  PTExampleFrameTypePing = 102,        // Ping
  PTExampleFrameTypePong = 103,        // Pong
};
4、联调测试

定好协议后,指定相同ip地址和端口,进行测试。如连接失败,可尝试更换端口号重试。先调试连接,然后再收发数据,最后进行数据处理。

peertalk

目录

Usbmux

USBMuxd

usbmuxd连接流程

二、Peertalk 的使用:iPhone 与 Mac 通信

iOS 端

Mac 端

三、libimobiledevice、Peertalk 的使用:iPhone 与 windows 通信

1、实现原理

2、安装服务

3、规定协议

4、联调测试


https://blog.csdn.net/yxys01/article/details/77718506

https://www.zhaoxiaodan.com/ios/ituns%E4%B8%8Eiphone%E7%9A%84%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AEusbmuxd%E8%A7%A3%E6%9E%90.html如何获取iphone已安装的app?如何获取iphone文件列表?iTunes使用一种叫icon-default.png?t=N7T8https://www.zhaoxiaodan.com/ios/ituns%E4%B8%8Eiphone%E7%9A%84%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AEusbmuxd%E8%A7%A3%E6%9E%90.html

ituns与iphone的通信协议usbmuxd解析

最开始研究与iphone通信, 都会想当然的google下usb协议, 必经iphone是通过usb线连接到电脑. 其实不然, iTunes是通过TCP协议与iPhone通信的

##usbmuxd

iTunes使用一种叫”usbmux”的东西与iphone通信, 这个东西提供了一个USB - TCP的转换服务.
这个服务在Mac端是由/System/Library/PrivateFrameworks/MobileDevice.framework/Resources/usbmuxd 提供的, 当然, 开机自动启动.
它创建了一个Unix Domain Socket 在 /var/run/usbmuxd. usbmuxd服务程序监控iPhone在USB口上的连接, 当它监控到iPhone以用户模式连接到USB, (相对的是recovery模式), usbmuxd服务程序就会连接到这个/var/run/usbmuxd的TCP端口, 并开始成为一个USB - TCP 请求转发器

那么,如果想编写个第三方程序与iphone进行通信,实现类似iTunes的功能, 你的程序可以通过usbmuxd! 建立一个TCP连接到/var/run/usbmuxd端口, 根据协议发送对应的请求包, usbmuxd服务会将请求转发到USB的iPhone上

##lockdownd协议

//协议头
struct usbmux_header {
	u32 length;	// 消息长度,包括头部
	u32 version;	// 协议版本号
	u32 type;       // 消息类型,请求,响应,握手,等
	u32 tag;	// 消息编号, 用来对应响应
	char payload;  //请求体
};

//头部中的type类型
enum {
	usbmux_result  = 1,
	usbmux_connect = 2,
	usbmux_hello   = 3,
	usbmux_payload = 8,
};

##监听

知道了iTunes使用的协议, 那么有没有办法看看iTunes都发了些什么包? 有个简单的办法就是使用socat, 类似:

sudo mv /var/run/usbmuxd /var/run/usbmuxx
sudo socat -t100 -x -v UNIX-LISTEN:/var/run/usbmuxd,mode=777,reuseaddr,fork UNIX-CONNECT:/var/run/usbmuxx

##包示例:

。。。

第一个iTunes向/var/run/usbmuxd发的请求包, 第一行 e3 01 00 00 01 00 00 00 08 00 00 00 02 00 00 00是头部 e3 01 00 00 即0x01e3 = 483, 包长度

头部后面紧跟的是payload, 也就是xml格式的请求内容

阿里最新、最强开源iOS自动化测试神器 - 知乎

前言

一直以来,iOS自动化的实现&执行都依赖 Mac 系统,其主要原因是因为需要通过 xcodebuild 编译&安装 WDA (WebDriverAgent) 到 iOS 设备中,通过WDA实现对被测应用进行操作。

导致想要做iOS自动化 就必须拥有 Mac 设备的现象

  • 常用电脑非 Mac 的同学 想要做 iOS 自动化的时候, 就需要再申请一台Mac设备 ,可能会出现资源利用不充分的情况
  • 云测试平台要搭建 IOS 自动化 服务环境时,也只能批量申请 Mac 设备,经费在燃烧

开源神器: tidevice

一个月前,阿里团队开源了一个内部使用的 iOS自动化工具 :tidevice (

https://github.com/alibaba/taobao-iphone-device

) ,让我们可以更方便、简单的脱离Mac的限制。

tidevice 能做什么

  • 设备信息获取
  • 应用安装、卸载、启动、停止、查看应用信息、已安装应用列表
  • 启动 WebDriverAgent (不依赖 xcodebuild , 跨平台)
  • 运行 XcTest UITest
  • 性能数据采集
  • 设备截图、设备日志 ...

tidevice 核心原理

usbmux通信协议:

实现 Mac/Windows/Linux 与 iOS设备服务间的通信

  • Mac
    • usbmuxd 是苹果的一个服务,这个服务主要用于在USB协议上实现多路TCP连接,将USB通信抽象为TCP通信。苹果的iTunes、Xcode,都直接或间接地用到了这个服务。
  • Linux / Windows
    • 本身是没有 usbmux的,不过都有开源项目的实现,可以直接使用/参考
  • Windows 另外依赖 AppleApplicationSupport和AppleMobileDeviceSupport 两个服务,安装Itunes 环境即可安装对应服务

usbmux 本身是socket 套接字,通过截获、破解等手段,结合开源界的成果,用python 进行模拟,从而实现了当前工具已有的所有功能

usbmux协议分析 - Lazy Eval

usbmux协议分析

  

  • usbmuxd
  •  Aug 20, 2019

usbmux协议介绍

初闻usbmux协议时,可能会让人感觉比较陌生,但其实如果你是MAC用户的话,你可能每天都在和它打交道,只是不知道而已。当通过USB将iPhone连接到MAC时,usbmux协议已经在背后为你默默工作了。

usbmux是苹果的私有协议,苹果设计该协议的原因是为了自家的macOS APP能够和iDevice进行通信,从而实现诸如iTunes备份iPhone、Xcode真机调试等功能。只是后来该协议被开发者破解了,于是为众人所知。

usbmuxd是对usbmux协议在macOS平台的上实现,也是macOS系统上的一个守护进程(Daemon),它随着系统的启动而启动。在macOS上,与usbmuxd相关的文件主要有3个:

  1. /System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/Resources/usbmuxd
  2. /System/Library/LaunchDaemons/com.apple.usbmuxd.plist
  3. /var/run/usbmuxd

其中/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/Resources/usbmuxd为可执行文件。

/System/Library/LaunchDaemons/com.apple.usbmuxd.plist为usbmuxd服务的开机启动配置文件,如图所示。launchd在系统启动时会根据该文件的配置信息,启动usbmuxd服务,创建对应的socket(也即/var/run/usbmuxd)。

image-20190820203643037

/var/run/usbmuxd在开机启动时创建,该文件其实是一个Unix Domain Socket,用于usbmuxd进程和其他进程——比如Xcode、iTunes等——之间的进程间通信(IPC)。

➜  zyqhi.github.io git:(master) ✗ file /var/run/usbmuxd
/var/run/usbmuxd: socket

基于usbmux协议的设备间通信

前文也提到,苹果设计usbmux协议是为了解决macOS APP和iOS设备之间的通信问题。如果只是普通的协议的话,倒也没什么可说的,只是该协议呢,提供了一种类似TCP socket的API,使得macOS和iOS设备之间的通信,如同是网络上的两个主机之间的通信。

During normal operations, iTunes communicates with the iPhone using something called “usbmux” – this is a system for multiplexing several “connections” over one USB pipe. Conceptually, it provides a TCP-like system – processes on the host machine open up connections to specific, numbered ports on the mobile device. (This resemblance is more than superficial – on the mobile device, usbmuxd actually makes TCP connections to localhost using the port number you give it.)

FROM: https://www.theiphonewiki.com/wiki/Usbmux

说完usbmux协议的用途,我们再来看看usbmux协议是怎样工作的。这里以iTunes和iOS通信为例,来看下整个通信架构,如图所示:

image-20190902163407986

从图中可以看出,整个通信架构和经典的C/S架构非常类似。其中iOS端的服务(lockdown)的角色是服务端,macOS端的程序(iTunes)则为客户端。

因为没有源码,所以此处的通信架构图只是根据网上前人的工作,结合个人的测试,推测得出的。如有错误,还请指正。

lockdown服务

先来看下iOS端:

图中的lockdown是iOS端的系统服务,它在iOS系统启动时,由launchd进程启动。lockdown服务对应的启动配置文件在iOS系统上的完整路径为:/System/Library/LaunchDaemons/com.apple.mobile.lockdown.plist

image-20190821135906471

lockdown作为一个网关,用于协调macOS进程和iOS其他服务之间的通信。像我们平时用到的iTunes备份iPhone,Xcode真机调试等功能,在iOS侧,都是需要lockdown服务进行中转处理的。举个例子,Xcode若要执行真机调试,首先需要和lockdown服务通信,发出启动调试请求,lockdown收到请求以后,启动iOS端对应的调试服务(debugserver),然后Xcode便与debugserver之间建立了通信连接。

lockdown服务启动以后,会创建一个TCP listen socket,端口号为62078,地址为本机地址:127.0.0.1。这一点我们可以在越狱机器上得到验证:

image-20190821141233973

如果从TCP/IP网络编程的角度来看lockdown的话,会发现它和普通的TCP Server没有差别。

关于lockdown服务的更多内容,可以参考这篇文章:Understanding usbmux and the iOS lockdown service

usbmuxd服务

再来看macOS端:

前文讲过,usbmuxd是macOS的系统服务,该服务为macOS端的通信代理,当其他应用——比如iTunes——需要与iOS设备通信时,只需要与usbmuxd服务通信即可,usbmuxd负责将iTunes的通信请求转发到iOS端。usbmuxd服务与iOS设备通信基于USB通信协议。

iTunes与usbmuxd服务之间通过Unix Domain Socket进行通信,Unix Domain Socket的API和TCP Socket非常类似。来看下与usbmuxd协议之间建立连接的代码:

int connect_to_usbmuxd() {
    // Create Unix domain socket
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    // prevent SIGPIPE
    int on = 1;
    setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));

    // Connect socket
    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    // 这个路径就相当于ip地址,为了理解这个过程,可以和TCP进行对比
    strcpy(addr.sun_path, "/var/run/usbmuxd");
    socklen_t socklen = sizeof(addr);

    if (connect(fd, (struct sockaddr *)&addr, socklen) == -1) {
        printf("Connect failure, fd is: %d.\n", fd);
    } else {
        printf("Connect successifully, fd is: %d.\n", fd);
    }

    return fd;
}

和TCP的差别有以下几点:

  1. domian参数为AF_UNIX,而非熟悉的AF_NET
  2. 通信地址不是IP地址,而是路径/var/run/usbmuxd
  3. 不需要指定端口。

除此之外,整个API的使用方式则几乎一样。iTunes在建立和usbmuxd之间的连接时,应该使用了类似的代码。

从一端到另一端

前文中提到,对于macOS端的进程而言,如果要与iOS上的进程通信,只需要和usbmuxd服务通信即可。而在上一节我们介绍了进程和usbmuxd服务建立连接的方式,那么假设iTunes通过上述方式建立了与usbmuxd之间的连接,那么接下来iTunes是不是就可以直接与位于iOS端的lockdown服务通信了呢?

答案是NO。

在回答为什么是NO之前,先来回忆一下基于socket的TCP通信过程。我们知道在TCP/IP通信协议体系里,IP抽象了网络上两台主机之间的通信,而TCP抽象了两台主机上的进程之间的通信。IP地址标识了网络中的一台主机,而端口号则标识了一台主机上的特定进程。

而在上一节,在建立进程(iTunes)与代理(usbmuxd)之间的连接时,并未指定端口号。未指定端口号,那么usbmuxd服务便不知道进程要和iOS端的哪个进程进行通信。

比如iTunes要和lockdown服务建立连接,lockdown服务的端口号是62078,而iTunes在建立连接时并未指定该端口号,因此对于usbmuxd而言,根本不知道iTunes要和lockdown服务通信。建立连接所需的信息是不完备的。

其实,如果用上一节中的提到的建立连接,只是建立了macOS进程与代理之间的通信连接,该连接建立以后,进程能够和代理进行通信,但若要和iOS上的目标进程通信的话,我们还需要对代理进行配置,让代理和iOS端的进程也建立连接,整个通信链路才算打通。

下面我们便来看下如何『配置』代理,从而打通整个通信链路。

Listen

在讲具体的连接过程之前,我们先来想下,从通信的角度讲,如果要usbmuxd服务和iOS端进程之间建立连接,必须要满足哪些条件:

  1. 一台Mac可以连接多台iOS设备,连接时需要知道和哪一台iOS设备建立连接,因此需要device id来唯一标识一台设备;
  2. 建立连接时必须要知道当前Mac是否连接到iOS设备,也即需要监听iOS设备的插拔;
  3. usbmuxd不会主动去连接iOS端上的进程,必须macOS进程驱使其完成,也即对其进行配置,此时macOS进程和usbmuxd之间需要一套通信协议;
  4. 必须指定需要连接到的目标进程的端口号,这样才能唯一标识一个进程。

先看问题3,苹果定义了一套配置协议,该协议的数据传输格式为plist,并被破解,协议的具体细节会在下文中阐释数据收发过程中讲到。而对于4,后文中会给出解答,现在先忽略。

而对于问题1和2,usbmuxd已经具备该功能,但是如果要『看到』该功能,我们需要向usbmuxd发送命令,这便是本节中要讲的Listen命令。

所谓发送Listen命令,便是macOS进程调用上一节中提到的connect_to_usbmuxd函数,创建的socket,然后向对应的socket文件描述符中写入一段数据(控制命令),然后读一段数据(usbmuxd的返回值)。发送逻辑的代码如下:

void send_listen_packet() {
    listen_channel = connect_to_usbmuxd_channel(); // 该函数调用 connect_to_usbmuxd 创建socket,并根据socket文件描述符创建一个dispatch I/O对象
    
    // 1. Listen指令对应的键值对
    NSDictionary *packet = @{
                             @"ClientVersionString": @"1",
                             @"MessageType": @"Listen",
                             @"ProgName": @"Peertalk Example"
                             };
    NSLog(@"send listen packet: %@", packet);
    send_packet(packet, 0, listen_channel);
}

void send_packet(NSDictionary *packetDict, int tag, dispatch_io_t channel) {
    NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:packetDict format:NSPropertyListXMLFormat_v1_0 options:0 error:NULL];
    
    int protocol = USBMuxPacketProtocolPlist;
    int type = USBMuxPacketTypePlistPayload;
    
    // 2. 封装成usbmux协议要求的格式
    usbmux_packet_t *upacket = usbmux_packet_create(
                                                    protocol,
                                                    type,
                                                    tag,
                                                    plistData ? plistData.bytes : nil,
                                                    (uint32_t)(plistData.length)
                                                    );
    
    dispatch_data_t data = dispatch_data_create((const void*)upacket, upacket->size, usbmuxd_io_queue, ^{
        usbmux_packet_free(upacket);
    });
    
    dispatch_io_write(channel, 0, data, usbmuxd_io_queue, ^(bool done, dispatch_data_t data, int _errno) {
        NSLog(@"dispatch_io_write: done=%d data=%p error=%d", done, data, _errno);
        if (!done) { return; }
    });
}

此处的listen_channel是一个dispatch I/O对象,所谓的dispatch I/O对象,就是一个封装了文件描述符,以及作用于该文件描述符上的异步I/O策略。本文中可以将其和文件描述符作为等价的概念来理解。

这部分代码核心就2处,代码中已标出:

  1. 以plist格式封装Listen指令;
  2. 是将封装后的Listen报文发送到usbmuxd。

对应的报文格式如下图所示:

image-20190821164908991

Listen命令发送成功以后,每当有iOS设备插拔,便会收到usbmuxd服务返回的数据。读取逻辑如下:

void read_packet_on_channle(dispatch_io_t channel) {
    // 1. Read the header
    usbmux_packet_t ref_upacket;
    dispatch_io_read(channel, 0, sizeof(ref_upacket.size), usbmuxd_io_queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
        
        if (!done) { return; }
        
        // Read size of incoming usbmux_packet_t
        uint32_t upacket_len = 0;
        char *buffer = NULL;
        size_t buffer_size = 0;
        // data 是读取到的数据,这一步获取到读取到的data的长度,并将buffer指向对应的缓冲区
        dispatch_data_t map_data = dispatch_data_create_map(data, (const void **)&buffer, &buffer_size);
        memcpy((void *)&(upacket_len), (const void *)buffer, buffer_size);
        
        // Allocate a new usbmux_packet_t for the expected size
        uint32_t payloadLength = upacket_len - (uint32_t)sizeof(usbmux_packet_t);
        usbmux_packet_t *upacket = usbmux_packet_alloc(payloadLength);
        
        // 2. Read rest of the incoming usbmux_packet_t
        off_t offset = sizeof(ref_upacket.size);
        dispatch_io_read(channel, offset, upacket->size - offset, usbmuxd_io_queue, ^(bool done, dispatch_data_t data, int error) {
            NSLog(@"dispatch_io_read %lld,%lld: done=%d data=%p error=%d", offset, upacket->size - offset, done, data, error);
            
            if (!done) { return; }
            
            // Copy read bytes onto our usbmux_packet_t
            char *buffer = NULL;
            size_t buffer_size = 0;
            dispatch_data_t map_data = dispatch_data_create_map(data, (const void **)&buffer, &buffer_size);
            memcpy(((void *)(upacket))+offset, (const void *)buffer, buffer_size);
            NSLog(@"package protocol is: %u, type is: %u", upacket->protocol, upacket->type);
            
            // 3. Try to decode any payload as plist
            NSError *err = nil;
            NSDictionary *dict = nil;
            if (usbmux_packet_payload_size(upacket)) {
                dict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:usbmux_packet_payload(upacket) length:usbmux_packet_payload_size(upacket) freeWhenDone:NO] options:NSPropertyListImmutable format:NULL error:&err];
            }
            
            // 4. Read next
            read_packet_on_channle(channel);
            
            usbmux_packet_free(upacket);
        });
    });
}

读逻辑中我删除了校验和容错逻辑,只保留了比较关键的部分,Listen命令的读操作说明如下:

  1. 先读取报文头,usbmuxd返回的报文格式也遵循usbmux协议,头部中记录了报文载荷的长度,所以必须先读出报文头之后,才能知道载荷的边界到哪里;
  2. 从头部解析出载荷长度之后,读取报文载荷数据;
  3. 头部和载荷读取完成之后,构成一个完整的报文,此时以plist格式解析载荷,获取返回内容;
  4. 读取下一个报文,读操作采用的I/O模式是阻塞模式,一直监听是否有数据,当有数据时便进行读取解析。

image-20190821171830735

如图所示,是当iOS设备通过USB连接到(Attached)Mac时,usbmuxd服务返回的数据。其中比较关键的字段有两个:

  1. MessageType: Attached表明有设备连接的Mac,而Detached则表明断开连接;
  2. DeviceID: 唯一标识当前插入的设备,后续与设备建立连接时会用到。

iTunes监听设备插拔时,便是通过类似的方式。

Connect

说完Listen,再来看下Connect。在上一节中,我们抛出了usbmuxd服务和iOS端进程之间建立连接的4点要求,其中前3个在上一节都已经得到解决,而第4个问题——端口号——其实在lockdown的启动配置文件时就已经知道了。此时我们已经具备建立连接的所有条件:

  1. Listen时得到DeviceID;
  2. lockdown启动配置文件中得知lockdown的端口号。

在讲Listen时,我们知道可以通过向usbmuxd服务发送Listen命令监听iOS设备插拔,类似地,也可以通过向usbmuxd服务发送Connect命令对其配置,使其建立和iOS端应用程序的连接。

整个过程如下,我们需要再调用connect_to_usbmuxd函数再创建一个socket,用作Connect命令,和Listen命令的发送方式一样,发送Connect命令也是向socket文件描述符中写入数据:

void send_connect_usb_packet() {
    print_empty_lines();
    
    connect_channel = connect_to_usbmuxd_channel();
    

    port = ((port<<8) & 0xFF00) | (port>>8);
    NSDictionary *packet = @{
                             @"ClientVersionString" : @"1",
                             @"DeviceID" : deviceID,
                             @"MessageType" : @"Connect",
                             @"PortNumber" : [NSNumber numberWithInt:port],
                             @"ProgName" : @"Peertalk Example"
                             };
    
    NSLog(@"send connect to usb packet: %@", packet);
    send_packet(packet, 1, connect_channel);
    read_packet_on_channle(connect_channel);
}

其中关键的字段有:

  1. DeviceID: 设备id,在Linten时获得;
  2. MessageType: 消息类型,Connect表示需要建立连接;
  3. PortNumber: 端口号,对应iOS端目标连接服务的端口,以lockdown为例,为62078。

对应的报文格式为:

image-20190821193559069

注:实际发送过程中此处的PortNumber会以网络字节序的形式传输。

发送Connect命令之后,如果连接成功,此时便会收到usbmuxd返回的数据(读取Connect返回的数据和之前的读取Listen命令返回的数据方式相同):

image-20190821193827916

Number为0表示连接成功。

为了容易理解,我们来和标准的TCP通信做个对比,其中右侧的lockdown,就是标准的TCP Server,而左侧的iTunes,建立与lockdown的连接时不是像标准的TCP Client一样,调用connect函数,而是向usbmuxd服务发送Connect命令。然而,虽然方法不同,但是二者在通信意义上是等价的。

image-20190902163830106

并且,当在Connect命令发送后,在iOS侧,lockdown的accept方法会执行,随后双方便可以像标准的TCP通信一样发送和接收数据。

iTunes发送数据的方式如下:

void send_msg(NSString *msg) {
    dispatch_data_t payload = create_payload(msg);
    dispatch_data_t frame = create_frame(101, 0, payload);

    // send through connect channel, not tcp_channel
    dispatch_io_write(connect_channel, 0, frame, usbmuxd_io_queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
        NSLog(@"error is: %d", error);
    });
}

其中connect_channel是我们在发送Connect命令时创建的文件描述符。

注意Listen和Connect使用不同的文件描述符。

经历以上过程,macOS程序便实现了通过usbmuxd和iOS通信。总结下来:

  1. macOS端进程通过Unix Domain Socket以类似TCP的方式和usbmuxd服务进行通信;
  2. macOS端进程通过向usbmuxd服务发送Connect命令,建立与iOS端目标进程之间的通信连接,端到端的通信基于端口号;
  3. iOS端进程以标准的TCP Server形式存在;
  4. usbmuxd作为中间代理,封装了USB协议的通信细节,对上提供类TCP的接口;
  5. 基于usbmux协议和usbmuxd服务,实现了macOS进程和iOS端进程之间简单、高效的通信。

简单在于协议的API基于socket,为大众熟知,而在通信效率方面,基于USB协议的通信效率显然比基于TCP/IP网络要快得多。

usbmux应用

如果是单纯分析macOS进程和iOS进程之间的通信原理的话,那大可不必如此大费周章,usbmuxd的魔力不仅在于它可以用于苹果自带应用和iOS程序之间的通信,而且,我是说而且,它可以用于为我们所用,基于usbmuxd服务,我们可以利用usbmux协议构建我们自己的应用。

我们可以基于usbmuxd服务,利用usbmux协议构建我们自己的应用。

重新审视lockdown

我们回过头来,重新看下lockdown做了哪些事情,从前文中lockdown的启动配置文件可以看出,它在开机时创建了一个listen socket,监听本地端口:62078,然后像普通的TCP Server那样,bind、accept阻塞等待客户端的连接。而当macOS端向usbmuxd发送Connect命令时,lockdown的accept结束阻塞,连接建立。

lookdown的源码没有,但是从启动配置文件和nc端口测试,结合网络知识,我们基本可以确认lockdown的内在逻辑。

那么,如果我们模拟lockdown,新建一个iOS APP,指定端口号,比如2345,然后通过调用socket、bind、listen、accept建立我们自己的TCP Server监听127.0.0.1:2345。

同时新建一个macOS应用,像iTunes一样连接usbmuxd服务,以端口号2345向其发送Connect命令,那么会发生什么呢?

答案是,如我们所愿,利用usbmuxd协议,我们建立了自己的macOS应用和iOS APP之间的通信连接,随后便可在此协议的基础之上,实现自建iOS APP和macOS应用程序之间的高效通信。整个通信框架如下:

image-20190902163853436

前文中花了大量的篇幅来阐述usbmux协议的原理,最终的目的就是这么简单,我们想在自己的产品中应用usbmux协议。

usbmux应用举例

usbmux协议由于通信效率较高,在数据量较大的通信场景中,体验非常突出,毕竟当传输的数据量较大时,效率就是体验。目前基于usbmux有非常多的有意思的应用,常见的有:

  • PeerTalk: PeerTalk基于usbmux实现了一个简单的IM,可用于macOS端和和iOS端之间聊天。本文的代码就是参照PeerTalk,如果你想基于usbmux协议构建自己的应用,而又不想深入了解usbmux协议的的话,可以直接搬运PeerTalk的代码。
  • lookin: 腾讯开发的iOS界面调试利器,简直比Reveal还好用,而且免费。

参考

  • usbmux_demo: 本文中的示例代码。
  • libimobiledevice: 跨平台的协议库,用于iOS设备通信
  • usbmux
  • launchd
  • Understanding usbmux and the iOS lockdown service
  • Unix domain socket 简介
  • Dispath I/O

peertalk 实验

macos example  & ios example

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

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

相关文章

PHP基础 - 输入输出

在 PHP 中,有多种方法可以用来输出内容。下面是其中的几种: 1、echo: 这是最常见的输出语句之一,可以输出一个或多个字符串。它是一个语言结构,可以省略括号。使用示例如下: <?php // 使用 echo 语句输出一个字符串 echo "Hello, world!\n";// 可以使用…

中伟视界:皮带跑偏、异物检测AI算法除了矿山行业应用,还能在钢铁、火电、港口等行业中使用吗?

随着工业化的发展&#xff0c;皮带输送机已经成为各行业中不可或缺的重要设备&#xff0c;但是在使用过程中&#xff0c;由于各种原因&#xff0c;皮带常常出现跑偏问题&#xff0c;给生产运营带来了诸多困扰。不仅仅是矿山行业&#xff0c;钢铁、火电、港口等行业也都面临着皮…

C++异常剖析

什么是异常&#xff1f; 在程序运行的过程中&#xff0c;我们不可能保证我们的程序百分百不出现异常和错误&#xff0c;那么出现异常时该怎么报错&#xff0c;让我们知道是哪个地方错误了呢? C中就提供了异常处理的机制。 一、异常处理的关键字 &#xff08;1&#…

【Vue】vue整合element

上一篇&#xff1a; vue项目的创建 https://blog.csdn.net/m0_67930426/article/details/134816155 目录 整合过程 使用&#xff1a; 整合过程 项目创建完之后&#xff0c;使用编译器打开项目 在控制器里输入如下命令 npm install element-ui 如图表示安装完毕 然后在…

欧拉回路欧拉路【详解】

1.引入 2.概念 3.解决方法 4.例题 5.回顾 1.引入 经典的七桥问题 哥尼斯堡是位于普累格河上的一座城市&#xff0c;它包含两个岛屿及连接它们的七座桥&#xff0c;如下图所示。 可否走过这样的七座桥&#xff0c;而且每桥只走过一次&#xff1f; 你怎样证明&#xff1f;…

高性能队列框架-Disruptor使用、Netty结合Disruptor大幅提高数据处理性能

高性能队列框架-Disruptor 首先介绍一下 Disruptor 框架&#xff0c;Disruptor是一个通用解决方案&#xff0c;用于解决并发编程中的难题&#xff08;低延迟与高吞吐量&#xff09;&#xff0c;Disruptor 在高并发场景下性能表现很好&#xff0c;如果有这方面需要&#xff0c;…

wps word中图片 一保存失真变糊

在wps中依次点击 文件-文字偏好设置-常规与保存 勾选不压缩文件中的图像 并 将默认目标输出设置为220ppi 即可

FacetWP WordPress网站高级筛选过滤插件(含所有扩展)

点击阅读FacetWP WordPress网站高级筛选过滤插件原文 FacetWP WordPress网站高级筛选过滤插件向电子商务网站、资源库、搜索页面等添加分面搜索。FacetWP 的过滤元素&#xff08;称为 facets&#xff09;动态调整以适应用户输入。这有助于防止出现“未找到结果”&#xff0c;从…

剪切板管理 Paste中文 for Mac

Paste是一个方便的剪贴板管理工具&#xff0c;它可以帮助你更好地组织、查找和管理剪贴板中的内容。它提供了历史记录、搜索、组织、格式处理和云同步等功能&#xff0c;使你能够更高效地使用剪贴板&#xff0c;并节省时间和精力。无论是在个人使用还是团队协作中&#xff0c;P…

Gitlab+GitlabRunner搭建CICD自动化流水线将应用部署上Kubernetes

文章目录 安装Gitlab服务器准备安装版本安装依赖和暴露端口安装Gitlab修改Gitlab配置文件访问Gitlab 安装Gitlab Runner服务器准备安装版本安装依赖安装Gitlab Runner安装打包工具安装docker安装java17安装maven 注册Gitlab Runner 搭建自动化部署准备SpringBoot项目添加一个Co…

华媒舍:引擎霸屏推广,10个技巧帮助你登上霸者!

下面我们就向您介绍引擎霸屏推广&#xff0c;及其10个技巧&#xff0c;这种技巧将帮助你在市场上获得不菲的成绩。 引擎霸屏推广引擎霸屏推广是一种营销策略&#xff0c;希望通过规模性推广产品&#xff0c;帮助品牌在顾客脑中占主导地位。这是一种依靠检索引擎等途径&#xf…

总线(什么是南北桥?您都用过哪些总线?)

什么是总线&#xff1f; 计算机系统中的总线&#xff08;Bus&#xff09;是指计算机设备和设备之间传输信息的公共数据通道&#xff0c;是连接计算机硬件系统内多种设备的通信线路&#xff0c;它的一个重要特征是由总线上的所有设备共享&#xff0c;因此可以将计算机系统内的多…

嵌入版python作为便携计算器(安装及配置ipython)

今天用别的电脑调试C&#xff0c;需要计算反三角函数时发现没有趁手工具&#xff0c;忽然想用python作为便携计算器放在U盘&#xff0c;遂想到嵌入版python 懒得自己配可以直接下载&#xff0c;使用方法见第4节 1&#xff0c;下载embeddable python&#xff08;嵌入版python&…

[mysql]linux安装mysql5.7

之前安装的时候遇到了很多问题&#xff0c;浪费了一些时间。整理出这份教程&#xff0c;照着做基本一遍过。 这是安装包: 链接&#xff1a;https://pan.baidu.com/s/1gBuQBjA4R5qRYZKPKN3uXw?pwd1nuz 1.下载安装包&#xff0c;上传到linux。我这里就放到downloads目录下面…

循环单向链表与约瑟夫问题

循环链表介绍 先不急着看约瑟夫问题是什么&#xff0c;先了解循环链表的结构&#xff0c;那什么是循环链表&#xff1f; 循环&#xff0c;顾名思义&#xff0c;从链表中第一个节点出发&#xff0c;还会遇到第一个节点&#xff0c;形成循环的一环。也就是说链表中最后一个节点…

项目实战之RabbitMQ冗余双写架构

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;项…

12.6每日一题(备战蓝桥杯程序的控制结构)

12.6每日一题&#xff08;备战蓝桥杯程序的控制结构&#xff09; 题目 1638: 【入门】判断正负数或零题目描述输入输出样例输入样例输出来源/分类 题解 1638: 【入门】判断正负数或零题目 1348: 【入门】求绝对值题目描述输入输出样例输入样例输出来源/分类 题解 1348: 【入门】…

【教学类-35-05】17号的学号字帖(A4竖版1份)

作品展示&#xff1a; 背景需求&#xff1a; 大四班17号男孩目前无法自主数学数字。他表示自己能够认识数字&#xff0c;但不会写。 保育老师说&#xff1a;我曾经教过他&#xff0c;抓着手示范的。但是他记不住。家里估计也不练习的。年龄还没到&#xff0c;下学期再看看能不…

SpringBoot项目访问resources下的静态资源

1.新建一个配置文件夹&#xff0c;放配置类 2.编辑 WebMvcConfig.java package com.southwind.configuration;import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import or…

期末速成数据库极简版【查询】(2)

目录 select数据查询----表 【1】筛选列 【2】where简单查询 【3】top-n/distinct/排序的查询 【4】常用内置函数 常用日期函数 常用的字符串函数 【5】模糊查询 【6】表数据操作——增/删/改 插入 更新 删除 【7】数据汇总 聚合 分类 ​ &#x1f642;&#…