基于XMPP协议的即时通讯教程附Demo

技术资料总结 专栏收录该内容
5 篇文章 0 订阅

1、XMPP中的几个重要概念:
JID:即Jabber ID。xmpp网络有一个或多个地址jid,如a@aaa.lit/study 。
组成:节点、域、资源(带有资源的jid是完整jid,没有资源的jid是裸jid),其中节点和资源是可选的,域是必选的。jid的资源部分会标识一个特定的客户端xmpp链接,对于xmpp客户端来说,每个链接均被指派一个资源。

XMPP中定义了三个角色,客户端,服务器,网关。
通信能够在这三者的任意两个之间双向发生。
服务器同时承担了客户端信息记录,连接管理和信息的路由功能。
网关承担着与异构即时通信系统的互联互通,异构系统可以包括SMS(短信),MSN,ICQ等。

客户端或服务器发送的所有XML文本连缀在一起,从到构成了一个完整的XML文档。
XML流:其中的stream标签就是所谓的XML Stream。
XML节:在与中间的那些…这样的XML元素就是所谓的XML Stanza

关于XMPP协议的一些细节,这里也只写了个大概。具体请参见“XMPP协议参考手册”,或查阅相关书籍。

2、XMPP工作原理:
(1)先建立一个stream,然后协商一堆安全之类的东西,中间通信过程就是客户端一个接一个的发送XML Stanza给服务器。
(2)服务器根据客户端发送的信息以及程序的逻辑,发送XML Stanza给客户端。(这个过程并不是一问一答的,任何时候都有可能从一方发信给另外一方。)
(3)通信的最后阶段关闭流,关闭TCP/IP连接。
XMPP 是一种很类似于http协议的一种数据传输协议,它的过程就如同“解包装–〉包装”的过程,用户只需要明白它接收的类型,并理解它返回的类型,就可以很好的利用xmpp来进行数据通讯。

关于XMPP的一些扩展协议:http://xmpp.org/xmpp-protocols/xmpp-extensions/

二、XMPP环境
1、服务器软件
服务器软件列表:http://xmpp.org/xmpp-software/servers/
用得最多的当是Openfire了。ejabberd也用的比较多。
关于Openfire的安装与配置,看下面的blog就可以了
http://blog.csdn.net/shimiso/article/details/8816558 Windows下
http://blog.csdn.net/jeepxiaozi/article/details/16357337 Mac下

2、客户端软件
客户端软件主要用来自己做开发测试时使用。
客户端软件列表:http://xmpp.org/xmpp-software/clients/
客户端使用Spark即可,登录时,先在服务器上多创建几个账号,然后直接登录其中一个,添加联系然即可进行聊天。
其他客户端软件参考Spark即可(最重要的是把JID输入正确)
参考下面文章:(仅供参考,没他写的那么复杂。第四步是另一回事,不用管)
http://www.cnblogs.com/top5/archive/2009/04/07/1431001.html

1. 环境搭建 demo下载地址在最后

我使用的是openfire,使用他我们前端只需要告诉它 消息, 和接受对象.openfire 服务进行转发,前端无需担心.自己测试可以用spark.
具体搭建步骤网上教程有很多,我这里不做介绍了.(其实我也而不是很熟.)

2. XMPPframework导入

github 下载地址

https://github.com/robbiehanson/XMPPFramework

下载完后拖入你的工程里
添加 依赖关系
CFNetwork.framework
Security.framework
libxml2.dylib
libresolv.dylib
libin.a

3. XMPP配置

XMPPManager 我创建的管理类
.h

typedef NS_ENUM(NSInteger, ReceiveMessageType) {
    ReceiveMessageText = 0, //  文本
    ReceiveMessageImage, //  图片
    ReceiveMessageVoice, //
};
typedef void (^XMPPManagerCallBlock)(BOOL isSuccessed);
typedef void (^XMPPFetchCallBlock)(NSArray* buddyList, NSString* errogMsg);
typedef XMPPFetchCallBlock XMPPMessageListBlock;

