XMPP之注册登录【新萄京娱乐app】,于是有了XMP

作者:新闻中心
  1. 下载 XMPPFramework 框架

    GitHub: XMPPFramework

  2. 导入依赖框架

    • CocoaLumberjack : 日志框架

    • CocoaAsyncSocket : 底层网络框架需要添加 CFNetwork & Security 框架依赖(XCode 6 无需导入)

    • KissXML : XML解析框架需要添加libxml2.dylib框架依赖需要指定如下编译选项:Other Linker Flags = -lxml2Header Search Paths = /usr/include/libxml2

    • libidn

  3. 导入一下文件夹

    • Authentication
    • Categories
    • Core
    • Utilities
    • 添加依赖:libresolv.dylib
  4. 导入XMPP扩展框架

    • Extensions 文件夹
    • 导入 Sample_XMPPFramework.h 并更名为:XMPPFramework.h
    • 添加PCH文件:
    • Add New File -> Other -> PCH文件
    • 添加 #import<UIKit/UIKit.h> 到PCH文件中
    • 设置编译选项,Build Settings -> Precompile prefix Header 选择Yes
    • 设置编译选项,Build Settings -> Prefix Header 设置PCH文件名:“项目名/“PCH文件名”

即时通讯

新萄京娱乐app 1

屏幕快照 2017-04-24 上午10.43.21.png

XMPP

  • 需要在本地配置好openfire服务器

实现用户登录的步骤如下:

1. 实例化XMPPStream并设置代理,同时添加代理到工作队列 2. 使用JID连接至服务器,默认端口为5222,JID字符串中需要包含服务器的域名 3. 在完成连接的代理方法中验证用户密码,连接完成后XMPPStream的isConnect属性为YES 4. 在验证代理方法中判断用户是否登录成功 5. 上线或者下线成功后,向服务器发送Presence数据,以更新用户在服务器的状态 

