XMPP - 簡(jiǎn)單通訊

這一章節(jié)會(huì)從基礎(chǔ)模塊開(kāi)始逐步實(shí)現(xiàn)私聊功能

XMPP 基礎(chǔ)模塊

基礎(chǔ)模塊

1碑定、 XMPPJID : 身份模塊,在 XMPP 中代表各個(gè)實(shí)體對(duì)象(例如:每一個(gè)用戶督惰、每一個(gè)房間)不傅,所有的消息都是根據(jù) JID 進(jìn)行分發(fā)傳遞的。

完整的格式:user@domain/resource
必要參數(shù)

  • user : 使用 XMPP 服務(wù)的實(shí)體 ID 赏胚,具有唯一性访娶。
  • domain : 服務(wù)器網(wǎng)關(guān)地址,可以是 IP 也可以是 域名

非必要參數(shù)

  • resource : 用來(lái)表示一個(gè)特定的會(huì)話(與某個(gè)設(shè)備)觉阅,連接(與某個(gè)地址)崖疤,或者一個(gè)附屬于某個(gè)節(jié)點(diǎn)ID實(shí)體相關(guān)實(shí)體的對(duì)象(比如多用戶聊天室中的一個(gè)參加者)
/* XMPPFramework 中的初始化方法 */
[XMPPJID jidWithUser:userName domain:domain resource:@"iOS"];

2、XMPPStream : 流模塊(或者可以叫通信模塊)典勇,XMPP 最基本的一個(gè)模塊劫哼,可以用來(lái)加載其他核心(JID)或者拓展模塊(Mut Chat)。通常在客戶端的所有操作都是在此模塊上進(jìn)行的割笙。

/* XMPPFramework 中的初始化方法 */

