`

iOS使用GameKit构建“Master-Client”网络

 
阅读更多
GameKit是iOS SDK中一个常用的框架。其核心功能有3个:

* 交互游戏平台Game Center,
* P2P设备通讯功能
* In-Game Voice。

本文主要介绍的就是我在开发一个对战游戏时使用到的设备通讯功能。在游戏中采用的是“Master-Client”形式的网路拓扑。即有机器作为主机Host一个游戏,然后其他设备以玩家的身份加入,同时主机也是其中一个玩家。UI界面大概就是下面这个样子:
Host界面:


玩家界面:



整个网络的连接过程为:

* 主机向自己所在的局域网发出信号:“我是擎天柱,我现在可以接受连接了。”,等待连接
* 玩家进入搜寻主机界面,发现主机,则把主机的信息显示在自己的可连接游戏列表中。
* 玩家点击主机名,连接到主机上。
* 主机接受到其他设备的连接,把接入设备信息显示到自己的接入玩家列表中。

这样,就完成了局域网设备中,设备之间的相互发现功能。这一切在iOS中是怎么实现的呢?

iOS中P2P设备的通讯功能都是基于GKSession来实现的,关于GKSession,苹果的官网上描述如下:

“A GKSession object provides the ability to discover and connect to nearby iOS devices using Bluetooth or Wi-fi.

Sessions primarily work with peers. A peer is any iOS device made visible by creating and configuring a GKSession object. Each peer is identified by a unique identifier, called a peer id (peerID) string. Your application can use a peerID string to obtain a user-readable name for a remote peer and to attempt to connect to that peer. Similarly, your session’s peer ID is visible to other nearby peers. After a connection is established, your application uses the remote peer’s ID to address data packets that it wants to send.

Peers discover other peers by using a unique string to identify the service they implement, called a session ID (sessionID). Sessions can be configured to broadcast a session ID (as a server), to search for other peers advertising with that session ID (as a client), or to act as both a server and a client simultaneously (as a peer.Your application controls the behavior of a session through a delegate that implements the GKSessionDelegate protocol. The delegate is called when remote peers are discovered, when those peers attempt to connect to the session, and when the state of a remote peer changes. Your application also provides a data handler to the session so that the session can forward data it receives from remote peers. The data handler can be a separate object or the same object as the delegate.”

简单来说,就是如下几点:

* 1)这个类就是用来连接附近的iOS设备的,这个近怎么算呢?就是蓝牙和WiFi可达;
* 2)在GKSession中每台设备都有一个自己的Peer ID,这是整个网络连接中最重要的信息。连接哪个设备,数据传送到那个设备,设备名称配置等都是通过Peer ID来做的;
* 3)设备之间的识别,连接,状态变化是通过实现GKSessionDelegate来确认具体行为的;
* 4)设备之间的数据的接收则是通过指定Data Receiver Handler来实现,这个Data Receive Handler可以随便指定。

好,接下来就看一下游戏中的设备发现功能是怎么做的,在Host界面的ViewController中,我使用了一个MatchingServer类来处理所有和网络连接相关的代码,避免ViewController过于庞大。在ViewController启动完之后之后,调用了MatchingServer的代码来启动一个Session,向外传递信息;“本主机可以接收连接了…”
[_matchingServer startAcceptingConnectionsForSessionID:SESSION_ID];

在MatchingServer中,保存了一个GKSession的属性,同时该类实现了GKSessionDelegate protocol。
@interface MatchingServer : NSObject<GKSessionDelegate>
...
@property (nonatomic, strong, readonly) GKSession *session;
...

在实现类中,MatchingServer创建了一个新的GKSession,创建GKSession给了Session的名字,如果给nil的话,或自动使用应用的Bundle Name,然后主机想要展示的名字,最后是这是一个这个Session的模式。
_session = [[GKSession alloc] initWithSessionID:sessionId displayName:nil sessionMode:GKSessionModeServer];
_session.delegate = self;
_session.available = YES;

在玩家界面时,同样的,我使用了一个MatchingClient来处理所有网络连接的事。
[_client startSearchForServersWithSessionID:SESSION_ID];

@interface MatchingClient : NSObject<GKSessionDelegate>
...
@property (nonatomic, strong, readonly) GKSession *session;
...

_session=[[GKSession alloc] initWithSessionID:SESSION_ID displayName:nil sessionMode:GKSessionModeClient];
_session.delegate = self;
_session.available = YES;

上面的代码中,主机以Server模式创建了一个GKSession并设置为available,Client以Client模式创建了一个GKSession同样设置为available,他们都使用了相同的Session ID,因为GKSession只能发现具有相同Session ID的设备。GKSession一创建,设备就开始查找附近的设备,发现设备了怎么处理呢?

设备连接
这就是GKSessionDelegate的控制领域,GKSession会根据自己探测到的设备的状态调用对应的GKSessionDelegate的方法。

GKSessionDelegate有4个方法:
检测到设备发生状态改变时,调用:
- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state;
收到其它设备的连接请求时,调用:
- (void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID;
和其他设备连接发生错误时,调用:
- (void)session:(GKSession *)session connectionWithPeerFailed:(NSString *)peerID withError:(NSError *)error;
GKSession内部发生错误时,比方说无法初始化GKSession,调用:
- (void)session:(GKSession *)session didFailWithError:(NSError *)error;

因此,在MatchingServer中,主要就是等待和接受Client连接,需要关注的状态改变就是一个Client的连接和断开,当和一个Client建立连接时,把接入的client放入connectedClients,断开连接则移除:
-(void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state
{
    NSLog(@"MatchmakingServer: peer %@ changed state %d", peerID, state);
    switch (state) {
        case GKPeerStateConnected:
            if (_serverState == ServerStateAcceptingConnection) {
                if (![_connectedClients containsObject:peerID]) {
                    [_connectedClients addObject:peerID];
                    [self.delegate matchingServer:self clientDidConnected:peerID];
                }
            }
            break;
       case GKPeerStateDisconnected:
            if (_serverState != ServerStateIdle) {
                if ([_connectedClients containsObject:peerID]) {
                    [_connectedClients removeObject:peerID];
                    [self.delegate matchingServer:self clientDidDisconnected:peerID];
                }
            }
            break;
        default:
            break;
    }
}

当Server收到一个连接请求时,如果主机尚未开始游戏,且游戏未达最大人数限制,则接受连接,反之,则拒绝连接。
-(void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID
{
    NSLog(@"MatchmakingServer: connection request from peer %@", peerID);
    if (_serverState == ServerStateAcceptingConnection && [_connectedClients count] < _maxClients) {
        NSError *error;
        if ([_session acceptConnectionFromPeer:peerID error:&error]) {
            NSLog(@"MatchmakingServer: connected to peer %@", peerID);
        }
        else
        {
            NSLog(@"MatchmakingServer: Error accepting connection from peer %@, %@", peerID, error);
        }
    }
    else
    {
        [session denyConnectionFromPeer:peerID];
    }
}

在MatchingClient中,则主要关注的是主机设备的状态,不同的状态,Client则需要对自身状态做出调整,同时更新一些界面参数:
- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state
{
#ifdef DEBUG
	NSLog(@"MatchmakingClient: peer %@ changed state %d", peerID, state);
#endif
    
    switch (state) {
        case GKPeerStateAvailable:
            if(_clientState == ClientStateSearchingForServers){
                if (![_availableServers containsObject:peerID])
                {
                    [_availableServers addObject:peerID];
                    [self.delegate matchingClient:self serverBecameAvailable:peerID];
                }
            }
            break;
        case GKPeerStateUnavailable:
            if(_clientState == ClientStateSearchingForServers){
                if([_availableServers containsObject:peerID])
                {
                    [_availableServers removeObject:peerID];
                    [self.delegate matchingClient:self serverBecameUnavailable:peerID];
                }
            }
            if (_clientState == ClientStateConnecting && [_serverPeerID isEqualToString:peerID]) {
                [self disconnectFromServer];
            }
            break;
        case GKPeerStateConnected:
            if (_clientState == ClientStateConnecting) {
                _clientState = ClientStateConnected;
                [self.delegate matchingClient:self didConnectToServer:peerID];
            }
            break;
        case GKPeerStateDisconnected:
            if (_clientState == ClientStateConnected) {
                [self disconnectFromServer];
            }
        default:
            break;
    }
}

通过上面所有的代码,游戏具备了基本的发现设备,连接设备的能力,接下来说说,如何在设备之间传递数据。

数据传输
在GKSession中,数据传输非常简单。只需要准备好数据,然后调用相关的session的方法发送即可。与数据传输相关的方法有3个:

设定接受数据的Delegate:
– setDataReceiveHandler:withContext:

发送数据到指定的节点:
– sendData:toPeers:withDataMode:error:

发送数据给所有节点:
– sendDataToAllPeers:withDataMode:error:


前面提到GKSession对象的Data Receive Handler可以设定为任何对象,没有限制。但是被设为Data Receive Handler的对象必须实现方法:
- (void)receiveData:(NSData *)data fromPeer:(NSString *)peerID inSession:(GKSession *)session context:(void *)context 

否则,会导致整个应用的崩溃。



参考:http://developer.apple.com/library/ios/#documentation/GameKit/Reference/GKSession_Class/Reference/Reference.html
  • 大小: 451.1 KB
  • 大小: 437.2 KB
分享到:
评论

相关推荐

    ios应用源码之感恩---samplecode程序清单 程序列表 2018128

    最后,这份源码清单可能还涵盖了其他主题,比如动画效果的实现(如CAAnimation)、推送通知的配置、定位服务(CLLocationManager)的使用,甚至是ARKit或GameKit等高级功能的应用。 总的来说,"ios应用源码之感恩-...

    IOS应用源码之bryansum-iPong-8c15a5f.zip

    【标题】"IOS应用源码之bryansum-iPong-8c15a5f.zip" 提供的是一个iOS应用的源代码,名为iPong,版本号8c15a5f。这个项目可能是一个基于Apple的iOS平台开发的乒乓球游戏应用。源代码的分享通常用于学习、研究或改进...

    IOS应用源码——plaetzchen-Super-Herbert-ad42767.rar

    7. **GameKit或SpriteKit**:如果"Super Herbert"是一个游戏,那么可能使用了GameKit(处理多人游戏和成就系统)或SpriteKit(2D游戏引擎)。源码会揭示如何实现游戏逻辑、动画效果和物理模拟。 8. **第三方库和API...

    IOS应用源码——zootella-cocos2d-abfc8f6.rar

    3. **iOS SDK**:包含Apple的API和库,如UIKit用于构建用户界面,GameKit提供游戏服务,CoreData进行数据持久化等。 4. **Interface Builder**:Xcode内的设计工具,用于可视化布局UI元素,与Storyboard结合使用。 ...

    IOS应用源码——robin-cocoa-web-resource-8f47f25.rar

    Cocoa Touch是Apple为iOS和watchOS开发的应用程序提供的一套框架,它包括UIKit、SpriteKit、GameKit等子框架,涵盖了用户界面、动画、网络通信等多个方面。在这个源码库中,我们可以找到关于如何利用Cocoa Touch构建...

    IOS应用源码——stevederico-TabooPlus-057743b.rar

    4. **GameKit框架**:如果应用支持多人在线游戏,那么GameKit框架的使用就显得尤为重要。它可以处理玩家匹配、对战和成就等功能。 5. **Storyboard与Auto Layout**:iOS应用通常使用Storyboard来组织UI界面,同时...

    IOS应用源码之codypo-ShipwreckAdventure-ae3098a.zip

    4. **框架和库**:项目可能会引用Apple的SDK(如UIKit、GameKit等)以及其他第三方库,如AFNetworking(网络请求)、MBProgressHUD(加载指示器)等,以增强功能。 5. **测试文件**:`.m`和`.h`文件的测试版本,...

    IOS应用源码——yanzheng-match_game_git-c230d4f.rar

    通过深入分析“yanzheng-match_game_git-c230d4f”的源码,开发者可以学习到如何构建一个完整的iOS游戏应用,理解游戏逻辑的实现、用户界面的搭建以及数据管理的方法。此外,该项目还可能涵盖了错误处理、性能优化、...

    ios-蓝牙实现对等网络连接.zip

    在iOS平台上,蓝牙技术被广泛应用于设备间的无线通信,尤其是对于构建对等网络(Peer-to-Peer,简称P2P)连接。这个“ios-蓝牙实现对等网络连接.zip”压缩包很可能包含了一个名为“gameKitDemo”的示例项目,它是...

    IOS应用源码——zuwiki-RainbowDash2D-450e340.rar

    6. ** Game Center集成 **:如果应用集成了Game Center,我们可以学习如何实现排行榜、成就系统等功能,这涉及到GameKit框架的使用。 7. ** 用户界面设计 **:除了游戏核心逻辑,用户界面的设计和交互也是重要组成...

    iOS 8应用开发实战-205个快速上手的开发技巧源代码

    通过研究和实践这些源代码,开发者不仅能提升编码技巧,还能深入了解iOS 8的特性和API,从而更好地构建高效、功能丰富的应用程序。每个示例都是一次学习的机会,将理论知识转化为实际操作,是成为优秀iOS开发者的...

    PyPI 官网下载 | pyobjc-framework-GameKit-5.1.2.tar.gz

    其中,“pyobjc-framework-GameKit-5.1.2.tar.gz”是一个特定版本的Python库,它将Apple的GameKit框架与Python编程语言相连接,使得Python开发者也能利用GameKit提供的功能。 PyObjC是一个强大的工具,它允许Python...

    iOS游戏应用源代码——iOS-Gamer-Cocos2D-86714e3.zip

    2. **Objective-C编程**:作为苹果的官方编程语言,Objective-C是iOS开发的基础,这个项目中的源代码将展示如何在Objective-C中组织类和对象,以及如何利用Apple的UIKit和GameKit框架进行应用开发。 3. **Xcode集成...

    IOS应用源码Demo-多人游戏_Pong_源码_for_iPhone_iPad-毕设学习.zip

    5. **网络通信**:如果Pong游戏支持多人在线对战,那么源码中将涉及网络编程,可能使用Socket编程或者GameKit框架来实现玩家间的实时通信。 6. **游戏物理模拟**:Pong游戏的物理规则,如球的反弹、速度变化,可能...

    iOS游戏应用源代码——mgrebenets-the-eggs-ios-0ad97de.zip

    iOS游戏通常使用Apple的Objective-C或Swift语言编写,结合使用Cocoa Touch框架和GameKit等库。在这个项目中,源代码可能混合了这两种语言,因为Objective-C是早期iOS开发的主流,而Swift则在后来成为首选。开发者...

    iOS实例开发源码——instinct-GameFroot-bcca837.zip

    9. **Multiplayer Networking**:如果是多人在线游戏,可能使用GameKit进行网络通信。 10. **Xcode IDE**:所有iOS开发都离不开Xcode,它提供了代码编辑、调试、构建和部署等一系列功能。 11. **Interface Builder...

    IOS网络开发高级教程

    在iOS网络开发中,应用之间以及设备间的通信是构建大规模分布式应用系统的关键。本书的第四部分讲解了如何实现应用间的通信,如使用URL schemes和自定义URL types。同时,还介绍了使用GameKit进行设备间的通信,这是...

    iOS五子棋-双人联机源码.zip

    总的来说,通过研究这个开源项目,开发者不仅可以学习到如何在iOS平台上构建一个完整的双人对战游戏,还能掌握游戏网络同步、用户交互设计以及基础的AI实现。对于想要提升iOS游戏开发技能的人来说,这是一个极好的...

    iOS实例开发源码——kballenegger-MusicKombat-d8c11bd.zip

    综上所述,"MusicKombat"项目涵盖了iOS开发的多个重要方面,包括Swift编程、用户界面设计、音频处理、网络通信以及版本控制等。通过深入研究该项目源码,开发者不仅可以学习到实际的开发技巧,还能了解如何将这些...

    iOS实例开发源码——Lizdo-MatchTwo-c85444f.zip

    iOS开发通常涉及到使用Xcode集成开发环境(IDE),以及苹果提供的各种框架和服务,如UIKit、SpriteKit、GameKit等。 【压缩包子文件的文件名称列表】: "Lizdo-MatchTwo-c85444f" 这个单一的文件名可能代表了整个项目...

Global site tag (gtag.js) - Google Analytics