1.XMPP(http://xmpp.org)

The Extensible Messaging and PresenceProtocol(可扩展通讯协议)

将XMPP框架拖入工程,一拖入就编译的话是会报错的,需要配置一下

新萄京娱乐app 2

屏幕快照 2016-03-09 12.46.19.png

  • 配置一些东西
    1:添加libxml2.tab框架

新萄京娱乐app 3

屏幕快照 2016-03-09 09.15.03.png

2:在Build Settings 里面搜索 other Linker Flags 添加- lxml2

新萄京娱乐app 4

屏幕快照 2016-03-09 09.29.52.png

3:还是在Build Settings 里面搜索 Header Search Paths 里面添加 /usr/include/libxml2

新萄京娱乐app 5

屏幕快照 2016-03-09 09.31.12.png

4:添加libresolv.tab 框架

新萄京娱乐app 6

屏幕快照 2016-03-09 12.51.07.png

5:添加一个PCH文件 名字随意

新萄京娱乐app 7

6:在Build Settings 里面搜索 prefix 配置PCH路径 路径格式是:工程名/PCH名

新萄京娱乐app 8

7:在pch文件里面导入 #import <UIKit/UIKit.h> 编译发现还有一个错误,点击错误将错误的那一行注释掉就是了,再编译就没问题了

新萄京娱乐app 9

屏幕快照 2016-03-09 09.25.30.png

各部分的实现代码如下:

  • 初始化 XMPPStream 并设置代理:

    -setupXMPPStream{ _xmppStream = [[XMPPStream alloc] init]; // 设置代理 [_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];}
    
  • 连接到服务器

    -connectToHost{ NSLog(@"开始连接到服务器"); if (!_xmppStream) { [self setupXMPPStream]; } // 设置登录用户JID //resource 标识用户登录的客户端 iphone android XMPPJID *myJID = [XMPPJID jidWithUser:@"aaa" domain:@"bourne-mbp.local" resource:@"iphone" ]; _xmppStream.myJID = myJID; // 设置服务器域名 _xmppStream.hostName = @"bourne-mbp.local";//不仅可以是域名,还可是IP地址 // 设置端口 如果服务器端口是5222,可以省略 _xmppStream.hostPort = 5222; // 连接 NSError *err = nil; if(![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&err]){ NSLog(@"%@",err); } }
    
  • 连接成功后发送密码验证

    -sendPwdToHost{ NSLog(@"再发送密码授权"); NSError *err = nil; [_xmppStream authenticateWithPassword:@"123456" error:&err]; if  { NSLog(@"%@",err); }}
    
  • 授权成功后,发送 在线 消息

    #pragma mark 授权成功后,发送"在线" 消息-sendOnlineToHost{ NSLog(@"发送 在线 消息"); XMPPPresence *presence = [XMPPPresence presence]; NSLog(@"%@",presence); [_xmppStream sendElement:presence];}
    

1.概念

XMPP是一种基于标准通用标记语言的子集XML的协议,它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能

补充:

新萄京娱乐app 10

屏幕快照 2017-04-24 上午11.09.01.png

  • XMPP是一个基于个Socket通过的网络协议,目的是为了保存长连接,以实现即时通讯功能
  • XMPP的客户端是使用一个XMPPFramework框架实现
  • XMPP的服务器是使用Openfire,一个开源的服务器
  • 客户端获取到服务器发送过来的好友消息,客户端需要对XML进行解析,使用的解析框架的KissXML框架,而不是NSXMLParser/GDataXML
  • XMPP是一个即时通讯的协议,它规范了用于即时通信在网络上数据传输格式的,比如登录,获取好友列表等等的格式。XMPP在网络传输的数据是XML格式。比如登录:把用户名和密码放在xml的标签中,传输到服务器

新萄京娱乐app 11

屏幕快照 2017-04-24 上午11.11.29.png

1.XMPP是一个即时通讯的协议,它规范了用于即时通讯在网络上传输数据的格式,XMPP在网络上传输的格式为XML格式,eg:登录,把用户名和密码放到XML的标签中,传输到服务器

接下来新建几个实体文件夹 分别是 登录模块 注册模块 好友模块 聊天模块

新萄京娱乐app 12

屏幕快照 2016-03-09 13.04.32.png

  • 考虑到登录 注册 等最好封装为一个类让外界直接使用就好了,所以创建一个专门放置工具类的文件夹,先在里面创建一个继承自NSObject的类 名字随意

新萄京娱乐app 13

屏幕快照 2016-03-09 13.07.21.png

  • 今天登录注册用到的XMPPStreamDelegate代理方法
    XMPP的登录,注册主要就是在这些代理方法里面写代码

在连接服务器成功以后的代理方法

  • (void)xmppStreamDidConnect:(XMPPStream *)sender;

在连接服务器失败以后的代理方法

  • (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender;

密码验证成功的代理方法,肯定是要在连接了服务器以后才能验证密码的了

  • (void)xmppStreamDidAuthenticate:(XMPPStream *)sender;

密码验证失败的代理方法

  • (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error;

注册成功的代理方法 注册也得是在连接了服务器以后才能进行

  • (void)xmppStreamDidRegister:(XMPPStream *)sender

注册失败的代理方法

  • (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error

需要实现的几个代理方法

#pragma mark 与主机连接成功-xmppStreamDidConnect:(XMPPStream *)sender{ NSLog(@"与主机连接成功"); // 主机连接成功后,发送密码进行授权 [self sendPwdToHost];}#pragma mark 与主机断开连接-xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{ // 如果有错误,代表连接失败 NSLog(@"与主机断开连接 %@",error);}#pragma mark 授权成功-xmppStreamDidAuthenticate:(XMPPStream *)sender{ NSLog; [self sendOnlineToHost];}#pragma mark 授权失败-xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{ NSLog(@"授权失败 %@",error);}

2.XMPP的使用

讲解以下内容:
1.导入XMPP框架
2.登录 & 注销
3.注册
4.用户信息
5.好友
6.消息
7.文件传送(图片,音频)

2.XMPP是一个基于socket 的网络协议,目的是为了保持长连接,以实现即时通讯

开始撸代码

  • 首先在XMPPManager类里面先导入XMPP框架,再创建单例方法,登录方法,注册方法,注销方法 还需要遵守XMPPStreamDelegate协议,并且定义一个XMPPStream属性暴露在.h文件中
  • 下面就是头文件的所有代码,今天只记录登录,注册,以及注销

新萄京娱乐app 14

屏幕快照 2016-03-09 15.21.39.png

  • 接下来到.m里面实现这些方法

  • 因为我们要在代理方法里面获取到登录或者注册时的用户名与密码,所以需要用属性记录下来,在XMPPManager里面添加一个扩展,扩展里面添加用户名与密码属性,并且因为不管是登录还是注册都需要验证密码,并且都是在连接服务器成功的代理方法里面分别调用登录/注册的密码验证方法,所以 最好定义一个枚举值来表示是要登录还是注册

  • 1:定义枚举以及添加扩展,在扩展里面添加 用户名,密码,区别登录还是注册属性

新萄京娱乐app 15

屏幕快照 2016-03-09 17.00.34.png

  • 2:实现单例方法 单例的写法有好几种,并且差不多都是统一的格式

新萄京娱乐app 16

屏幕快照 2016-03-09 15.43.59.png

  • 3 重写init初始化方法因为要在外界一调用单例方法的时候就已经建立好通信管道(XMPPStream),在这个方法里面只要初始化XMPPStream,以及配置好服务器地址,端口,以及设置代理人就行(不要忘记遵守XMPPStreamDelegate协议)

新萄京娱乐app 17

屏幕快照 2016-03-09 15.52.50.png

新萄京娱乐app 18

屏幕快照 2016-03-09 16.12.48.png

  • 4 因为连接服务器的步骤多个地方都需要用到,所以专门抽取一个方法来连接服务器,这个方法需要做的事情是,先判断当前是否是处于连接的状态,如果是则先让它与服务器断开连接,然后在发送连接的消息给服务器,并且实现注销的方法,因为连接服务器的时候需要用到

新萄京娱乐app 19

屏幕快照 2016-03-09 16.11.05.png

  • 5 准备工作做好了,开始实现登陆方法,登陆需要实现的是先创建一个XMPPJID(类似于用户名吧),再把JID与通信管道绑定,调用连接服务器的方法,给用户名,密码属性等赋值 接着就看是否连接成功了,连接成功会走连接成功的方法,失败则走失败的方法(废话),假设连接成功了则在成功的的方法里面调用验证密码的方法,此时也是两个状态,要么密码验证成功,要么失败!假设成功,则向服务器发送上线的消息,这时我们在openfire里面就能看到我们登陆的用户的在线状态了,说白了这写都是在XMPP提供的各种状态代理方法里面写一点代码就好了

新萄京娱乐app 20

屏幕快照 2016-03-09 17.25.48.png

  • 登陆就算是做完了,当然了这是很不完整的,比如我们要在登陆成功以后将用户名和密码存储起来,这样下次就不需要再次登陆了,并且登陆成功以后需要调整到主界面了,这里并没有做

  • 6 注册 注册的步骤跟登陆差不多,同样是创建一个XMPPJID 然后连接服务器,看服务器是否连接成功,成功则验证密码,密码验证成功以后,则注册成功了.也是有两个代理方法 需要完善的是,要判断用户是否存在了,存在则不允许注册,不过这里也没有做判断

新萄京娱乐app 21

屏幕快照 2016-03-09 17.46.13.png

  • 这些方法实现了只需要在对应的控制器中调用即可 真正开发来说,在登陆成功以后应该发出一个通知切换控制器,并且保存下用户名以及密码,这样下次就不需要再进行登陆了

注销登录

  • 发送 离线 信息
  • 断开连接
-logout{ // 1." 发送 `离线` 消息" XMPPPresence *offline = [XMPPPresence presenceWithType:@"unavailable"]; [_xmppStream sendElement:offline]; // 2. 与服务器断开连接 [_xmppStream disconnect];}
  • 与登录一样,首先发送帐号建立连接

  • 连接成功后,发送注册的密码

  • 注册成功后,框架会通知代理

1.导入XMPP框架

1.1 下载XMPPFramework框架
GitHub: XMPPFramework

1.2导入依赖框架

新萄京娱乐app 22

屏幕快照 2017-04-24 上午9.50.19.png

1.3导入一下文件夹

新萄京娱乐app 23

屏幕快照 2017-04-24 上午9.52.17.png

**1.4导入XMPP扩展框架 **

新萄京娱乐app 24

屏幕快照 2017-04-24 上午9.52.48.png

3.XMPP的客户端是使用一个XMPPFramework框架实现的

今天的任务完成,明天写添加好友,删除好友,以及实现聊天(界面丑陋)

实现以下代理方法

- xmppStreamDidRegister:(XMPPStream *)sender { NSLog; if (_resultBlock) { _resultBlock(BWXMPPLoginResultSuccessed); }}- xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error { if (_resultBlock) { _resultBlock(BWXMPPLoginResultFailure); }}

XMPP是面向模块的,每一个大的动能都属于某一个模块,需要使用时,就在头文件中将其暴露出来。

  1. 工作原理:

    添加用户信息模块之后,XMPPFramework框架会自动从服务器获取用户信息,并使用CoreData保存到本地的数据库中,使用XMPPvCardTempModule可以访问数据

  2. XMPPFramework.h中将以下的头文件前面的注释去掉:

    // 电子名片模块#import "XMPPvCardTempModule.h"#import "XMPPvCardCoreDataStorage.h"// 头像模块#import "XMPPvCardAvatarModule.h"
    
  3. 初始化模块

    //添加电子名片模块_vCardStorage = [XMPPvCardCoreDataStorage sharedInstance];_vCard = [[XMPPvCardTempModule alloc] initWithvCardStorage:_vCardStorage]; //激活[_vCard activate:_xmppStream]; //添加头像模块_avatar = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_vCard];[_avatar activate:_xmppStream];
    
  4. 应用

    //xmpp提供了一个方法,直接获取个人信息XMPPvCardTemp *myVCard =[WCXMPPTool sharedWCXMPPTool].vCard.myvCardTemp; // 设置头像if(myVCard.photo){ self.haedView.image = [UIImage imageWithData:myVCard.photo];} // 设置昵称self.nicknameLabel.text = myVCard.nickname;
    

与用户信息模块相似,添加相应的好友花名册模块即可。

  1. 头文件

    // 花名册模块#import "XMPPRoster.h"#import "XMPPRosterCoreDataStorage.h"
    
  2. 初始化

    // 添加花名册模块_rosterStorage = [[XMPPRosterCoreDataStorage alloc] init];_roster = [[XMPPRoster alloc] initWithRosterStorage:_rosterStorage];[_roster activate:_xmppStream];
    
  3. 应用

    //使用CoreData获取数据// 1.上下文【关联到数据库XMPPRoster.sqlite】NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].rosterStorage.mainThreadManagedObjectContext; // 2.FetchRequestNSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"]; // 3.设置过滤和排序// 过滤当前登录用户的好友NSString *jid = [WCUserInfo sharedWCUserInfo].jid;NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@",jid];request.predicate = pre; //排序NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"displayName" ascending:YES];request.sortDescriptors = @[sort]; // 4.执行请求获取数据_resultsContrl = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil]; _resultsContrl.delegate = self; NSError *err = nil;[_resultsContrl performFetch:&err];if  { WCLog(@"%@",err);}
    
    • 注意:使用NSFetchedResultsController并设置代理,如果数据库的内容发生了变化,这个类会自动通知代理,就可以设置界面的数据,做到实时更新。
  1. 头文件

    • 注意:这几个头文件没在XMPPFramework.h文件中,需要自己添加
    // 消息模块#import "XMPPMessageArchiving.h"#import "XMPPMessageArchivingCoreDataStorage.h"
    
  2. 初始化

    // 添加聊天模块_msgStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init];_msgArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:_msgStorage];[_msgArchiving activate:_xmppStream]; 
    
  3. 应用

// 上下文NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].msgStorage.mainThreadManagedObjectContext;// 请求对象NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];

// 过滤、排序// 1.当前登录用户的JID的消息// 2.好友的Jid的消息NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@ AND bareJidStr = %@",[WCUserInfo sharedWCUserInfo].jid,self.friendJid.bare];NSLog(@"%@",pre);request.predicate = pre;// 时间升序NSSortDescriptor *timeSort = [NSSortDescriptor sortDescriptorWithKey:@"timestamp" ascending:YES];request.sortDescriptors = @[timeSort];// 查询_resultsContr = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];NSError *err = nil;// 代理_resultsContr.delegate = self;[_resultsContr performFetch:&err];NSLog(@"%@",_resultsContr.fetchedObjects);if  { WCLog(@"%@",err);}```
  1. 原理分析

    • 使用base64将文件转化为字符串,然后再通过XMPPFramework传输。
    • 先将文件上传到服务器,再将文件网址通过XMPPFramework转输给好友,好友收到后再自行下载文件。
  1. 难点解析

    • 需要给XMPPFramework新萄京娱乐app,的``数据体添加一个信息类型字段。
    XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid]; //text 纯文本//image 图片[msg addAttributeWithName:@"bodyType" stringValue:bodyType]; // 设置内容[msg addBody:text];NSLog(@"%@",msg);[[WCXMPPTool sharedWCXMPPTool].xmppStream sendElement:msg];
    
    • 根据消息类型解析消息
    XMPPMessageArchiving_Message_CoreDataObject *msg = _resultsContr.fetchedObjects[indexPath.row];// 判断是图片还是纯文本NSString *chatType = [msg.message attributeStringValueForName:@"bodyType"];if ([chatType isEqualToString:@"image"]) { //下图片显示 [cell.imageView sd_setImageWithURL:[NSURL URLWithString:msg.body] placeholderImage:[UIImage imageNamed:@"DefaultProfileHead_qq"]]; cell.textLabel.text = nil;} else if ([chatType isEqualToString:@"text"]){ //显示消息 if ([msg.outgoing boolValue]) {//自己发 cell.textLabel.text = msg.body; }else{//别人发的 cell.textLabel.text = msg.body; } cell.imageView.image = nil;}
    

2.登录&注销

2.1 实现用户登录的步骤如下:

1. 实例化XMPPStream并设置代理,同时添加代理到工作队列      

2. 使用JID连接至服务器,默认端口为5222,JID字符串中需要包含服务器的域名     

3. 在完成连接的代理方法中验证用户密码,连接完成后XMPPStream的isConnect属性为YES     

4. 在验证代理方法中判断用户是否登录成功        

5. 上线或者下线成功后,向服务器发送Presence数据,以更新用户在服务器的状态

2.2 各部分的实现代码如下:

  • 初始化 XMPPStream 并设置代理:
-(void)setupXMPPStream{

   _xmppStream = [[XMPPStream alloc] init];

   // 设置代理
   [_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
}
  • 连接到服务器
-(void)connectToHost{
   NSLog(@"开始连接到服务器");
   if (!_xmppStream) {
       [self setupXMPPStream];
   }

   // 设置登录用户JID
   //resource 标识用户登录的客户端 iphone android

   XMPPJID *myJID = [XMPPJID jidWithUser:@"aaa" domain:@"bourne-mbp.local" resource:@"iphone" ];
   _xmppStream.myJID = myJID;

   // 设置服务器域名
   _xmppStream.hostName = @"bourne-mbp.local";//不仅可以是域名,还可是IP地址

   // 设置端口 如果服务器端口是5222,可以省略
   _xmppStream.hostPort = 5222;

   // 连接
   NSError *err = nil;
   if(![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&err]){
       NSLog(@"%@",err);
   }

}
  • 连接成功后发送密码验证
-(void)sendPwdToHost{
   NSLog(@"再发送密码授权");
   NSError *err = nil;
   [_xmppStream authenticateWithPassword:@"123456" error:&err];
   if (err) {
       NSLog(@"%@",err);
   }
}
  • 授权成功后,发送 在线 消息
#pragma mark  授权成功后,发送"在线" 消息
-(void)sendOnlineToHost{

   NSLog(@"发送 在线 消息");
   XMPPPresence *presence = [XMPPPresence presence];
   NSLog(@"%@",presence);

   [_xmppStream sendElement:presence];
}

2.3 需要实现的几个代理方法

#pragma mark 与主机连接成功
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
    NSLog(@"与主机连接成功");

    // 主机连接成功后,发送密码进行授权
    [self sendPwdToHost];
}

#pragma mark  与主机断开连接
-(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{
    // 如果有错误,代表连接失败
    NSLog(@"与主机断开连接 %@",error);
}

#pragma mark 授权成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
    NSLog(@"授权成功");

    [self sendOnlineToHost];
}

#pragma mark 授权失败
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
    NSLog(@"授权失败 %@",error);
}

2.4 注销登录

  • 发送离线信息
  • 断开连接
-(void)logout{
    // 1." 发送 `离线` 消息"
    XMPPPresence *offline = [XMPPPresence presenceWithType:@"unavailable"];
    [_xmppStream sendElement:offline];

    // 2. 与服务器断开连接
    [_xmppStream disconnect];
}

4.XMPP的服务器是使用Openfire,一个开源的服务器

声明

  1. 澳门葡京国际,以上内容属于本人整理的笔记,如有错误的地方希望能告诉我,大家共同进步。

  2. 以上内容有些段落或语句可能是本人从其他地方Copy而来,如有侵权,请及时告诉我。

3.注册

  • 与登录一样,首先发送帐号建立连接
  • 连接成功后,发送注册的密码
  • 注册成功后,框架会通知代理

实现以下代理方法

- (void)xmppStreamDidRegister:(XMPPStream *)sender {
    NSLog(@"注册成功");

    if (_resultBlock) {
        _resultBlock(BWXMPPLoginResultSuccessed);
    }
}

- (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error {
    if (_resultBlock) {
        _resultBlock(BWXMPPLoginResultFailure);
    }
}

客户端获取服务器发送过来的好友消息,客户端需要对XML进行解析,使用的解析框架为KissXML   框架,而不是NSXMLParser(苹果的解析)/GDataXML(谷歌的解析)

4 .用户信息

XMPP是面向模块的,每一个大的动能都属于某一个模块,需要使用时,就在头文件中将其暴露出来(原本是被注释了的)

1.工作原理:

添加用户信息模块之后,XMPPFramework框架会自动从服务器获取用户信息,并使用CoreData保存到本地的数据库中,使用XMPPvCardTempModule可以访问数据

2.在XMPPFramework.h中将以下的头文件前面的注释去掉:

// 电子名片模块
#import "XMPPvCardTempModule.h"
#import "XMPPvCardCoreDataStorage.h"

// 头像模块
#import "XMPPvCardAvatarModule.h"

3.初始化模块

//添加电子名片模块
_vCardStorage = [XMPPvCardCoreDataStorage sharedInstance];
_vCard = [[XMPPvCardTempModule alloc] initWithvCardStorage:_vCardStorage];

//激活
[_vCard activate:_xmppStream];

//添加头像模块
_avatar = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_vCard];
[_avatar activate:_xmppStream];

4.应用

//xmpp提供了一个方法,直接获取个人信息
XMPPvCardTemp *myVCard =[WCXMPPTool sharedWCXMPPTool].vCard.myvCardTemp;

// 设置头像
if(myVCard.photo){
 self.haedView.image = [UIImage imageWithData:myVCard.photo];
}

// 设置昵称
self.nicknameLabel.text = myVCard.nickname;

缺点:

5.好友

与用户信息模块相似,添加相应的好友花名册模块即可。

1.头文件

// 花名册模块
#import "XMPPRoster.h"
#import "XMPPRosterCoreDataStorage.h"

2.初始化

// 添加花名册模块【获取好友列表】
_rosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
_roster = [[XMPPRoster alloc] initWithRosterStorage:_rosterStorage];
[_roster activate:_xmppStream];

3.应用

//使用CoreData获取数据
// 1.上下文【关联到数据库XMPPRoster.sqlite】
NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].rosterStorage.mainThreadManagedObjectContext;

// 2.FetchRequest【查哪张表】
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"];

// 3.设置过滤和排序
// 过滤当前登录用户的好友
NSString *jid = [WCUserInfo sharedWCUserInfo].jid;
NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@",jid];
request.predicate = pre;

//排序
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"displayName" ascending:YES];
request.sortDescriptors = @[sort];

// 4.执行请求获取数据
_resultsContrl = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];

_resultsContrl.delegate = self;

NSError *err = nil;
[_resultsContrl performFetch:&err];
if (err) {
 WCLog(@"%@",err);
}

注意:使用NSFetchedResultsController并设置代理,如果数据库的内容发生了变化,这个类会自动通知代理,就可以设置界面的数据,做到实时更新。

1.发送图片、语音、文档比较麻烦

6.消息

1.头文件

  • 注意:这几个头文件没在XMPPFramework.h文件中,需要自己添加
// 消息模块
#import "XMPPMessageArchiving.h"
#import "XMPPMessageArchivingCoreDataStorage.h"

2.初始化

// 添加聊天模块
_msgStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init];
_msgArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:_msgStorage];
[_msgArchiving activate:_xmppStream];