@protocol XMPPManagerDelegate <NSObject>
/*!
 *  接收到消息
 *
 *  @param message NSData 根据数据类型解码
 */
- (void)receivesMessage:(MessageModel*)message WithFromUserJID:(XMPPJID*)Jid MessageType:(ReceiveMessageType)type;
/*!
 *  接收好友列表
 *
 *  @param friendList XMPPJID对象
 */
- (void)receivesFriendList:(NSMutableArray*)friendList;
@end

@interface XMPPManager : NSObject <XMPPStreamDelegate, XMPPRosterDelegate, UIAlertViewDelegate>

@property (nonatomic, strong) XMPPReconnect* xmppReconnect;
@property (nonatomic, strong) XMPPStream* xmppStream; //通信管道相当于一根电话线

// 花名册相关
@property (nonatomic, strong) XMPPRoster* xmppRoster; //  好友花名册
@property (nonatomic, strong) XMPPRosterCoreDataStorage* xmppRosterStorage;

// 名片相关
@property (nonatomic, strong) XMPPvCardCoreDataStorage* xmppvCardStorage;
@property (nonatomic, strong) XMPPvCardTempModule* xmppvCardTempModule;
@property (nonatomic, strong) XMPPvCardAvatarModule* xmppvCardAvatarModule;

// 性能相关
@property (nonatomic, strong) XMPPCapabilities* xmppCapabilities;
@property (nonatomic, strong) XMPPCapabilitiesCoreDataStorage* xmppCapailitiesStorage;

//消息相关
@property (strong, nonatomic) XMPPMessageArchiving* xmppMessageArchiving;
@property (strong, nonatomic) NSManagedObjectContext* messageArchivingManagedObjectContext;
@property (nonatomic, strong) XMPPMessageArchivingCoreDataStorage* xmppMessageStorage;


//获得XMPPManager的单例方法
+ (XMPPManager*)defaultManager;

/*!
 *  连接服务器
 */
- (void)connectToOpenfireWithCallblock:(XMPPManagerCallBlock)callblock;

/*!
 *  取消和服务器的链接
 */
- (void)logout;

/*!
 *  注册方法
 *
 *  @param userName
 *  @param password
 */
- (void)registerWithUserName:(NSString*)userName password:(NSString*)password WithCallblock:(XMPPManagerCallBlock)callblock;
/*!
 *  登陆方法 //失败 暂无
 *
 *  @param userName
 *  @param password
 */
- (void)loginWithUserName:(NSString*)userName password:(NSString*)password WithCallblock:(XMPPManagerCallBlock)callblock;
/*!
 *  添加好友
 *
 *  @param Jid 好友的JID
 */
- (void)addFriendWithXMPPJID:(XMPPJID*)Jid WithCallblock:(XMPPManagerCallBlock)callblock;
/*!
 *  发送消息
 *
 *  @param message 消息内容
 *  @param jid     要发送的对象
 */
- (void)sendMessage:(MessageModel*)message toJID:(XMPPJID*)Jid WithCallblock:(XMPPManagerCallBlock)callblock;
/*!
 *  删除好友
 *
 *  @param Jid
 *  @param callblock
 */
- (void)removeXMppUserXMPPJID:(XMPPJID*)Jid WithCallblock:(XMPPManagerCallBlock)callblock;

/*!
 *  获取好友列表
 *
 *  @param callblock
 */
- (void)fetchFriendListWithCompletion:(XMPPFetchCallBlock)callblock;

/*!
 *  获取消息列表
 *
 *  @param callblock
 */
- (void)fetchMessageListWithXMPPJID:(XMPPJID*)Jid Completion:(XMPPMessageListBlock)callblock;

.m 首先要开启服务
确认XMPPStream 配置 正确,各项协议回调 已激活
初始化时 需要配置的类,否则服务无法检测

    self.xmppStream = [[XMPPStream alloc] init];
#if !TARGET_IPHONE_SIMULATOR
    // 设置此行为YES,表示允许socket在后台运行
    // 在模拟器上是不支持在后台运行的
    self.xmppStream.enableBackgroundingOnSocket = YES;
