局域网通讯

GameKit-GKPeerPickerController

  • Peer-to-Peer的连接

Session:会话iPhone通过Session互相发送数据读取数据
Peer:每一部网络中的设备算一个节点

第一节 发现其他的节点:
会话要么把自己当成 服务器/客户端 中的一种,要么做一个节点即当服务器又当客户端

  1. 实现一个Server
    initWithSessionID:displayName:sessionMode:GKSessionModeServer|GKSessionModePeer
    服务器收到客户请求后,回调 session:didReceiveConnectionRequestFromPeer:
    委托类 接受请求
    acceptConnectionFromPeer:error:
    委托类 拒绝请求
    denyConnectionFromPeer:
    成功建立连接后:回调委托类函数:session:peer:didChangeState
  2. 连接到一个服务
    initWithSessionID:displayName:sessionMode: GKSessionModeClient|GKSessionModePeer
    发现可用的服务后,回调委托类函数:session:peer:didChangeState: 提供自己的PeerID
    发送请求:connectToPeer:withTimeout
    成功建立连接:回调:session:peer:didChangeState
  • 交换数据
  1. 发送:
    sendDataToAllPeers:withDataMode:error:
    sendData:toPeer:withDataMode:error
  2. 接受:
    receiveData:fromPeer:inSession:context:
    setDataReceivehandler:withContext:

第三节
断开节点:
disconnectPeerFromAllPeers:
回调:session:peer:didChangeState:
第四节 清理:
移除数据处理器并释放会话

2.添加一个节点选择器

  1. 创建并初始化一个GKPeerPicerController
    picker = [[GKPeerPickerController alloc] init];
  2. 附加委托
    picker.delegate = self;
  3. 配置可选网络 ( 默认蓝牙 )
    picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby |
    GKPeerPickerConnectionTypeOnline;
  4. 若选择因特网连接,实现:
  • (void)peerPickerController:(GKPeerPickerController *)picker
    didSelectConnectionType:(GKPeerPickerConnectionType)type{
    if (type == GKPeerPickerConnectionTypeOnline) {
    picker.delegate = nil;
    [picker dismiss];
    [picker autorelease];
    //implement your own internet user inter face here
    }
    }
  1. 实现委托的节点选择器
    peerPickerController:(GKPeerPickerControlller )picker
    sessionForConnectionType:(GKPeerPickerConnectionType)type
    {
    GKSession
    session = [[GKSession alloc]] initWithSessionID:myExampleSessionID displayName:myName sessionMode:GKSessionModePeer] ;

[session autorelease];
}
//在覆盖节点选择器的原有方法时实现此方法。

  1. 实现委托的peerPickerController:didConnectPeer:toSession:方法获得对配置好的session的所有权。
    - (void)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:( NSString *)peerID toSession: (GKSession *)season{
    // 使用Retain过的属性拥有这个session
    self.gameSession = self;
    // 假设我们的对象同样是委托类
    session.delegate = self;
    [session setDataReceiveHandler: self withContext:nil];
    //除掉picker
    picker.delegate = nil;
    [picker dismiss];
    [picker autorelease];
    //开始你的游戏
    }
    7.实现取消连接的代理方法
  • (void) peerPickerControllerDidCancel:(GKPeerPickerController *)picker
    {
    picker.delegate = nil;
    //控制器自动取消对话界面
    [picker autorelease];
    }
  1. 显示对话
    [picker show];

*使用Sessions

  1. 实现 会话代理的方法:session:didChangeState:
  • (void) session:(GKSession *)session peer:(NSString *)peerID
    didChangeState:(GKPeerConnectionState)state
    {
    switch ( state)
    {
    case GKPeerStateConnecte:
    // 相关代码
    break;
    case GKPeerStateDisconnected:
    //相关代码
    break;
    }
    }
  1. 发送数据到其他Peer
  • (void) mySendDataToPeers: (NSData *)data
    {
    [session sendDataToAllPeers: data withDataMode: GKSendDataReliable error: nil];
    }
  1. 从其他节点接收数据
  • (void) receiveData:(NSData *)data fromPeer: (NSString *)peer inSession:
    (GKSession *)session context:(void *)context
    {
    //读取数据
    }
  1. 清除会话
    [session disconnectFromAllPeers];
    session.availabel = NO;
    [session setDataReceiveHandler: nil withContext: nil];
    session.delegate = nil;
    [session release];