3.应用

// 上下文
NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].msgStorage.mainThreadManagedObjectContext;
// 请求对象
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];

// 过滤、排序
// 1.当前登录用户的JID的消息
// 2.好友的Jid的消息
NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@ AND bareJidStr = %@",[WCUserInfo sharedWCUserInfo].jid,self.friendJid.bare];
NSLog(@"%@",pre);
request.predicate = pre;

// 时间升序
NSSortDescriptor *timeSort = [NSSortDescriptor sortDescriptorWithKey:@"timestamp" ascending:YES];
request.sortDescriptors = @[timeSort];

// 查询
_resultsContr = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];

NSError *err = nil;
// 代理
_resultsContr.delegate = self;

[_resultsContr performFetch:&err];

NSLog(@"%@",_resultsContr.fetchedObjects);
if (err) {
  WCLog(@"%@",err);
}

2.没有提供发送附件的功能,需要自己实现

7.文件传送(图片、音频)

1.原理分析

  • 使用base64将文件转化为字符串,然后再通过XMPPFramework传输。
  • 先将文件上传到服务器,再将文件网址通过XMPPFramework转输给好友,好友收到后再自行下载文件。

2.难点解析

  • 需要给XMPPFramework的``数据体添加一个信息类型字段。
XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid];

//text 纯文本
//image 图片
[msg addAttributeWithName:@"bodyType" stringValue:bodyType];

