OS X和iOS提供四个发现并公布网络服务的API。
- NSNetService —— 适合大多数应用开发的高级OC API。
- CFNetService —— 适合Core Fondation代码的高级C API。
- DNS Service Discovery —— 适合跨平台的代码的低级C API。
- Game Kit 框架 —— 为游戏提供点对点通信的高级OC API,可在本地(使用蓝牙和Wi-Fi)以及全球网络环境下使用。
除了这些API之外,Multipeer Connectivity 框架也提供对安装有同一款(或有相关性)应用的临近设备的相互发现和通信的支持。这些应用的所在设备,使用了基础Wi-Fi、点对点Wi-Fi、以及蓝牙(对于iOS)或者以太网(对于OS X)。
作为规则,你应该为游戏相关的点对点网络只使用Game Kit。对于运行iOS 7及更高版本的iOS设备之间的点对点网络。你应该使用Multipeer Connectivity框架。
为了兼容旧版本的iOS,你也可以使用CFNetService 或 NSNetService 编写自己的网络代码来进行发布。
注意:在支持蓝牙的设备上,蓝牙通讯通过Game Kit是自动使用的。(当通过设置interfaceIndex为kDNSServiceFlagsIncludeP2P来使用DNS Service Discovery C API的时候,使用蓝牙的Bonjour也可以被启用。详情见Bonjour over Bluetooth on iOS 5 and Later。)
Bonjour服务概述
Bonjour服务由三部分组成:
- 服务名称——这个名字对于在特定电脑上运行的特定应用实例来说必须是唯一的。
- 服务类型——它对于应用的所有实例都必须是相同的,并且应该向Internet Assigned Numbers Authority(互联网号码分配机构,IANA)注册。
- 域——如果域是空的,则主机会在它发布或浏览的域中进行选择。
当应用浏览Bonjour服务的时候,它会请求与特定域的特定类型匹配的服务,并且得到匹配的服务名字列表。然后,它会向用户呈现合适的UI。当用户告诉应用连接特定的服务时,应用应该使用连接到服务的(connect-to-service)API来进行连接。(如果因为某些原因无法这样做,该应用可以把服务的名字和端口传递给使用名字连接的(connect-by-name)API;如果通过名字连接的API不可用,该应用可以请求Bonjour来解析主机名,然后该应用可以通过IP地址和端口号来连接)。
发布网络服务
Bonjour零配置网络让你可以在网络上发布网络服务,例如打印机、或者文档同步服务。这里有三种发布网络服务的方式:
- 对于OC和Core Foundation代码,推荐的方式是使用CFNetServices API。
- 对于必须可以运行在iOS 和 OS X以外操作系统上的可移植C代码,推荐使用DNS Service Discovery C API。
你可以依照下面的步骤来发布网络服务:
- 创建一个socket来监听与服务器的连接。要了解监听网络socket上的连接的推荐方式,请参阅Networking Programming Topics中的Writing a TCP-Based Server。
- 创建一个服务对象,提供你的socket端口、域(通常是空字符串)、以及你选择的服务类型字符串:
- 对于Foundation,使用initWithDomain:type:name:port:方法初始化NSNetService对象。
- 对于Core Foundation,使用CFNetServiceCreate函数创建CFNetServiceRef对象。
- 对于DNS Service Discovery API,,调用DNSServiceRegister来返回DNSServiceRef对象。
- 分配一个委托或回调:
- 对于Foundation,使用委托方法给NSNetService对象分配一个委托。
- 对于Core Foundation,使用CFNetServiceSetClient函数给CFNetServiceRef对象分配一个委托人回调。
- 对于DNS Service Discovery API,你应该在调用DNSServiceRegister时传递一个委托人回调(或者,一个指向你选择的上下文对象的指针)。此时,除了当发生时处理回调的内容,其他已经完成。
- 如有需要,安排或重新安排服务:
- 对于Foundation,在默认模式下,该服务会在当前运行循环上被自动安排。如果你需要把该对象安排在另一个运行循环或不同的模式下,你应该取消安排并重新安排。
- 对于Core Foundation,你必须通过调用CFNetServiceScheduleWithRunLoop在运行循环上安排CFNetServicesRef对象。
- 对于DNS Service Discovery API,调用DNSServiceSetDispatchQueue,在调度队列上安排服务。(如果你必须支持OS X 10.7之前的操作系统,参见SRVResolver样例代码,它是一个说明在没有Grand Central Dispatch的情况下如何使用DNS Service Discovery的例子。)
- 如有必要,发布服务:
- 对于Foundation,通过调用publish方法发布服务。
- 对于Core Foundation,通过调用CFNetServiceRegisterWithOptions发布服务。
- 对于DNS Service Discovery API,不需要进一步操作;当你调用DNSServiceRegister的时候,服务就已经发布。
当你的服务发布后,你可以监听socket上的连接,并在连接时设置输入输出流。
重要:如果你创建了一个自定义协议,你应该使用自定义的服务类型,并使用IANA注册这个服务类型。详细信息,参见RFC 6335。
浏览和连接网络服务
寻找和解析网络服务的过程和发布它的过程一样简单。要在OC中浏览网络服务,创建一个NSNetServiceBrowser类的实例,并给它分配一个委托。然后该服务浏览器调用searchForServicesOfType:inDomain:方法。一旦有服务被发现,netServiceBrowser:didFindService:moreComing:委托方法就会被调用。
想要连接服务器,首先通过调用stop来暂停浏览(除非你有特殊的原因要继续浏览),然后用代表服务的NSNetService对象调用getInputStream:outputStream:方法。服务的地址将会自动解析。
你还可以使用带有CFNetServiceRef对象的CFStreamCreatePairWithSocketToNetService方法,来连接Bonjour服务。
重要:如果你正在使用ARC,你应该阅读NSNetService and Automatic Reference Counting (ARC)。
解析网络服务
你或许需要手动解析网络服务,以便提供服务的地址给没有接收到网络服务名称的API。想要在OC中解析网络服务,首先通过调用stop来听值浏览,然后该服务的NSNetService对象调用resolveWithTimeout:方法。
当服务的地址被解析之后,会调用服务的委托中的netServiceDidResolveAddress:方法。然后你可以使用hostName方法访问服务的主机名,或使用addresses方法访问它的地址信息。为了避免不必要的网络流量,你还应该在得到一组地址返回后,让NSNetService对象调用stop。
重要:这个解析过程返回数字的IP地址和主机名。IP地址可以是IPv4和IPv6地址的任意组合。除非你做不寻常的事,否则你通常应该将主机名传递给任何支持主机名的API,而不是直接使用IP地址,否则,你将不得不编写自己的代码来尝试将每个IP地址进行并联或串联(进一步描述在Avoid Resolving DNS Names Before Connecting to a Host中)。
多点连接(Multipeer Connectivity)概述
Multipeer Connectivity(多点连接)框架提供一个在Bonjour顶部的层,它让你与附近设备(通过基础Wi-Fi、点对点Wi-Fi、蓝牙或以太网)上运行的应用通信,而无需为你的应用编写很多网络代码。
使用多点连接,应用可以发布其可用性。然后,它可以发现正在附近其他设备上运行的该应用(或共享同一个服务类型的其他应用),并可以邀请这些附近的点加入会话。如果它们接收邀请,应用就可以指通过一个方法调用发送信息或文本到一个或多个连接的点。
重要:与Bonjour一样,应用必须提供服务类型,并且你应该使用IANA注册服务类型,参见RFC 6335。
如果你需要基于流的通信,应用可以打开单向流到任何连接的点(它也可以打开一个单向流返回到该应用作为响应)。
最后,多点连接提供在会话上下文之外分享少量数据的能力,如果需要,允许你提供给用户一些信息,用户可以在选择点时使用这些信息访问会话。
了解更多
Multipeer Connectivity——阅读Multipeer Connectivity Framework Reference和MultipeerGroupChat样例代码项目
Game Kit——阅读Game Center Programming Guide, Game Kit Framework Reference,以及 GKRocket and GKTank样例代码项目
NSNetService—阅读 NSNetServices and CFNetServices Programming Guide, NSNetServiceBrowser Class Reference, NSNetServiceBrowserDelegate Protocol Reference, 以及 NSNetServiceDelegate Protocol Reference. 样例代码, 参见RemoteCurrency 样例代码项目.
CFNetService—阅读 NSNetServices and CFNetServices Programming Guide 和 CFNetServices Reference.
DNS Service Discovery—阅读 DNS Service Discovery Programming Guide 和 DNS Service Discovery C Reference.