.

MultipeerConnectivity

此框架是在iOS7以后推出,旨在替代GameKit下的GKPeerPickerController

firechat APP iOS端采用此框架

FFMCManager.h

#import 
#import 

@class FFUser;

@interface FFMCManager : NSObject

/** 是否可以重连 当后台、退出控制器则NO */
@property (nonatomic, assign) BOOL canInvite;
/** 用户数组变更 */
@property (nonatomic, copy) void(^usersArrayChangeBlock)(NSMutableArray *onlineUsersArray);

/** 单例对象 */
+ (instancetype)sharedManager;

/** 初始化 */
- (instancetype)initWithUser:(FFUser *)user;

/** 重新初始化 */
- (void)restart;

/** 发送聊天消息给所有用户 */
- (BOOL)sendMessageToAll:(NSData *)message;

/** 发送聊天消息给指定用户 */
- (BOOL)sendMessageToUser:(FFUser *)user message:(NSData *)message;

/** 发送资源给指定用户 */
- (void)sendSourceToPeer:(MCPeerID *)peerID url:(NSURL *)url fileID:(NSString *)fileID result:(void (^)(BOOL success))result;

/** 发送聊天图片给所有用户 */
- (void)sendImgToAll:(NSURL *)url result:(void (^)(BOOL success))result;

/** 发送聊天图片给指定用户 */
- (void)sendImgToUser:(FFUser *)user url:(NSURL *)url result:(void (^)(BOOL success))result;

/** 发送头像给所有用户 */
- (void)sendAvatarToAll;

/** 发送语音给所有用户 */
- (void)sendVoiceToAll:(NSURL *)url result:(void (^)(BOOL success))result;

/** 发送语音给指定用户 */
- (void)sendVoiceToUser:(FFUser *)user url:(NSURL *)url result:(void (^)(BOOL success))result;

/** 发送语音给所有用户 */
- (void)sendVideoToAll:(NSURL *)url result:(void (^)(BOOL success))result;

/** 发送视频给指定用户 */
- (void)sendVideoToUser:(FFUser *)user url:(NSURL *)url result:(void (^)(BOOL success))result;

@end

FFMCManager.m

/**
 *  serviceType 命名规则: 由ASCII字母、数字和“-”组成的短文本串,最多15个字符
 *  MCPeerID  代表用户信息
 *  MCSession 启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion发送和读取数据
 *  MCNearbyServiceBrowser 用于搜索附近的服务端,并可以对搜索到的服务端发出邀请加入某个会话中
 *  MCNearbyServiceAdvertiser 接收并处理请求连接的响应。有回调,告知有用户要与服务端设备连接,需自定义提示框及连接处理。
 *  MCAdvertiserAssistant 接收并处理请求连接的响应。无回调,会弹出默认提示框并处理连接。
 *  MCBrowserViewController  用于搜索附近的用户,是基于MCNearbyServiceBrowse的封装
 */

/**
 *  1.通过MCPeer来生成节点信息
 *  2.通过MCNearbyServiceAdvertiser发送广播,告诉别人这里有个节点可连接
 *  3.通过MCNearbyServiceBrowser来搜索服务来找到发送广播的节点,并请求连接
 *  4.当连接成功后便可以通过MCSession来进行消息的发送和读取。
 */
#import "FFMCManager.h"

@interface FFMCManager ()
/** 当前会话 */
@property (nonatomic, strong) MCSession *session;
/** 搜索助手 */
@property (nonatomic, strong) MCNearbyServiceBrowser *browser;
/** 服务助手 */
@property (nonatomic, strong) MCNearbyServiceAdvertiser *advertiser;
/** 用户 */
@property (nonatomic, strong) MCPeerID *peerID;
/** 会话锁 */
@property (nonatomic, strong) NSLock *sessionLock;
/** 本人 */
@property (nonatomic, strong) FFUser *localUser;
/** 用户信息 */
@property (nonatomic, strong) NSMutableDictionary *usersInfoCache;
/** 在线用户 */
@property (nonatomic, strong) NSMutableArray *onlineUsersArray;