// 设置内容
[msg addBody:text];
NSLog(@"%@",msg);
[[WCXMPPTool sharedWCXMPPTool].xmppStream sendElement:msg];
  • 根据消息类型解析消息
XMPPMessageArchiving_Message_CoreDataObject *msg = _resultsContr.fetchedObjects[indexPath.row];

// 判断是图片还是纯文本
NSString *chatType = [msg.message attributeStringValueForName:@"bodyType"];
if ([chatType isEqualToString:@"image"]) {
    //下图片显示
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:msg.body] placeholderImage:[UIImage imageNamed:@"DefaultProfileHead_qq"]];
    cell.textLabel.text = nil;
} else if ([chatType isEqualToString:@"text"]){

    //显示消息
    if ([msg.outgoing boolValue]) {//自己发
        cell.textLabel.text = msg.body;
    }else{//别人发的
        cell.textLabel.text = msg.body;
    }

    cell.imageView.image = nil;
}

新萄京娱乐app 25

屏幕快照 2017-04-24 下午2.06.01.png

实现方法:把文件上传到服务器,让后把保存的地址发送给好友

2.环信

环信

1.概念

新萄京娱乐app 26

屏幕快照 2017-04-24 上午11.13.17.png

新萄京娱乐app 27

1429890-b745aac54b315572.png

