[iOS翻译]通过USB与您的iOS应用程序进行通信(C#和/或Xamarin)。

1,011 阅读6分钟

原文地址:thecodewash.blogspot.com/2017/05/com…

原文作者:thecodewash.blogspot.com/

发布时间:2017年5月24日

通过USB与您的iOS应用程序进行通信(C#和/或Xamarin)。

所以,你正在编写一个需要与桌面客户端通信的iOS应用。在google上快速搜索一下就会发现,唯一的方法就是在桌面客户端上创建一个小型的socket服务器,在给定的端口上进行监听,然后让iOS应用通过你的本地网络连接到桌面客户端。这绝对是可行的,而且很多时候已经很好了。如果你熟悉socket编程,这绝对是直接的。然而,肯定有一些缺点。应用程序必须与客户端在同一个网络上(在企业环境中,你的计算机在一些私有的有线网络上,这并不总是可能的)。客户端为你的电脑打开了一个端口,网络上的任何人都可以访问,这可能是一个安全问题。初始设置也比较繁琐,因为应用用户必须在应用中输入IP地址和端口才能连接。如果能够通过USB线实现 "iOS应用到桌面 "的通信,就可以解决所有这些问题,并使(在我看来)应用更加方便。

这个方法的问题只是关于如何实现这个功能的文档太少了。一些论坛的帖子说这是不可能的。其他人会说,你需要注册苹果的MFi计划(一个漫长而乏味的过程)。好吧, 在花了上周在互联网上搜索, 我在这里告诉你,它绝对是可能的,没有MFi. 而且,一旦你明白发生了什么,它甚至不是那么困难。所以,这就是我学到的一切。

几个快速说明

我主要是一个C#/.NET的开发人员,所以我写这篇文章是假设你也是。这些概念是非常通用的,但例子将使用C#。我正在使用Xamarin.iOS来构建iOS应用,所以即使是iOS代码也将使用C#。

另外,我认为有必要指出,对于编写Swift/Objective-C代码的开发者来说,有一个非常棒的平台工具,叫做Peertalk,它可以完成我将要讲到的所有内容。不幸的是,没有Xamarin的绑定。我几乎决定关掉Xamarin来使用这个,最后才知道如何非常快速、轻松地完成Peertalk的工作。

苹果是怎么做的呢?

当你把你的iOS设备插到电脑上时,你就可以通过USB线来同步你的数据。苹果实现这个功能的方式是通过一个非常方便的程序usbmuxd。这是一个在电脑上运行的程序,等待iOS设备的连接。如果你的机器上安装了iTunes,你也安装了这个程序。在Windows上,承载这个程序的服务名为 "Apple Mobile Device Service",可以在services.msc中看到。那么,usbmuxd是做什么的呢?

usbmuxd

usbmuxd是USB多路复用守护进程的缩写。如果你和我一样,"USB "是这个名字中唯一真正熟悉的术语。但这个程序所做的事情其实很简单(而且对我们来说非常有用)。它是一个后台进程(守护进程),可以告诉你的iOS设备将所有从USB电缆接收到的数据映射(复用)到iOS设备上的网络端口,并返回一个套接字连接到该端口。

那么,实际上,这对我们意味着什么呢?这意味着,如果你在iOS设备上做了这样的事情

                   var socket = new Socket(AddressFamily.InterNetwork,
                       SocketType.Stream,
                       ProtocolType.Tcp);

                   socket.Bind(new IPEndPoint(IPAddress.Loopback, 5050));
                   socket.Listen(100);
                   socket.BeginAccept((ar) =>
                   {
                       var connectionAttempt = (Socket) ar.AsyncState;
                       var connectedSocket = connectionAttempt.EndAccept(ar);
                       // Do something with "connectedSocket"
                   }, socket);

上面的代码(正在你的iOS设备上运行)现在正在监听5050端口(我完全随意选择的)的传入连接。我知道你可能在想什么。"我以为我们不会使用网络连接。什么情况!?"

这就是usbmuxd的神奇之处。从你的桌面应用程序,你可以发送一个消息到usbmuxd,通知连接的iOS设备接收网络端口上的所有USB数据。在我们的例子中,我们想让usbmuxd告诉我们的iOS设备在5050端口接收USB数据。usbmuxd会答应,并给你一个套接字句柄发送数据和接收数据。这时,你的iOS应用将调用 "BeginAccept "回调,你将有一个网络套接字,但不是真正的 "网络 "套接字。即使你的电脑和iOS设备在不同的网络上,或者根本就没有网络,这也可以工作。所有的数据都是通过你的USB线进行的!

那么......我到底该如何与usbmuxd通信呢?

幸运的是,有一个nuget包可以解决这个问题! iMobileDevice.Net的nuget页面可以在这里找到。它并不是最完善的文档工具,但它非常好用。它假定usbmuxd服务在你的机器上运行。最简单的方法是确保你已经安装了iTunes. 如果你不想依赖iTunes,你可以像Duet Display的人一样,从iTunes安装程序中解压出相应的msi,并将其作为应用程序的先决条件。

AppleApplicationSupport64.msi AppleMobileDeviceSupport6464.msi安装程序将安装你需要的服务。

一旦你知道usbmuxd确实在你的机器上,你就可以开始使用nuget包了。这个包实际上只是一个C库的包装器,它显示了。有很多IntPtr's和out参数,所以它不是很好看。但它可以工作。

这是我正在做的一个项目中的一个片段。下面的代码监听一个iOS设备事件(usbmuxd为我们调用)。当该事件是 "DeviceAdd "事件时,我尝试连接到它。

        public void BeginListening()
        {
            _iDeviceApi.idevice_event_subscribe(_eventCallback, new IntPtr());
        }

        private iDeviceEventCallBack EventCallback()
        {
            return (ref iDeviceEvent devEvent, IntPtr data) =>
            {
                switch (devEvent.@event)
                {
                    case iDeviceEventType.DeviceAdd:
                        Connect(devEvent.udidString);
                        break;
                    case iDeviceEventType.DeviceRemove:
                        break;
                    default:
                        return;
                }
            };
        }

Connect方法使用所连接的设备的udid获取一个 "DeviceHandle "引用。一旦得到这个句柄,它就会调用 "ReceiveDataFromDevice "方法。

        private void Connect(string newUdid)
        {
            _iDeviceApi.idevice_new(out iDeviceHandle deviceHandle, newUdid).ThrowOnError();
            var error =_iDeviceApi.idevice_connect(deviceHandle, 5050, out iDeviceConnectionHandle connection);
            if (error != iDeviceError.Success) return;
            ReceiveDataFromDevice(connection);
        }

        private void ReceiveDataFromDevice(iDeviceConnectionHandle connection)
        {
            Task.Run(() =>
            {
                while (true)
                {
                    uint receivedBytes = 0;
                    _iDeviceApi.idevice_connection_receive(connection, _inboxBuffer, (uint)_inboxBuffer.Length,
                        ref receivedBytes);
                    if (receivedBytes <= 0) continue;
                    // Do something with your received bytes
                }
            });
      }

最后,下面一行将数据发送到iOS设备。

        _iDeviceApi.idevice_connection_send(connection, bytesPending, (uint)bytesPending.Length, ref sentBytes);

还有很多其他的函数可供你使用,但这些是一些重要的函数。使用这些功能,你可以开始通过USB向你的iOS设备发送数据。你的iOS应用甚至不会知道这是一个USB连接。对应用程序来说,它只是另一个TCP网络连接。希望这能为你节省一些时间,如果你有任何问题,可以随时评论或给我留言。

祝你编码快乐!


www.deepl.com 翻译