@end

@implementation FFMCManager

#pragma mark - 单例对象

static FFMCManager *_instance;

+ (instancetype)sharedManager {
    return [[self alloc] init];
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}

- (id)copyWithZone:(NSZone *)zone {
    return _instance;
}

- (id)mutableCopyWithZone:(NSZone *)zone {
    return _instance;
}

#pragma mark 初始化
- (instancetype)initWithUser:(FFUser *)user
{
    if (self = [super init]) {
        _localUser = user;
        _sessionLock = [[NSLock alloc] init];
        [self restart];
        [self addObserverActive];
    }
    return self;
}

#pragma mark 定义用户(本人)
- (void)initLocalUser
{
    if (_peerID) {
        _peerID = nil;
    }
    _usersInfoCache = [NSMutableDictionary dictionary];
    _onlineUsersArray = [NSMutableArray array];
    _peerID = [[MCPeerID alloc] initWithDisplayName:_localUser.localID];
    [_usersInfoCache setObject:_localUser forKey:_localUser.localID];
}

#pragma mark 开始会话
- (void)startSession
{
    [self stopSession];
    _session = [[MCSession alloc] initWithPeer:_peerID securityIdentity:nil encryptionPreference:MCEncryptionNone];
    _session.delegate = self;
}

#pragma mark 停止会话
- (void)stopSession
{
    if (_session) {
        [_session disconnect];
        _session.delegate = nil;
        _session = nil;
    }
}

#pragma mark 开始广播
- (void)startAdvertise
{
    [self stopAdvertise];
    _localUser.timeStamp = DDYStrFormat(@"%lf",[[NSDate date] timeIntervalSince1970]);
    _advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:_peerID discoveryInfo:_localUser.userInfo serviceType:FFService];
    _advertiser.delegate = self;
    [_advertiser startAdvertisingPeer];
}

#pragma mark 停止广播
- (void)stopAdvertise
{
    if (_advertiser) {
        [_advertiser stopAdvertisingPeer];
        _advertiser.delegate = nil;
        _advertiser = nil;
    }
}

#pragma mark 开始搜索
- (void)startBrowse
{
    [self stopBrowse];
    _browser = [[MCNearbyServiceBrowser alloc] initWithPeer:_peerID serviceType:FFService];
    _browser.delegate = self;
    [_browser startBrowsingForPeers];
}

#pragma mark 停止搜索
- (void)stopBrowse
{
    if (_browser) {
        [_browser stopBrowsingForPeers];
        _browser.delegate = nil;
        _browser = nil;
    }
}

#pragma mark 重新开始
- (void)restart
{
    [self initLocalUser];
    [self startSession];
    [self startBrowse];
    [self startAdvertise];
}

#pragma mark 邀请用户
- (void)inviteUser:(MCPeerID *)peerID
{
    if (_browser) {
        [_browser invitePeer:peerID toSession:_session withContext:nil timeout:0];
    }
}

#pragma mark 用户加入
- (void)userJoin:(MCPeerID *)peerID
{
    DDYInfoLog(@"已经连接");
    [_onlineUsersArray addObject:_usersInfoCache[peerID.displayName]];
    if (self.usersArrayChangeBlock) {
        self.usersArrayChangeBlock(_onlineUsersArray);
    }
}

#pragma mark 正在进入
- (void)userConnecting:(MCPeerID *)peerID
{
    DDYInfoLog(@"正在连接");
}

#pragma mark 用户离开
- (void)userLeave:(MCPeerID *)peerID
{
    DDYInfoLog(@"失去连接");
    [_onlineUsersArray removeObject:_usersInfoCache[peerID.displayName]];
    if (self.usersArrayChangeBlock) {
        self.usersArrayChangeBlock(_onlineUsersArray);
    }
}

