美文网首页简友们的精华
<iOS 实践>关于在 iOS Socket 的一些

<iOS 实践>关于在 iOS Socket 的一些

作者: 貘鸣 | 来源:发表于2018-01-17 15:06 被阅读689次

    首先需要了解 Socket 的一些基本知识, 然后看了一下官方的 API.

    这次由于要构建一个调试工具, 先选用的是 robbiehanson/CocoaAsyncSocket 这个框架, 先对而言上手很快, 且使用简单.

    要实现的需求是要在手机端搭建一个 Socket 接收端, 同时也需要发送数据.

    但是第一个坎就没有过去, 当在手机上(iOS 11.2.2)实现服务端时, 无法接收外界传入的数据(使用 UDP 情况下, TCP 下亦然).

    为了验证, 故切换为 TCP, 然后用 MAC 实现一个服务端, 手机连接进去一切正常, 情况如下:

    把服务端放到 MAC 上, 完全相同的 Socket 服务端代码条件下, 指定了如下图的 APP 功能, 则可以正常连接(TCP):


    image.png

    这里是手机端的输出:

    尝试连接
    已连接到: 192.168.199.127:9002
    

    这里是 MAC 电脑上的输出:

    已启动监听
    已监听到连接并建立连接.
    newSocket's localHost: 192.168.199.127
    newSocket's connectedHost: 192.168.199.116
    

    如果上面的勾勾不打的话, 会提示绑定端口错误.

    但是在 iOS 工程上, 真没有找到和这个勾勾类似的东西...

    再去参考一下 swisspol/GCDWebServer, 为什么它可以让服务器在手机上运行起来呢, 而且可以指定端口? 证明并非是 IP 的关系. 所以这里就进入到它的源码里面去看看.

    发现两个的实现原理都不一样, GCDWebServer 是基于 bonjour 的.

    莫非是 iOS 直接就禁止了监听外界的 Socket 请求? 找了网上很久也没找到可以在 iOS 上构建 Socket 服务端到底可行否? 且完全没有找到任何地方有在 iOS 上建立 Socket 服务端的案例, 所以最好的办法还是从头开始仔细看看官方文档...

    好吧, 网上有说用 multi peer connectivity framework 来实现的, 下面就来看看. 有时候一条路走不通, 就换种思路继续做呗...

    先看了一下这篇文章, 里面说到它是Bonjour 的继承者, 然后看了一下内容, 貌似还是只有苹果平台可用...

    文末提到 Websocket, 可用尝试一下...

    但最终发现全部的例子中, 如果用 TCP 的情况下, 都是把手机作为客户端的. 如果用 UDP 的话手机和其他地方就是两个对等的点了.

    故继续看官方文档, 先看 Networking Overview 启发一下思路, 然后是 Networking Programming Topics...

    参考官文里面的一句:

    There are only two APIs that provide the ability to listen for incoming network connections: the Core Foundation socket API and the POSIX (BSD) socket API. Higher-level APIs cannot be used for accepting incoming connections.

    即目前只有两套 API 提供了监听网络连接请求的能力, 一套是 Core Foundation socket API, 另外一套是 POSIX 的 Socket API. 高层的 API 无法被用来 accepting 进入的连接.

    use NSStream for remote connections and CFSocket for listening

    使用 NSStream 来建立远程连接, 而用 CFSocket 来监听及 accepting 进入的连接.(详见上述Networking Programming Topics 中的 Do Not Use NSSocketPort (OS X) or NSFileHandle for General Socket Communication)

    之前直接建立了一个可工作的客户端, 下面就来跟着官文建立一个可监听外界连接请求的服务端.

    文档中需要的 <sys/socket.h> 在 swift 4 中默认就已经引入进去了的, 貌似 <netinet/in.h> 也是一样.

    在 swift 官网上有一句说的是:

    BSD sockets are a common pain point for Swift interoperability. Swift 3.0 exposes the difficultly in doing this correcly. Fortunately, Quinn “The Eskimo!” has provided these helpful wrappers.

    下面提供了一个 Socket 扩展, 详见这个链接, 便于使用.

    但为何两个 iPhone 之间的 Socket 通信这么蛋疼? 在苹果官方论坛找到了这样一个问答: iOS 10.2 socket issue, 这里说到了两个 iPhone 之间的 Socket 通信受阻并非是由系统造成的, 可能是由于 wifi 等外部原因造成的(比如本地的 wifi 阻止了 iOS 设备间的 STA TO STA 通信, STA 即 station, 它相当于 wifi 中的客户端设备), 在里面推荐了一篇关于入门文章Wi-Fi Fundamentals.

    由于要彻底解决 wifi 导致的通信受阻问题, 就需要首先了解 wifi 中的一些东西, 根据上面的文章, 慢慢看.

    术语表:

    • STA (station) : wifi 中的客户端设备
    • AP (access point): 指的是运行 Wi-Fi 网络的硬件设备. 关于这里定义的 Wifi 网络的详细信息在后面会讲.
    • SSID (Service Set Identifier): It’s the user-visible network identifier string that you see throughout the system.
    • BSSID (Basic Service Set Identifier): This defines a single Wi-Fi network at the Wi-Fi level. It’s identified by the MAC address of the AP, something that’s generally not user visible.

    wifi 实现了如下两类广播:

    • Unicasts
    • Broadcasts(Multicasts)

    不过最后最主要的还是在文章末尾提到一个检测 STA--->AP, AP--->STA 的包传输跟踪方法, 详见Getting a Packet Trace.

    这里是一些 MAC 上可用的网络调试工具列表.

    先把 iPhone 的整个网络栈映射到 MAC 上, 然后通过网络调试工具来查看通信内容:

    The RVI represents the entire networking stack of the iOS device

    具体操作:

    1. 用 USB 连接手机和 MAC 电脑.
    2. 为设备创建一个 RVI(remote virtual interface, 远程虚拟接口, iOS 5 以上版本才有) , 通过 RVI, 可以在 MAC 上使用抓包工具来追踪 iPhone 上的网络通信.
      # 使用如下命令可以查看当前所有的网络接口:
      ifconfig -l
      # 这个命令执行后显示如下
      lo0 gif0 stf0 en0 en1 p2p0 fw0 ppp0 utun0
      # 获取设备的 UUID(在 itunes 里就看得到), 然后用如下命令建立 RVI, 命令参数即UUID:
      rvictl -s 74bd53c647548234ddcef0ee3abee616005051ed
      # 此时再用 ifconfig -l 查看的话, 会发现多了一个网络接口, 多出来的 rvi0 就是新建的:
      ifconfig -l
      lo0 gif0 stf0 en0 en1 p2p0 fw0 ppp0 utun0 rvi0
      
    3. 有了上述的接口名称 rvi0, 就可以通过抓包工具对该接口进行抓包(下面只是演示):
      sudo tcpdump -i rvi0 -A
      
    4. 如果是想移除 RVI, 则执行如下命令:
      # 其中参数值是设备的 UUID
      rvictl -x 74bd53c647548234ddcef0ee3abee616005051ed
      

    另外有一个 CPA 工具, 来分析看包到底到了手机端没有.

    tcpdump 也是个好东西, 可以学学它的用法, 具体可以参考这篇官文.

    下面的测试使用的是如下命令来执行的 tcpdump:

    # ip 地址为想要监听的host, 从该 host 发送的包, 或发向该 host 的包都会被抓取.
    sudo tcpdump host 192.168.199.116 -i rvi1
    

    但在测试的时候有一个问题:
    两台手机, 一台 iPhone6P, 一台 iPhone 6, 然后在 Xcode 中写了两个示例工程, 一个工程只是通过
    UDP 协议利用 Socket 发送数据, 另外一个工程的作用是接收通过 UDP 协议发送的过来的数据.

    1. 当使用 6 来向 6P 发送数据, 无法发送.
    2. 当使用 6P 来向 6 发送数据, 一切正常.
    3. 使用 tcpdump 查看, 实际上每次发送的时候数据包都是过去了的(抓取接收方的包是接收到了的, 发送方也是发送了的).

    数据包在两种情况下都是顺利被发送和接收到了的, 但为什么一个手机在上层无法接收, 另外一个可以接收呢?

    苹果在 10.2 之后可能对网络功能进行了修改? 但两个手机的系统都是 11.2.2 , 为什么同样的发送和接收代码, 在两台 iPhone 上面还会有不同的反应?

    上面遇到的问题就无法解答了, 望看到的朋友能够给予帮助, 万分感谢!

    相关文章

      网友评论

      • ebay_Happy:没说重点
        貘鸣:额, 感谢提醒, 最近也准备将之前写的这些文章重新整理一下再发布.

      本文标题:<iOS 实践>关于在 iOS Socket 的一些

      本文链接:https://www.haomeiwen.com/subject/iiosoxtx.html