// 創(chuàng)建對(duì)象
_xmppStream = [[XMPPStream alloc] init];
// 配置服務(wù)器地址
[_xmppStream setHostName:XMPP_HOST];
// 配置服務(wù)器端口號(hào)(可以不配置权烧,默認(rèn)值為 5222)       
[_xmppStream setHostPort:XMPP_PORT];
// 設(shè)置代理及回調(diào)隊(duì)列
[_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
// 設(shè)置心跳包發(fā)送間隔
[_xmppStream setKeepAliveInterval:30];
// 設(shè)置允許后臺(tái)連接
[_xmppStream setEnableBackgroundingOnSocket:YES];

在 App 的一個(gè)完整的生命周期內(nèi)應(yīng)該只初始化一次此對(duì)象眯亦。

3、XMPPReconnect : 斷線重連模塊般码,沒(méi)有什么特殊的功能妻率,但最好加上“遄#可以避免因弱網(wǎng)情況下導(dǎo)致掉線而收不到消息宫静。

/* XMPPFramework 中的初始化方法 */

// 創(chuàng)建對(duì)象
_xmppReconnect = [[XMPPReconnect alloc] initWithDispatchQueue:dispatch_get_main_queue()];
// 啟用自動(dòng)重連
[_xmppReconnect setAutoReconnect:YES];
// 激活模塊,一定要加這句代碼否則不會(huì)執(zhí)行
[_xmppReconnect activate:_xmppStream];

4券时、XMPPStreamManagement : 流管理模塊孤里,這個(gè)模塊其實(shí)應(yīng)該是和 XMPPReconnect 配套使用,作用是在客戶端和服務(wù)器通訊消息中加入一個(gè) </r> 標(biāo)識(shí)橘洞,用來(lái)同步捌袜、確認(rèn)消息是否被成功接收。

/* XMPPFramework 中的初始化方法 */

// 創(chuàng)建流狀態(tài)緩存對(duì)象
_streamStorage = [[XMPPStreamManagementMemoryStorage alloc] init];
// 創(chuàng)建流管理對(duì)象
_xmppStreamManagement = [[XMPPStreamManagement alloc] initWithStorage:_streamStorage];
// 設(shè)置自動(dòng)恢復(fù)
[_xmppStreamManagement setAutoResume:YES];
// 設(shè)置代理和返回隊(duì)列
[_xmppStreamManagement addDelegate:self delegateQueue:dispatch_get_main_queue()];
// 激活模塊
[_xmppStreamManagement activate:_xmppStream];

上面這幾個(gè)模塊是維持 XMPP 正常通訊所必須的震檩,也如大家看到的在 XMPPFramework 中所有模塊的加載都需要一個(gè)步驟:[xxx activate:_xmppStream] 所以當(dāng)大家以后自己添加某個(gè)模塊但沒(méi)有執(zhí)行的時(shí)候可以先檢查一下是否添加了這句代碼琢蛤。

常用代理方法

一個(gè)簡(jiǎn)單的通訊其實(shí)只需要實(shí)現(xiàn) XMPPStream 中以下幾個(gè)常用的代理方法:

// 與服務(wù)器成功建立連接
- (void)xmppStreamDidConnect:(XMPPStream *)sender;
// 注冊(cè) XMPP 賬號(hào)成功
- (void)xmppStreamDidRegister:(XMPPStream *)sender;
// 注冊(cè) XMPP 賬號(hào)失敗 ( error 失敗原因 )
- (void)xmppStream:(XMPPStream *)sender didNotRegister:(NSXMLElement *)error;
// 登錄成功
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender;
// 登錄失敗 ( error 失敗原因 )
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error;

// 收到 <message></message> 類型的消息
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message;

// 發(fā)送消息成功
- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message;
// 發(fā)送消息失敗
- (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error;

簡(jiǎn)單通訊

實(shí)現(xiàn)步驟

1.初始化流


- (void)initalize
{
    // 初始化連接
    _xmppStream = [[XMPPStream alloc] init];
    [_xmppStream setHostName:XMPP_HOST];
    [_xmppStream setHostPort:XMPP_PORT];
    [_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_xmppStream setKeepAliveInterval:30];
    [_xmppStream setEnableBackgroundingOnSocket:YES];
    
    // 接入斷線重連模塊
    _xmppReconnect = [[XMPPReconnect alloc] initWithDispatchQueue:dispatch_get_main_queue()];
    [_xmppReconnect setAutoReconnect:YES];
    [_xmppReconnect activate:_xmppStream];
    
    // 接入流管理模塊
    _streamStorage = [[XMPPStreamManagementMemoryStorage alloc] init];
    _xmppStreamManagement = [[XMPPStreamManagement alloc] initWithStorage:_streamStorage];
    [_xmppStreamManagement setAutoResume:YES];
    [_xmppStreamManagement addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_xmppStreamManagement activate:_xmppStream];
}

2.連接服務(wù)器

- (void)xmppConnect
{
    NSError  *connectError = nil;
    if (![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&connectError]) {
        
        NSLog(@"XMPP Connect With Error : %@", connectError);
    }
}

3.登錄賬號(hào)
還記得上面提到的 XMPPStream 中的提到成功連接服務(wù)器的代理方法么?

- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    // 一定要在成功連接服務(wù)器之后再登錄
    NSError  *error = nil;
    if (![_xmppStream authenticateWithPassword:_myPassword error:&error]) 
    {    
        NSLog(@"Error Authenticate : %@", error);
    }
}

// 登錄成功
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
    NSLog(@"Authenticate Success !");
    
    // 發(fā)送上線消息 ( 如果僅僅登錄成功不發(fā)送上線消息的話是無(wú)法正常聊天的 )
    XMPPPresence  *presence = [XMPPPresence presence];
    [_xmppStream sendElement:presence];
    
    // 啟用流管理
    [_xmppStreamManagement enableStreamManagementWithResumption:YES maxTimeout:0];
}

// 登錄失敗
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error
{
    NSLog(@"Did Not Authenticate : %@", error);
}

4.開(kāi)始聊天