#endif

    // 设置自动断线重连 模块会监控意外断开连接并自动重连
    self.xmppReconnect = [[XMPPReconnect alloc] init];
    [self.xmppReconnect activate:self.xmppStream];

    //好友相关
    // 配置花名册并配置本地花名册储存
    self.xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
    self.xmppRoster =
        [[XMPPRoster alloc] initWithRosterStorage:self.xmppRosterStorage];
    self.xmppRoster.autoFetchRoster = YES; //是否自动获取花名册
    // 是否自动同意添加好友
    self.xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = YES;

    // 配置vCard存储支持,vCard模块结合vCardTempModule可下载用户Avatar
    self.xmppvCardStorage = [[XMPPvCardCoreDataStorage alloc] init];
    self.xmppvCardTempModule =
        [[XMPPvCardTempModule alloc] initWithvCardStorage:self.xmppvCardStorage];
    self.xmppvCardAvatarModule = [[XMPPvCardAvatarModule alloc]
        initWithvCardTempModule:self.xmppvCardTempModule];

    // XMPP特性模块配置,用于处理复杂的哈希协议等
    self.xmppCapailitiesStorage = [[XMPPCapabilitiesCoreDataStorage alloc] init];
    self.xmppCapabilities = [[XMPPCapabilities alloc]
        initWithCapabilitiesStorage:_xmppCapailitiesStorage];
    self.xmppCapabilities.autoFetchHashedCapabilities = YES;
    self.xmppCapabilities.autoFetchNonHashedCapabilities = NO;

    // 激活XMPP stream
    [self.xmppReconnect activate:self.xmppStream];
    [self.xmppRoster activate:self.xmppStream];
    [self.xmppvCardTempModule activate:self.xmppStream];
    [self.xmppvCardAvatarModule activate:self.xmppStream];
    [self.xmppCapabilities activate:self.xmppStream];

    // 消息相关
    self.xmppMessageStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init];
    self.xmppMessageArchiving = [[XMPPMessageArchiving alloc]
        initWithMessageArchivingStorage:self.xmppMessageStorage];
    [self.xmppMessageArchiving setClientSideMessageArchivingOnly:YES];
    [self.xmppMessageArchiving activate:self.xmppStream];

    // 添加代理
    [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [self.xmppMessageArchiving addDelegate:self
                             delegateQueue:dispatch_get_main_queue()];

初始化时需要将这些服务开启

4. 登陆

登陆前,首先要配置服务器,和连接服务器的相关参数,例如:
服务器ip地址
域名
登陆时需要 验证密码
登陆时验证密码流程 : 首先连接到服务器 -> 将用户名拼接发送到服务器 -> 将密码发送给服务器等待验证
⇒ 返回验证结果(正确或失败)
XMPP登陆 所用的方法
{
// 连接服务器,调用此方法时,参数必须已设定 myJid .

-   (BOOL)connectWithTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr

//登陆成功回调

-   (void)xmppStreamDidAuthenticate:(XMPPStream*)sender

// 验证密码 XMPPStream 对象调用

authenticateWithPassword

// 登陆失败回调

- (void)xmppStream:(XMPPStream*)sender didNotAuthenticate:(NSXMLElement*)error

} 

登陆成功回调

- (void)xmppStreamDidConnect:(XMPPStream*)sender

注册

  [sender registerWithPassword:_userPS error:NULL];

//登陆和注册验证方法是不一样的,所以在发出登陆或者注册请求前,要做一下标示,标示当前发出的是登陆请求还是注册请求,以便在回调验证时选择 验证方法.

5. 发送文本消息

XMPP 即时通讯 是通过xml 流 传输. 下面是一段 基础文本 消息的 xml .

<message type="chat" to="97@zxcvbnm/8.100000"><body>2</body></message>

type = “chat” 是消息类型,此次xmpp 实现收发消息 都是通过这种类型
to = 91@zxcvnm/8.10000 为消息接受者
body 作为消息载体,例子 xml 中 body 2 为消息内容

XMPPStream sendElement:

Xmppstream 调用sendElement 方法将 消息发送给服务器,再有服务器转发给消息接受者

同时 XMPP 提供了 2个消息 发送后的回调方法,分别是:
1.发送成功

- (void)xmppStream:(XMPPStream*)sender didSendMessage:(XMPPMessage*)message

2.发送失败

- (void)xmppStream:(XMPPStream*)sender
    didFailToSendMessage:(XMPPMessage*)message
                   error:(NSError*)error

6. 发送语音和图片

实现语音和图片一般有两种实现方式:
1.将图片或语音上传到服务器,然后将url发送给对方.
2.将图片或语音转成字符串 添加到 body 中发送给对方.

本人采用第二种方式实现.
图片: 将想要发送的图片压缩(为了节省流量,压缩后每张图片大约为 10k,压缩到最低)转整 NSData
然后通过base64 转换成 字符串 拼接到 body 中进行发送.(发送前将body前插入图片消息的标示)
.
语音:将录制好的NSData 语音数组,通过base64 转换成字符串.以下处理与图片消息一致,不在复述.

为了标示出三种消息类型 我在 xml 中添加了 一个 messagetype 标签,与body平级.
这样我们在发送消息时,为消息加入 消息类型,接收消息时就可以根据消息类型加载数据了.

7. 添加好友

添加好友需要用到的两个类分别是:
XMPPRoster //这个类是管理好友,增删改查
XMPPRosterCoreDataStorage //提供用来管理Coredata进行好友存储的类
好友添加流程:
首先检索输入的联系人是否是好友,如果不是 -> 发送添加好友请求(XMPPRoster调用 subscribePresenceToUser:Jid 此方法只是发送好友请求,不做其他任何操作) -> 等待被添加用户进行确认(xmpp可以设置默认同意) ->
根据被添加用户的操作状态回调,进行(添加好友,和删除好友,如果对方同意那么将用户添加到自己的好友列表当中,如果不同意那么将用户从自己的好友列表当中删除,这里需要解释一下为什么删除,为什么coredata存储好友信息,它使无论对方是否同意关注你,都会讲你发送过请求的用户信息添加到数据库当中当然服务器数据库也是这样的,所以对方拒绝添加的时候我选着了从好友列表当中删除) -> 当双方成为好友时更新好友列表

//回调方法
 - (void)xmppRoster:(XMPPRoster*)sender
didReceiveRosterItem:(NSXMLElement*)item 

8. 消息列表

XMPP自带消息存储(CoreData)所以此Demo并没有再通过数据库将消息做持久化的必要.一下只列出 过滤 所需要的联系人得消息记录,
通过谓词检索过滤出 当前回话联系人得消息记录

"streamBareJidStr == %@ AND bareJidStr == %@”

第一个为myJid,第二个为联系人Jid.
返回消息载体为XMPPMessageArchiving_Message_CoreDataObject对象
这里有必要 介绍 该对象 的isOutgoing属性,此属性是标示 消息来源,也就是说 代表是别人发给你的,还是你发给别人得 (YES 是你自己发的,NO…)

9. 好友列表获取

好友列表可以通过两种方式得到
1. 通过 检索 本地数据库 获得好友列表 (此Demo采用了本方法).
2. 通过 向服务器发送请求 (此Demo讲此段代码注掉了,然而我发送的请求服务器并没有回答我,所以将此方法废弃).
下面是2中方式的代码 实现
实体名 @” XMPPUserCoreDataStorageObject”
当然CoreData是支持排序的.我选着按 用户的Jid 进行了升序排序@”jidStr”
返回的对象XMPPUserCoreDataStorageObject.

Demo下载链接

https://github.com/yangyangFeng/TY_XMPPDEMO.git

至于语音通话和视频通话,本人技术实现还有一定难度.如果大神有开源的Demo欢迎分享.

  • 2
    点赞
  • 0
    评论
  • 3
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

风扬扬

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值