新萄京娱乐app 28

屏幕快照 2017-04-24 下午2.04.35.png

1.环信是一个即时通讯的服务商

2.集成环信的准备工作

  • 注册成为环信开发者:http://www.easemob.com
  • 在开发者后台创建APP获取Key
  • 官方SDKDEMO:http://www.easemob.com/docs/ios/IOSSDKPrepare/
  • 澳门新萄京娱乐场官网,下载环信SDK:http://www.easemob.com/download/im
  • 推送证书的创建(如果不需要离线推送消息可不必创建)

2.环信使用的是XMPP协议,它是对XMPP协议的二次开发,对服务器Openfire和客户端进行功能性模块的增加与SDK的封装,环信的本质还是使用XMPP,基于Socket的网络协议

3.有关推送证书的内容

  • 3.1 创建推送证书的步骤:

step1. 打开[苹果开发者网站:https://developer.apple.com/cn/]

新萄京娱乐app 29

1400051-faf55bf12d2e19b6.jpg

step2. 从Member Center进入Certificates, Identifiers & Profiles

新萄京娱乐app 30

1400051-31c93383be646f4f.png

step3. 选择要制作的推送证书

新萄京娱乐app 31

1400051-c8fe1f3ce910ce87.png

对于开发环境(sandbox)的推送证书, 请选择 Apple Push Notification service SSL (Sandbox)
对于生产环境(production)的推送证书, 请选择 Apple Push Notification service SSL (Production)

step4. 选择对应的APP ID (环信示例使用ChatDemoUI, 所以此处选择com.easemob.enterprise.demo.ui)

新萄京娱乐app 32

1400051-1801b88869b66e3e.png

step5. 根据Certificate Assistant的提示, 创建Certificate Request

新萄京娱乐app 33

1400051-5f6dee6e50845490.png

step6. 上传上一步中创建的Certificate Request文件

新萄京娱乐app 34

1400051-64a6679b846fd796.png

step7. 上传完毕后, 推送证书就被正确生成了, 之后我们下载下来这个证书, 并双击导入系统

新萄京娱乐app 35

1400051-b659eee552ef787a.png

  • 3.2 上传推送证书的步骤:

step1. 打开Application –> Utilities –> Keychain Access应用, 我们会看到有刚刚我们制作好的推送证书

新萄京娱乐app 36

1400051-e960ed3425d6f41e.jpg

step2. 选中证书对应的私钥(或者展开后选中证书), 点右键, 选择导出, 并设定密码(本步导出的证书使用的电脑务必与制作证书时step5中使用的是一台电脑。)

新萄京娱乐app 37

1400051-395417e48955a3c3.png

step3. 登陆环信管理后台

新萄京娱乐app 38

1400051-6be2e2ca31cac67a.png

step4. 输入了正确的账号后, 选择对应的APP(环信示例为ChatDemoUI, 点击ChatDemoUI)

新萄京娱乐app 39

1400051-d7c978e572f3e16f.png

step5. 填写的证书名称

这个名称是个有意义的名字, 对推送直接相关, 稍后会在源码的修改里继续用到这个名字. 上传之前导出的P12文件, 密码则为此P12文件的密码, 证书类型请根据具体情况选择
(创建的是Apple Push Notification service SSL Sandbox请选择开发环境; Apple Push Notification service SSL Production请选择生产环境)

step6. 上传

新萄京娱乐app 40

1400051-cc45658d4f29fda5.png

请注意正确选择是生产环境还是测试环境的证书(我选的是开发环境,如果报错就选择生产环境)

3.环信内部实现了数据缓存,会把聊天记录添加到数据库,把附件下载到本地

4.集成环信SDK

集成SDK有两种方法,一种是用cocoaPods直接下载到自己的项目,一种是从官网下载SDK然后自己导入,不管哪种都需要导入第三方依赖库。

1.导入SDK
将下载好的SDK文件夹(EaseMobSDK)拖入到项目中,并勾选上Destination

新萄京娱乐app 41

1400051-d5c3c3971cbee487.jpg

2.设置工程属性
2.1. 向Build Phases → Link Binary With Libraries 中添加依赖库

新萄京娱乐app 42

1400051-671cb36841abd039.jpg

新萄京娱乐app 43

屏幕快照 2017-04-24 上午11.49.28.png

2.2. 向Build Settings → Linking → Other Linker Flags 中 添加-ObjC(注意大小写)

新萄京娱乐app 44

1400051-4788a01e68fe0fe8.jpg

2.3. 如果项目中使用-ObjC有冲突,可以添加-force_load来解决。
格式为: -force_load[空格]EaseMobSDK/lib/libEaseMobClientSDKLite.a(静态库的路径)(导入SDK过后会自动添加,如果没有就需要手动添加了,路径在EaseMobSDK-->lib中)。

  • step1. 先添加一个-force_load

新萄京娱乐app 45

1400051-65e75dc406cb51f6.jpg

step2. 将静态库拖动到上一步添加的-force_load下面

新萄京娱乐app 46

1400051-9cec842a3ad861f7.jpg

step3. 最终效果

新萄京娱乐app 47

1400051-f9eed763d745d60f.jpg

3.编译工程(根据情况修改所报的错误)

以上步骤进行完后,编译工程,如果没有报错,恭喜你,集成sdk成功,可以进行下一步了。
我们集成聊天功能的时候很多东西其实都不用我们动手,例如聊天页面等,我们可以直接从demo中拖过来,如果有不如意的地方,可以根据自己的喜好适当修改,下面我就说一下需要从环信3.0中搜索下面文件直接导入:

新萄京娱乐app 48

1400051-763d46c3a84279be.jpg

编译过后会报错如下图:

新萄京娱乐app 49

1400051-3df1c7f853dce967.jpg

这是因为没有导入EaseUI头文件导致的,在这里我们可以创建一个PCH文件(注意配置路径在

新萄京娱乐app 50

1400051-370c8dba936fc0f4.jpg


在pch文件中导入EaseUI.h头文件,编译成功。但是有可能出现报错这里介绍两种常见的报错
第一种:

新萄京娱乐app 51

1400051-e6c41c9155cf8ca4.jpg

这种报错解决办法是在环信3.0的demo中导入FixFopen.c文件即可,如果仍然报错那么在自己的pch文件中加入如下代码即可:

新萄京娱乐app 52

1400051-19e357f0a244e679.jpg

第二种:
属于第三方库的冲突报错,这种的话可以将环信中的第三方删除,其中需要注意的两个第三方库分别是:
1). EMSDWebImage,这是环信自己加了前缀,删除这个第三方库过后要在相应的代码中删除EM前缀删除;
2). MJRefresh,它使用的是老版本的,在新版本中一些老的方法已经删除,所以只能用EaseUI中的MJRefresh,后期环信应该会更新的。