- (void)sendMessage:(NSString *)body to:(XMPPJID *)toJID
{
    // 生成一個(gè)消息的唯一標(biāo)識(shí)
    NSString     *uuId    = [_xmppStream generateUUID];
    // 初始化消息對(duì)象 (type 類型:私聊-@"chat"抛虏,多人聊天-@"groupchat")
    XMPPMessage  *message = [XMPPMessage messageWithType:@"chat" to:toJID];
    // 拼接消息體
    [message addBody:body];
    // 拼接消息 ID
    [message addAttributeWithName:@"id" stringValue:uuId];
    // 發(fā)送消息
    [_xmppStream sendElement:message];
}

- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
    NSLog(@"Did Receive Message : %@", message);
}

- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message
{
    NSLog(@"Send Message Success!");
}

- (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error
{
    NSLog(@"Fail Send Message : %@", error);
}

是不是很簡(jiǎn)單博其,輕輕松松幾步讓你開(kāi)始聊天。

簡(jiǎn)單封裝

IM 作為一個(gè) App 的基本功能迂猴,肯定不會(huì)只在某一個(gè)界面去使用慕淡,那么做一下封裝就顯得很有必要了。下面直接上代碼來(lái)實(shí)現(xiàn)一個(gè)單例:

XMPPManager.h

//
//  XMPPManager.h
//  MusicSampleChat
//
//  Created by LonlyCat on 16/9/21.
//  Copyright ? 2016年 LonlyCat. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "XMPP.h"
#import "XMPPReconnect.h"
#import "XMPPStreamManagement.h"
#import "XMPPStreamManagementMemoryStorage.h"

#define XMPP_PORT     5222
#define XMPP_HOST     192.168.1.103  // 地址自己替換個(gè)可用的

typedef void(^MessageStatusHandle)(BOOL);
typedef void(^MessageReceiveHandle)(XMPPMessage *);

@interface XMPPManager : NSObject

/** 與服務(wù)器通訊鏈接 */
@property (nonatomic, strong) XMPPStream  *xmppStream;

/** XMPP 用戶對(duì)象(自己) */
@property (nonatomic, strong) XMPPJID   *myJID;
/** XMPP 密碼 */
@property (nonatomic, copy  ) NSString  *myPassword;

/** 斷線重連模塊 */
@property (nonatomic, strong) XMPPReconnect          *xmppReconnect;

/** 流管理模塊 */
@property (nonatomic, strong) XMPPStreamManagement   *xmppStreamManagement;
@property (nonatomic, strong) XMPPStreamManagementMemoryStorage  *streamStorage;

/**
 登錄

 @param userName 用戶名
 @param password 密碼
 */
- (void)loginWithName:(NSString *)userName password:(NSString *)password;

/**
 登出
 */
- (void)logOut;

/**
 上線
 */
- (void)goOnline;

/**
 下線
 */
- (void)goOffline;

/**
 發(fā)送消息
 
 @param body          內(nèi)容
 @param toJID         發(fā)送對(duì)象
 @param type          聊天類型 @"chat" / @"groupchat"
 @param statusHandle  消息發(fā)送狀態(tài)回調(diào)  YES 發(fā)送成功  NO 發(fā)送失敗

 @return XMPPMessage
 */
- (XMPPMessage *)sendMessage:(NSString *)body
                          to:(XMPPJID *)toJID
                        type:(NSString *)type
                statusHandle:(MessageStatusHandle)statusHandle;
                   
/**
 添加一個(gè)收到消息的回調(diào)沸毁,key 不能為空

 @param receiveHandle 回調(diào)
 @param key           查詢峰髓、刪除關(guān)鍵字
 
 @return 是否添加成功
 */
- (BOOL)addReceiveHandle:(MessageReceiveHandle)receiveHandle
                  forKey:(NSString *)key;

/**
 根據(jù)關(guān)鍵字移除一個(gè)消息回調(diào)
 */
- (void)removeReceiveHandleByKey:(NSString *)key;

@end

XMPPManager* getXMPPManager();

XMPPManager.m

#import "XMPPManager.h"

@interface XMPPManager ()

@property (nonatomic, strong) NSMutableDictionary   *statusHandleDic;
@property (nonatomic, strong) NSMutableDictionary   *receiveHandlesDic;

@end

@implementation XMPPManager

+ (XMPPManager *)sharedInstance
{
    static XMPPManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        manager = [[XMPPManager alloc] init];
        
        manager.statusHandleDic   = [NSMutableDictionary dictionary];
        manager.receiveHandlesDic = [NSMutableDictionary dictionary];
    });
    return manager;
}

