局域网通讯

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 <Foundation/Foundation.h>
#import <MultipeerConnectivity/MultipeerConnectivity.h>

@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 ()<MCSessionDelegate, MCNearbyServiceBrowserDelegate, MCNearbyServiceAdvertiserDelegate>
/** 当前会话 */
@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<NSString *, NSString *> *)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/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,121评论 29 470
  • 前言# 什么是蓝牙?#### 随着蓝牙低功耗技术BLE(Bluetooth Low Energy)的发展,蓝牙技术...
    maybe_mylove阅读 959评论 1 5
  • 1.OC里用到集合类是什么? 基本类型为:NSArray,NSSet以及NSDictionary 可变类型为:NS...
    轻皱眉头浅忧思阅读 1,359评论 0 3
  • 来自网络 序言 目前形势,参加到iOS队伍的人是越来越多,甚至已经到供过于求了。今年,找过工作人可能会更深刻地体会...
    用心在飞阅读 823评论 5 4
  • 新雪轻飘旧雪间,夜来闲话小炉前。 茶烟缭绕诗怀动,烛影参差酒兴酣。 嗔赋梅花开异地,醉吟松柏绿吾山。 北风悄住晨昏...
    田梦_阅读 529评论 36 46