4.环信内部已经实现了音频、视频、图片,等其他附件的发送功能

5.使用环信

1.初始化应用,有两个方法

/*
*registerSDKWithAppKey:区别app的标识,开发者注册及管理后台apnsCertName:iOS中推送证书名称。制作与上传推送证书
*/

//环信的初始化
//    [[EaseMob sharedInstance] registerSDKWithAppKey:@"MG#MGChat"
ap sCertName:@””];

//环信的初始化
并隐藏日志输出 
[[EaseMob sharedInstance] registerSDKWithAppKey:@"MG#MGChat" apnsCertName:@"" otherConfig:@{kSDKConfigEnableConsoleLogger:@(NO)}];

新萄京娱乐app 53

1429890-e6764e24a534832b.png

2.注册

[[EaseMob
sharedInstance].chatManager asyncRegisterNewAccount:”MG” password:”123456” withCompletion:^(NSString *username, NSString *password,EMError *eror) {
     NSLog(@"error:%@,username:%@,pwd:%@",error,username,password);
} onQueue:nil];

3. 登录

  • 3.1 自动登录

新萄京娱乐app 54

屏幕快照 2017-04-24 下午2.21.53.png

  • 3.2 掉线自动登录

如果网络不通过,用户应该自动连接到服务器,以及时接收消息;
此功能无需程序员自己做,环信框架已实现,环信SDK会调用自动连接的代理方法来通知应用程序