XMPPManager* getXMPPManager()
{
    XMPPManager  *manager = [XMPPManager sharedInstance];
    return manager;
}

#pragma mark - 初始化
- (instancetype)init
{
    self = [super init];
    if (self) {
        
        [self initalize];
        
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(applicationWillTerminate:)
                                                     name:UIApplicationWillTerminateNotification
                                                   object:nil];
        
    return self;
}

- (void)initalize
{
    // 初始化連接
    _xmppStream = [[XMPPStream alloc] init];
    [_xmppStream setHostName:XMPP_HOST];
    [_xmppStream setHostPort:XMPP_PORT];
    [_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_xmppStream setKeepAliveInterval:30];
    [_xmppStream setEnableBackgroundingOnSocket:YES];
    
    // 接入斷線重連模塊
    _xmppReconnect = [[XMPPReconnect alloc] initWithDispatchQueue:dispatch_get_main_queue()];
    [_xmppReconnect setAutoReconnect:YES];
    [_xmppReconnect activate:_xmppStream];
    
    // 接入流管理模塊
    _streamStorage = [[XMPPStreamManagementMemoryStorage alloc] init];
    _xmppStreamManagement = [[XMPPStreamManagement alloc] initWithStorage:_streamStorage];
    [_xmppStreamManagement setAutoResume:YES];
    [_xmppStreamManagement addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_xmppStreamManagement activate:_xmppStream];
}

#pragma mark - 登錄狀態(tài)
- (void)loginWithName:(NSString *)userName password:(NSString *)password
{
    NSLog(@"userName : %@, password : %@",userName, password);
    
    _myPassword = password;
    
    NSString  *domain = XMPP_HOST;
    _myJID = [XMPPJID jidWithUser:userName domain:domain resource:@"iOS"];
    [_xmppStream setMyJID:_myJID];
    
    if ([_xmppStream isConnected]) {
        
        [self xmppAuthen];
    } else {
        
        [self xmppConnect];
    }
}

- (void)logOut
{
    [self goOffline];
    [_xmppStream disconnectAfterSending];
}

- (void)goOnline
{
    XMPPPresence  *presence = [XMPPPresence presence];
    [_xmppStream sendElement:presence];
}

- (void)goOffline
{
    XMPPPresence  *presence = [XMPPPresence presenceWithType:@"unavailable"];
    [_xmppStream sendElement:presence];
}

#pragma mark - 消息發(fā)送 / 接收

- (IMMessageModel *)sendMessage:(NSString *)body
                             to:(XMPPJID *)toJID
                           type:(NSString *)type
                   statusHandle:(MessageStatusHandle)statusHandle
{
    return [self sendMessage:body to:toJID type:type extend:nil statusHandle:statusHandle];
}

- (XMPPMessage *)sendMessage:(NSString *)body
                             to:(XMPPJID *)toJID
                           type:(NSString *)type
                         extend:(DDXMLElement *)extend
                   statusHandle:(MessageStatusHandle)statusHandle
{
    NSString     *uuId    = [_xmppStream generateUUID];
    XMPPMessage  *message = [XMPPMessage messageWithType:type to:toJID];
    [message addBody:body];
    [message addAttributeWithName:@"id" stringValue:uuId];
    [_xmppStream sendElement:message];
    
    [_statusHandleDic setObject:statusHandle forKey:uuId];
    
    return message;
}

- (BOOL)addReceiveHandle:(MessageReceiveHandle)receiveHandle
                  forKey:(NSString *)key
{
    if (receiveHandle && [key isKindOfClass:[NSString class]]) {
        
        [_receiveHandlesDic setObject:receiveHandle forKey:key];
        return YES;
    }
    return NO;
}