#pragma mark - 发送
#pragma mark 发送聊天消息给所有用户
- (BOOL)sendMessageToAll:(NSData *)message
{
    return [_session sendData:message toPeers:_session.connectedPeers withMode:MCSessionSendDataReliable error:nil];
}

#pragma mark 发送聊天消息给指定用户
- (BOOL)sendMessageToUser:(FFUser *)user message:(NSData *)message
{
    MCPeerID *realPeerID = nil;
    for (MCPeerID *peerID in _session.connectedPeers) {
        if ([peerID.displayName isEqualToString:user.localID]) {
            realPeerID = peerID;
        }
    }
    return realPeerID ? [_session sendData:message toPeers:@[realPeerID] withMode:MCSessionSendDataReliable error:nil] : NO;
}

#pragma mark 发送资源给指定用户
- (void)sendSourceToPeer:(MCPeerID *)peerID url:(NSURL *)url fileID:(NSString *)fileID result:(void (^)(BOOL success))result
{
    NSString *imgName = DDYStrFormat(@"%@<*>%@", fileID, [url lastPathComponent]);
    [_session sendResourceAtURL:url withName:imgName toPeer:peerID withCompletionHandler:^(NSError *error) {
        if (result) result(!error);
    }];
}

#pragma mark 发送聊天图片给所有用户
- (void)sendImgToAll:(NSURL *)url result:(void (^)(BOOL success))result
{
    if (_session.connectedPeers && _session.connectedPeers.count) {
        for (MCPeerID *peerID in _session.connectedPeers) {
            [self sendSourceToPeer:peerID url:url fileID:FFSendImage result:nil];
        }
        if (result) result(YES);
    } else {
        if (result) result(NO);
    }
}

#pragma mark 发送聊天图片给指定用户
- (void)sendImgToUser:(FFUser *)user url:(NSURL *)url result:(void (^)(BOOL success))result
{
    MCPeerID *realPeerID = nil;
    for (MCPeerID *peerID in _session.connectedPeers) {
        if ([peerID.displayName isEqualToString:user.localID]) {
            realPeerID = peerID;
        }
    }
    if (realPeerID) {
        [self sendSourceToPeer:realPeerID url:url fileID:FFSendImage result:^(BOOL success) {
            if (result) result(success);
        }];
    } else {
        if (result) result(NO);
    }
}

#pragma mark 发送头像给所有用户
- (void)sendAvatarToAll
{
    for (MCPeerID *peerID in _session.connectedPeers) {
        [self sendSourceToPeer:peerID url:DDYURLStr(_localUser.avatarPath) fileID:FFSendAvatar result:nil];
    }
}

#pragma mark 发送语音给所有用户
- (void)sendVoiceToAll:(NSURL *)url result:(void (^)(BOOL success))result
{
    if (_session.connectedPeers && _session.connectedPeers.count) {
        for (MCPeerID *peerID in _session.connectedPeers) {
            [self sendSourceToPeer:peerID url:url fileID:FFSendVoice result:nil];
        }
        if (result) result(YES);
    } else {
        if (result) result(NO);
    }
}

#pragma mark 发送语音给指定用户
- (void)sendVoiceToUser:(FFUser *)user url:(NSURL *)url result:(void (^)(BOOL success))result
{
    MCPeerID *realPeerID = nil;
    for (MCPeerID *peerID in _session.connectedPeers) {
        if ([peerID.displayName isEqualToString:user.localID]) realPeerID = peerID;
    }
    if (realPeerID) {
        [self sendSourceToPeer:realPeerID url:url fileID:FFSendVoice result:^(BOOL success) {
            if (result) result(success);
        }];
    } else {
        if (result) result(NO);
    }
}

#pragma mark 发送语音给所有用户
- (void)sendVideoToAll:(NSURL *)url result:(void (^)(BOOL success))result
{
    if (_session.connectedPeers && _session.connectedPeers.count) {
        for (MCPeerID *peerID in _session.connectedPeers) {
            [self sendSourceToPeer:peerID url:url fileID:FFSendVideo result:nil];
        }
        if (result) result(YES);
    } else {
        if (result) result(NO);
    }
}