/*!
 @method
 @brief 将要发起自动重连操作时发送该回调
 @discussion
 @result
 */
- (void)willAutoReconnect;

/*!
 @method
 @brief 自动重连操作完成后的回调(成功的话,error为nil,失败的话,查看error的错误信息)
 @discussion
 @result
 */
- (void)didAutoReconnectFinishedWithError:(NSError*)error;

环信可能出现的问题

4.好友

新萄京娱乐app 55

屏幕快照 2017-04-24 下午2.24.28.png

http://www.finalshares.com/read-779

5.聊天

环信消息发送的流程:

1.先把记录保存到Conversation表

2.接着发送网络请求,API如下:

[[EaseMob sharedInstance].chatManager asyncSendMessage:message progress:self prepare:^(EMMessage *message, EMError *error) {
      NSLog(@"prepare %@",message.messageBodies);
} onQueue:nil completion:^(EMMessage *message, EMError *error) {
      NSLog(@"完成%@",message.messageBodies);
} onQueue:nil];

新萄京娱乐app 56

屏幕快照 2017-04-24 下午2.27.42.png

  • 环信提供会话管理者(EMConversation)来管理未读消息数和历史聊天记录,具体代码如下:
    总的未读消息数需要遍历conversations
// 1.获取所有历史会话
NSArray *conversations = [[EaseMob sharedInstance].chatManager conversations];

// 2.如果内存中,没有会话,从数据库中加载
if(conversations.count == 0)
{
    conversations = [[EaseMob sharedInstance].chatManager loadAllConversationsFromDatabaseWithAppend2Chat:YES];
}

新萄京娱乐app 57

1429890-19ee9e81764f398c.png

当进入聊天页面时,需要设置所有当前会话信息或者设置已经加载的消息为已读

// 设置当前会话所有消息都为已读
[self.conversation markAllMessagesAsRead:YES];