- (void)removeReceiveHandleByKey:(NSString *)key
{
    [_receiveHandlesDic removeObjectForKey:key];
}

#pragma mark - XMPPStreamDelegate
#pragma mark connect

- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    NSLog(@"%s", __func__);
    [self xmppAuthen];
}

- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
    NSLog(@"%s", __func__);
}

- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
    NSLog(@"Authenticate Success !");
    
    // 發(fā)送上線消息
    [self goOnline];
    
    // 啟用流管理
    [_xmppStreamManagement enableStreamManagementWithResumption:YES maxTimeout:0];
    
    // 發(fā)送 xmpp 登錄成功通知
    [[NSNotificationCenter defaultCenter] postNotificationName:XMPPLoginSuccess object:nil];
}

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error
{
    NSLog(@"Did Not Authenticate : %@", error);
    [[NSNotificationCenter defaultCenter] postNotificationName:XMPPLoginFail object:error];
}

#pragma mark message
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
    NSLog(@"Did Receive Message : %@", message);
    
    if ([message isErrorMessage]) {
        
        // 警告類消息
        NSLog(@"error message : %@ ", [message errorMessage]);
        return;
    }
    
    NSString  *type = [[message attributeForName:@"type"] stringValue];
    if ([message isChatMessageWithBody]) {
        
        // 私聊消息
        [self sendReceiveMessageNotification:message];
        
    } else if ([message isGroupChatMessageWithBody]) {
        
        // 群聊消息
       [self sendReceiveMessageNotification:message];
    } else if ([type isEqualToString:@"normal"]) {
        
        // 常規(guī)類型(已知邀請(qǐng)群聊為常規(guī)類型,不用在此處理)   
    }
}

- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message
{
    NSLog(@"Did Send Message !");
    
    NSString *key = [message elementID];
    [self handleMessageStatusByKey:key result:YES];
}

- (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error
{
    NSLog(@"Fail Send Message : %@", error);
    NSString *key = [message elementID];
    [self handleMessageStatusByKey:key result:NO];
}

#pragma mark - 私有方法
- (void)xmppConnect
{
    NSError  *connectError = nil;
    if (![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&connectError]) {
        
        NSLog(@"XMPP Connect With Error : %@", connectError);
    }
}

- (void)xmppAuthen
{
    NSError  *error = nil;
    if (![_xmppStream authenticateWithPassword:_myPassword error:&error]) {
        
        NSLog(@"Error Authenticate : %@", error);
        [[NSNotificationCenter defaultCenter] postNotificationName:XMPPLoginFail object:error];
    }
}

- (void)handleMessageStatusByKey:(NSString *)string result:(BOOL)result
{
    MessageStatusHandle  statusHandle = [_statusHandleDic objectForKey:string];
    if (statusHandle) {
        
        statusHandle(result);
        [_statusHandleDic removeObjectForKey:string];
        statusHandle = nil;
    }
}

- (void)sendReceiveMessageNotification:(XMPPMessage *)message
{
    NSArray  *receiveHandleArray = [_receiveHandlesDic allValues];
    [receiveHandleArray enumerateObjectsUsingBlock:^(MessageReceiveHandle  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
    {
        obj(message);
    }];
}

#pragma mark -- terminate
/**
 *  申請(qǐng)后臺(tái)時(shí)間來(lái)清理下線的任務(wù)
 */
- (void)applicationWillTerminate:(NSNotification *)noti
{
    [self goOffline];
    [_xmppStream disconnectAfterSending];
}

@end

有了這個(gè)封裝好的單例我們就可以開(kāi)始聊天了( 當(dāng)然你首先得有一個(gè)搭好的 XMPP 服務(wù)器 )息尺。

結(jié)尾

如果你是有一定經(jīng)驗(yàn)的開(kāi)發(fā)者携兵,看完之后肯定能發(fā)現(xiàn)倆個(gè)問(wèn)題:

  • XMPPMessage 的傳值使用的是原始格式
  • 消息對(duì)象沒(méi)有進(jìn)行存儲(chǔ)

這倆個(gè)問(wèn)題對(duì)應(yīng)的能力應(yīng)該是 iOS 開(kāi)發(fā)者的基本功,所以我希望你能自己修改下上面的代碼來(lái)解決搂誉。

PS : XMPPFramework 框架本身是實(shí)現(xiàn)了對(duì) XMPPMessage 的存儲(chǔ)徐紧,實(shí)現(xiàn)方式是使用的 CoreData 。雖然方便了開(kāi)發(fā)者進(jìn)行開(kāi)發(fā)炭懊,但對(duì)于某些需求的定制造成了局限( 比如你想保存用戶的聊天記錄到服務(wù)器 )并级。所以在這里我還是推薦大家使用 realm 或者 SQLite 來(lái)實(shí)現(xiàn)數(shù)據(jù)的存儲(chǔ)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市侮腹,隨后出現(xiàn)的幾起案子嘲碧,更是在濱河造成了極大的恐慌,老刑警劉巖父阻,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愈涩,死亡現(xiàn)場(chǎng)離奇詭異望抽,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)履婉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)糠聪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人谐鼎,你說(shuō)我怎么就攤上這事∪せ荩” “怎么了狸棍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)味悄。 經(jīng)常有香客問(wèn)我草戈,道長(zhǎng),這世上最難降的妖魔是什么侍瑟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任唐片,我火速辦了婚禮,結(jié)果婚禮上涨颜,老公的妹妹穿的比我還像新娘费韭。我一直安慰自己,他們只是感情好庭瑰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布星持。 她就那樣靜靜地躺著,像睡著了一般弹灭。 火紅的嫁衣襯著肌膚如雪督暂。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,084評(píng)論 1 291
  • 那天穷吮,我揣著相機(jī)與錄音逻翁,去河邊找鬼。 笑死捡鱼,一個(gè)胖子當(dāng)著我的面吹牛八回,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播堰汉,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼辽社,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了翘鸭?” 一聲冷哼從身側(cè)響起滴铅,我...
    開(kāi)封第一講書(shū)人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎就乓,沒(méi)想到半個(gè)月后汉匙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拱烁,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年噩翠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了戏自。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伤锚,死狀恐怖擅笔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屯援,我是刑警寧澤猛们,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站狞洋,受9級(jí)特大地震影響弯淘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吉懊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一庐橙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧借嗽,春花似錦态鳖、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至甲锡,卻和暖如春兆蕉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缤沦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工虎韵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缸废。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓包蓝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親企量。 傳聞我的和親對(duì)象是個(gè)殘疾皇子测萎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 前面關(guān)于即時(shí)通訊基礎(chǔ)Socket,大家學(xué)習(xí)使用XMPP之前可以先看看即時(shí)通訊系列之Socket簡(jiǎn)介 前言 前段時(shí)間...
    音符上的碼字員閱讀 4,216評(píng)論 3 16
  • 關(guān)于XMPP最權(quán)威的講解:http://www.jabbercn.org/RFC3920(這個(gè)才是最權(quán)威的,下面文...
    隨風(fēng)飄蕩的小逗逼閱讀 1,481評(píng)論 1 5
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)届巩、插件硅瞧、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,066評(píng)論 4 62
  • 文/斯云 只想天快點(diǎn)亮孤寂長(zhǎng)夜禁錮著無(wú)眠人的心 深夜總是喚起那個(gè)我一直不愿醒來(lái)的夢(mèng)夢(mèng)里有夏日的陽(yáng)光 只是此刻夢(mèng)已成...
    斯云閱讀 93評(píng)論 12 3
  • 1 什么是Dalvik虛擬機(jī) Dalvik是Google公司自己設(shè)計(jì)用于Android平臺(tái)的Java虛擬機(jī),它是A...
    Android看海閱讀 4,776評(píng)論 0 7