#pragma mark 发送视频给指定用户
- (void)sendVideoToUser:(FFUser *)user url:(NSURL *)url result:(void (^)(BOOL success))result
{
    MCPeerID *realPeerID = nil;
    for (MCPeerID *peerID in _session.connectedPeers) {
        if ([peerID.displayName isEqualToString:user.localID]) realPeerID = peerID;
    }
    if (realPeerID) {
        [self sendSourceToPeer:realPeerID url:url fileID:FFSendVideo result:^(BOOL success) {
            if (result) result(success);
        }];
    } else {
        if (result) result(NO);
    }
}

#pragma mark - MCNearbyServiceBrowserDelegate
#pragma mark Found a nearby advertising peer
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(nullable NSDictionary *)info
{
    if (info) {
        FFUser *user = [FFUser userWithDict:info];
        [_usersInfoCache setObject:user forKey:peerID.displayName];
        if ([info[@"timeStamp"] doubleValue] > [_localUser.userInfo[@"timeStamp"] doubleValue]) {
            [self performSelectorOnMainThread:@selector(inviteUser:) withObject:peerID waitUntilDone:NO];
        }
    }
}

#pragma mark A nearby peer has stopped advertising
- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID
{
    
}

#pragma mark Browsing did not start due to an error
- (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error
{
    
}

#pragma mark - MCNearbyServiceAdvertiserDelegate
#pragma mark Receive Invitation
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(nullable NSData *)context invitationHandler:(void (^)(BOOL accept, MCSession *session))invitationHandler
{
    invitationHandler(YES, _session);
}

#pragma mark Advertising did not start due to an error.
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didNotStartAdvertisingPeer:(NSError *)error
{
    
}

#pragma mark - MCSessionDelegate
#pragma mark Remote peer changed state.
- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state
{
    switch (state)
    {
        case MCSessionStateNotConnected:
            [self performSelectorOnMainThread:@selector(userLeave:) withObject:peerID waitUntilDone:NO];
            break;
        case MCSessionStateConnecting:
            [self performSelectorOnMainThread:@selector(userConnecting:) withObject:peerID waitUntilDone:NO];
            break;
        case MCSessionStateConnected:
            [self performSelectorOnMainThread:@selector(userJoin:) withObject:peerID waitUntilDone:NO];
            break;
    }
}

#pragma mark Received data from remote peer.
- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID
{
    
}

#pragma markReceived a byte stream from remote peer.
- (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID
{
    
}

#pragma mark Start receiving a resource from remote peer.
- (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress
{
    
}

#pragma mark receiving a resource from remote peer and saved the content
- (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(nullable NSError *)error
{
    
}

#pragma mark Made first contact with peer and have identity information about the remote peer (certificate may be nil).
- (void)session:(MCSession *)session didReceiveCertificate:(nullable NSArray *)certificate fromPeer:(MCPeerID *)peerID certificateHandler:(void (^)(BOOL accept))certificateHandler
{
    if (certificateHandler) {
        certificateHandler(YES);
    }
}

#pragma mark 监听挂起和重新进入程序
#pragma mark 添加监听
- (void)addObserverActive
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:)
                                                 name:UIApplicationWillResignActiveNotification object:nil]; //监听home键挂起.
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:)
                                                 name:UIApplicationDidBecomeActiveNotification object:nil];  //监听重新进入程序.
}

#pragma mark 进入前台
- (void)applicationDidBecomeActive:(UIApplication *)application
{
    DDYInfoLog( @"[LC_UIApplication] Application did become active." );
    _canInvite = YES;
}

#pragma mark 挂起程序
- (void)applicationWillResignActive:(UIApplication *)application
{
    DDYInfoLog( @"[LC_UIApplication] Application will resign active." );
    _canInvite = NO;
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [self stopAdvertise];
}

@end

socket

http://code.google.com/p/cocoaasyncsocket/

你可能感兴趣的:(局域网通讯)