//设置某条消息为已读
 [self.conversation markMessageWithId:<#(NSString *)#> asRead:<#(BOOL)#>]

优点:

6.语言消息

  • 在录音前导入环信封装的两个录音框架,如图

新萄京娱乐app 58

1429890-0956ae0a330d57a2.png

  • 刚才导入的两个框架,已经实现了录音Api
// 开始录音 
[[EMCDDeviceManager
sharedInstance]
asyncStartRecordingWithFileName:fileName  completion:^(NSError *error){
     if(error) {
        NSLog(@"failure to start recording");
     }
}];
// 结束录音
[[EMCDDeviceManager sharedInstance] asyncStopRecordingWithCompletion:^(NSString *recordPath, NSInteger aDuration, NSError *error) {
    NSLog(@"%@",recordPath);
}];

// 语音对象
EMChatVoice *voice = [[EMChatVoice alloc] initWithFile:filePath displayName:@"audio"];

//消息体 
EMVoiceMessageBody *body = [[EMVoiceMessageBody alloc] initWithChatObject:voice];
EMMessage *message = [[EMMessage alloc] initWithReceiver:self.buddy.username bodies:@[body]];
message.messageType = eMessageTypeChat;// 私聊
// 不加密
message.requireEncryption = NO;
// 播放完成
[[EMCDDeviceManager sharedInstance] asyncPlayingWithPath:filePath completion:^(NSError *error) {
NSLog(@"播放完成%@",error);
  }];

1.不需要公司内部搭建服务器

7.退出(异步方法)

[[EaseMobsharedInstance].chatManagerasyncLogoffWithUnbindDeviceToken:YEScompletion:^(NSDictionary *info, EMError *error) {
    if (!error) {//退出成功
  }else{//退出失败;

  }
}
onQueue:nil];

2.客户端的开发,使用环信的SKD比使用XMPPFrameWork更简洁方便

Demo:

先来用户的登录和注册,由于只是搭建简单的基础聊天功能,我将这段代码写在appdelegate里面的,废话不多说,直接上图吧:

新萄京娱乐app 59

1400051-467a5403557dec81.jpg

注释中间有说明如何聊天

现在最主要的就是如何才能实现聊天界面的配置了,其实也是相当简单的,下面我们说说具体是怎么做的吧!!!其实聊天界面在我们刚才导入的EaseUI中就已经搭建好了,我们只需要跳转过去就行了

新萄京娱乐app 60

1400051-7024ee741c739758.jpg

这里我用的是button跳转,具体代码图片上面都有,下面给你们看下效果图吧!!!

新萄京娱乐app 61

1400051-d574a7dc52f0ab83.png

新萄京娱乐app 62

1400051-7c6eda53feb9088a.png

集成基础聊天功能到此就结束了,有什么不明白的和技术问题可以参考环信官方文档和在线咨询

相关优秀博客推荐链接:
(基于环信实现在线聊天功能)http://www.jianshu.com/p/055069fc10f3#
(基于环信实现实时视频语音通话功能)http://www.jianshu.com/p/c5c46c2fa9c6

XMPP的使用

1.要先下载XMPPFramework框架,并导入到工程中,XMPP 是罗宾汉森编写的开源框架

需要的头文件为

//导入头文件

#import"XMPP.h"

#import"XMPPRoster.h"

#import"XMPPRosterCoreDataStorage.h"

#import

#import"XMPPUserCoreDataStorageObject.h"

主要基于以下三个主要部分

数据传输:XMPPStream//基础通讯的服务器类,所有的数据传输都是这个类负责。

数据交换格式:XML

数据库:CoreData  // 会使用到coredata来存储数据

@property(nonatomic,strong)XMPPRoster*xmppRoster;

//好友类别的数据库

@property(nonatomic,strong)XMPPRosterCoreDataStorage*xmppRosterStorage;

//1.初始化基础服务类

_xmppStream= [[XMPPStreamalloc]init];

传输数据使用的是XML可以用DDXMLElement自定义XML格式

NSFetchedResultsController  // 可以监听数据库数据的变化

- (NSFetchedResultsController*)fetchedResultsController

{

if(!_fetchedResultsController)

{

//XMPPUserCoreDataStorageObject就是好友列表的实体

//1.查询对象

NSFetchRequest*request = [NSFetchRequestfetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"];

//2.设置排序

NSSortDescriptor*sort = [NSSortDescriptorsortDescriptorWithKey:@"jidStr"ascending:YES];

request.sortDescriptors=@[sort];

//3.获取上下文

NSManagedObjectContext*context = [[XMPPManagermanager]rosterStorageObjectContext];

//sectionNameKeyPath如果为空,表示一组

_fetchedResultsController= [[NSFetchedResultsControlleralloc]initWithFetchRequest:requestmanagedObjectContext:contextsectionNameKeyPath:nilcacheName:nil];

_fetchedResultsController.delegate=self;

}

return_fetchedResultsController;

}

//开始侦听好友列表CoreData的数据库变化

[self.fetchedResultsControllerperformFetch:nil];

*当前侦听的数据库发生后会自动触发

- (void)controller:(NSFetchedResultsController*)controller didChangeObject:(id)anObject atIndexPath:(nullableNSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(nullableNSIndexPath*)newIndexPath

{

[self.friendTableViewreloadData];

}

jid 是一个Model 保存着用户的信息

XMPP 用CoreData 保存数据

实现消息发送的原理

GCDAsyncSoket — TCP用

0 -1024 操作系统的端口

1025 — 65536 可用端口

-xml2 other link flag

本文由新葡京8455发布,转载请注明来源

关键词: